import org.jmonde.state.State;
import org.jmonde.state.StateMachine;
import org.jmonde.state.StateUtil;

public class Turnstile {

    private final State myStateLocked = new State();
    private final State myStateUnlocked = new State();
    private final State myStateBroken = new State();
    private final StateMachine myMachine = new StateMachine(myStateLocked);
    private boolean myLockError = false;
    private boolean myUnlockError = false;

    public Turnstile() {
        initializeFSM();
        myMachine.enter();
    }

    private void initializeFSM() {
        StateUtil.addActionEnter(myStateLocked,
                                 new Runnable() {
                                     public void run() {
                                         lock();
                                     }});
        StateUtil.addActionCurrent(myStateLocked,
                                   new Runnable() {
                                       public void run() {
                                           if (lockFailed()) {
                                               lockError();
                                               transitionTo(myStateBroken);
                                           }
                                       }});

        StateUtil.addActionEnter(myStateUnlocked,
                                 new Runnable() {
                                     public void run() {
                                         unlock();
                                     }});
        StateUtil.addActionCurrent(myStateUnlocked,
                                   new Runnable() {
                                       public void run() {
                                           if (unlockFailed()) {
                                               unlockError();
                                               transitionTo(myStateBroken);
                                           }
                                       }});

        StateUtil.addActionEnter(myStateBroken,
                                 new Runnable() {
                                     public void run() {
                                         outOfOrder();
                                     }});
        StateUtil.addActionLeave(myStateBroken,
                                 new Runnable() {
                                     public void run() {
                                         inOrder();
                                     }});
    }

    public void coin() {
        if (myStateLocked.isActive()) {
            transitionTo(myStateUnlocked);
        } else if (myStateUnlocked.isActive()) {
            thankYou();
        }
    }

    public void pass() {
        if (myStateUnlocked.isActive()) {
            transitionTo(myStateLocked);
        } else if (myStateLocked.isActive()) {
            alarm();
        }
    }

    public void fixed() {
        if (myStateBroken.isActive()) {
            transitionTo(myStateLocked);
        }
    }

    private void alarm() {
        System.out.println("Alarm");
    }

    private void inOrder() {
        System.out.println("InOrder");
    }

    private void lock() {
        System.out.println("Lock");
    }

    private void lockError() {
        System.out.println("LockError");
    }

    private boolean lockFailed()
    {
        return myLockError;
    }

    private void outOfOrder() {
        System.out.println("OutOfOrder");
    }

    private void thankYou() {
        System.out.println("ThankYou");
    }

    private void unlock() {
        System.out.println("Unlock");
    }

    private void unlockError() {
        System.out.println("UnlockError");
    }

    private boolean unlockFailed()
    {
        return myUnlockError;
    }

    private void transitionTo(State s) {
        myMachine.leaveFor(s);
        myMachine.enter();
    }

    public static void main(String[] args) {
        Turnstile t = new Turnstile();
        t.coin();
        t.pass();
        t.myUnlockError = true;
        t.coin();
        t.myUnlockError = false;
        t.fixed();
        t.coin();
        t.coin();
        t.myLockError = true;
        t.pass();
        t.myLockError = false;
        t.fixed();
        t.pass();
    }
}
