package anastore.client;

import anastore.util.Bug;

class RWState
{
    static enum State
    {
        ACTIVE, COMMITTING, COMMITTED, ABORTED;
    }

    public static final State ACTIVE = State.ACTIVE;
    public static final State COMMITTING = State.COMMITTING;
    public static final State COMMITTED = State.COMMITTED;
    public static final State ABORTED = State.ABORTED;

    private volatile State _state = State.ACTIVE;

    /**
     * Change this transaction's state.  The allowed transitions are
     *<ul>
     * <li>ACTIVE -> COMMITTING</li>
     * <li>ACTIVE, COMMITTING -> COMMITTED</li>
     * <li>ACTIVE, COMMITTING -> ABORTED</li>
     * <li>ABORTED -> ABORTED</li>
     *</ul>
     *
     * @throws IllegalStateException if another transition is
     * attempted (except for a transition to ACTIVE)
     * @throws IllegalArgumentException if a transition to ACTIVE is
     * attempted
     */
    public State set(State newState)
        throws IllegalStateException, IllegalArgumentException
    {
        if (newState == ACTIVE)
            throw new IllegalArgumentException
                ("Cannot change to state " + newState);
        synchronized (this) {
            State oldState = _state;
            switch (_state) {
            case ACTIVE:
                _state = newState;
                break;
            case COMMITTING:
                if (newState == COMMITTED || newState == ABORTED)
                    _state = newState;
                else if (newState == COMMITTING)
                    throw new IllegalStateException
                        ("Transaction is already in the process of committing");
                break;
            case COMMITTED:
                if (newState == COMMITTING || newState == COMMITTED)
                    throw new IllegalStateException
                        ("Transaction already committed");
                else if (newState == ABORTED)
                    throw new IllegalStateException
                        ("Cannot abort a committed transaction");
                throw new Bug();
            case ABORTED:
                if (newState == COMMITTING || newState == COMMITTED)
                    throw new IllegalStateException
                        ("Cannot commit an aborted transaction");
                else if (newState != ABORTED)
                    throw new Bug();
                break;
            }
            return oldState;
        }
    }

    /**
     * Check that the transaction is active (that is, neither
     * committing, committed, nor aborted)
     *
     * @throws AbortedException if the transaction has been aborted
     * @throws IllegalStateException if the transaction has been
     * committed or is in the process of committing
     */
    public void checkActive()
        throws AbortedException, IllegalStateException
    {
        switch (_state) {
        case ACTIVE:
            break;
        case COMMITTING:
            throw new IllegalStateException
                ("Transaction is in the process of committing");
        case COMMITTED:
            throw new IllegalStateException("Transaction committed");
        case ABORTED:
            throw new AbortedException("Transaction aborted");
        }
    }

    /**
     * Get the current state
     */
    public State get()
    {
        return _state;
    }
}
