package sale;

import users.User;

/**
  * A {@link Gate gate} at which a {@link FormSheet} and/or a {@link MenuSheet} can be displayed. The
  * {@link Transition transition} that leaves the gate will depend on the users interaction with the Form-
  * and/or MenuSheet.
  *
  * @author Steffen Zschaler
  * @version 2.0 17/08/1999
  * @since v2.0
  */
public class UIGate extends Object implements Gate {

  /**
    * The FormSheet to be displayed.
    *
    * @serial
    */
  protected FormSheet m_fsFormSheet;

  /**
    * The MenuSheet to be displayed.
    *
    * @serial
    */
  protected MenuSheet m_msMenuSheet;

  /**
    * The transition that will leave this gate.
    */
  private transient Transition m_tTransition;

  /**
    * The monitor synchronizing access to the elements as well as a communications channel.
    */
  private transient Object m_oLock;

  /**
    * Return the monitor synchronizing access to the elements as well as a communications channel.
    *
    * @override Never
    */
  private final Object getLock() {
    if (m_oLock == null) {
      m_oLock = new Object();
    }

    return m_oLock;
  }

  /**
    * Bit field indicating the last change.
    */
  private transient int m_nChanged = NOTHING;
  /**
    * Nothing changed.
    */
  private static final int NOTHING = 0;
  /**
    * The FormSheet changed.
    */
  private static final int FORMSHEET = 1;
  /**
    * The MenuSheet changed.
    */
  private static final int MENUSHEET = 2;
  /**
    * The transition was set.
    */
  private static final int TRANSITION = 8;

  /**
    * Create a new UIGate.
    *
    * @param fs the FormSheet to be displayed. Can be <code> null</code>.
    * @param ms the MenuSheet to be displayed. Can be <code> null</code>.
    */
  public UIGate (FormSheet fs,
                 MenuSheet ms) {
    super();

    m_fsFormSheet = fs;
    m_msMenuSheet = ms;
  }

  /**
    * @override Never
    */
  public Transition getNextTransition (SaleProcess pOwner, User usr)
    throws InterruptedException {

    synchronized (getLock()) {
      m_tTransition = null;
      m_nChanged = FORMSHEET | MENUSHEET;

      while ((m_nChanged & TRANSITION) == 0) {

        if ((m_nChanged & MENUSHEET) != 0) {
          pOwner.getContext().setMenuSheet (pOwner, m_msMenuSheet);
        }

        if ((m_nChanged & FORMSHEET) != 0) {
          if (m_fsFormSheet != null) {
            m_fsFormSheet.setWaitResponse (false);
          }
          pOwner.getContext().setFormSheet (pOwner, m_fsFormSheet);
        }

        m_nChanged = NOTHING;

        getLock().wait();
      }
    }

    return m_tTransition;
  }

  /**
    * Set the FormSheet that is being displayed at this Gate.
    *
    * @override Never
    *
    * @param fs the new FormSheet
    */
  public void setFormSheet (FormSheet fs) {
    synchronized (getLock()) {
      m_fsFormSheet = fs;
      m_nChanged |= FORMSHEET;

      getLock().notifyAll();
    }
  }

  /**
    * Set the MenuSheet that is being displayed at this gate.
    *
    * @override Never
    *
    * @param ms the MenuSheet.
    */
  public void setMenuSheet (MenuSheet ms) {
    synchronized (getLock()) {
      m_msMenuSheet = ms;
      m_nChanged |= MENUSHEET;

      getLock().notifyAll();
    }
  }

  /**
    * Set the transition that will leave this gate. This will leave the gate at once and enter the Transition.
    * The transition may return to the gate.
    *
    * @override Never
    *
    * @param tNext the transition. Must not be <code>null</code>.
    */
  public void setNextTransition (Transition tNext) {
    if (tNext == null) {
      throw new NullPointerException();
    }

    synchronized (getLock()) {
      m_tTransition = tNext;
      m_nChanged |= TRANSITION;

      getLock().notifyAll();
    }
  }
}