package state.ts2;

/**
 * This class is an implementation of the turnstile.
 *
 * @author fpereira
 *
 */
public class TurnstileUserImpl implements TurnstileUserInterface {

  /**
   * This interface describes a state in which the turnstile may be.
   */
  private interface State {
    /**
     * The event in which the user inserts a coin into the turnstile.
     * @param m the turnstile that will handle this event.
     * @return a new state, that describes the turn-stile after the event has
     * been handled.
     */
    State coin(TurnstileMachineInterface m);
    /**
     * The event in which the user pass through the turnstile.
     * @param m the turnstile that will handle this event.
     * @return a new state, that describes the turn-stile after the event has
     * been handled.
     */
    State pass(TurnstileMachineInterface m);
  }

  /**
   * The state that characterizes a turn-stile that cannot be crossed by any
   * user.
   */
  private static class LockedSt implements State {
    private static final LockedSt instance = new LockedSt();
    public static State createInstance() {
      return instance;
    }
    private LockedSt() {}
    public State coin(final TurnstileMachineInterface m) {
      m.unlock();
      return UnlockedSt.createInstance();
    }
    public State pass(final TurnstileMachineInterface m) {
      m.alarm();
      return FinedSt.createInstance();
    }
  }

  /**
   * The state that characterizes a turn-stile that has been unlocked by the
   * user.
   */
  private static class UnlockedSt implements State {
    private static final UnlockedSt instance = new UnlockedSt();
    public static State createInstance() {
      return instance;
    }
    private UnlockedSt() {}
    public State coin(final TurnstileMachineInterface m) {
      m.thankyou();
      return UnlockedSt.createInstance();
    }
    public State pass(final TurnstileMachineInterface m) {
      m.lock();
      return LockedSt.createInstance();
    }
  }

  public static class FinedSt extends LockedSt {
    private int counter = 0;
    private static FinedSt instance = new FinedSt();
    public static State createInstance() {
      return instance;
    }
    private FinedSt() {}
    public State coin(final TurnstileMachineInterface m) {
      if (counter > 0) {
        counter = 0;
        m.thankyou();
        return UnlockedSt.createInstance();
      } else {
        counter++;
        m.thankyou();
        return FinedSt.createInstance();
      }
    }
    public State pass(final TurnstileMachineInterface m) {
      m.alarm();
      return FinedSt.createInstance();
    }
  }

  /**
   * This is the implementation of the turnstile that this class manipulates.
   */
  private TurnstileMachineInterface turnstile;

  /**
   * The current state of this machine.
   */
  private State s;

  /**
   * Constructor.
   */
  public TurnstileUserImpl() {
    turnstile = new TurnstileMachineImpl();
    s = new LockedSt();
  }

  /**
   * In this event, the user deposits a coin in the turnstile.
   */
  public final void coin() {
    s = s.coin(turnstile);
  }

  /**
   * In this event, the user passes through the turnstile.
   */
  public final void pass() {
    s = s.pass(turnstile);
  }

  /**
   * True if the turn-stile is locked.
   * @return a boolean value.
   */
  public final boolean isLocked() {
    return s instanceof LockedSt;
  }

}
