package sale;

/**
  * A timer that has the functionality of the {@link StepTimer StepTimer} and additionally
  * can increase the time automatically.
  *
  * @author Stephan Gambke
  * @version 2.0 11/06/1999
  * @since v2.0
  */
public class AutoTimer extends StepTimer {

  /**
    * Communications channel used for controlling the internal processing loop.
    */
  private transient Object m_oTimer;
  /**
    * Return the communications channel used for controlling the internal processing loop.
    */
  private Object getTimer() {
    if (m_oTimer == null) {
      m_oTimer = new Object();
    }

    return m_oTimer;
  }

  /**
    * Is the timer running ?
    *
    * @serial
    */
  private boolean m_fOnTheRun;

  /**
    * Delay between increases.
    *
    * @serial
    */
  private long m_lDelay = 1;

  /**
    * Create a new AutoTimer with a {@link Step Step} as time object and a delay of 10 seconds.
    * The timer is initially not running.
    */
  public AutoTimer () {
    this (new Step(), 10000);
  }

  /**
    * Create a new AutoTimer with the given time object and a delay of 10 seconds. The timer is initially not
    * running.
    *
    * @param tTime the time object to be used
    */
  public AutoTimer (Time tTime) {
    this (tTime, 10000);
  }

  /**
    * Create a new AutoTimer with the given time object and delay. The timer is initially not running.
    *
    * @param tTime the time object to use
    * @param lDelay the delay in milliseconds between the increasing steps
    */
  public AutoTimer (Time tTime, long lDelay) {
    super (tTime);
    m_fOnTheRun = false;
    setDelay (lDelay);
  }

  /**
    * Internal method that just calls <code>super.goAhead()</code>
    *
    * @override Never
    */
  private void doGoAhead() throws IllegalArgumentException {
    super.goAhead();
  }

  /**
    * Increase the time by the current interval.
    *
    * <p>If no interval has yet been set, the interval given by the {@link Time#getDefaultInterval()} method
    * of the time object is used.</p>
    *
    * <p>If the AutoTimer is running, the time is increased and the delay restartet.</p>
    *
    * @override Never
    *
    * @exception IllegalArgumentException if the interval does not meet the time object's class or format
    * requirements.
    */
  public void goAhead () throws IllegalArgumentException {
    if (m_fOnTheRun) synchronized (getTimer()) {getTimer().notifyAll();}
    else doGoAhead();
  }

  /**
    * Set the timer running. If the timer is already running, nothing happens.
    *
    * <p>The first timer tick occurs at once.</p>
    *
    * @override Never
    */
  public void start() throws IllegalArgumentException {
    synchronized (getTimer()) {
      if (m_fOnTheRun) {
        return;
      }

      m_fOnTheRun = true;

      new Thread() {
        public void run() {
          synchronized (getTimer()) {
            while (m_fOnTheRun) {
              try {
                doGoAhead();
                getTimer().wait (m_lDelay);
              }
              catch (InterruptedException iexc) {}
              catch (Throwable t) {
                m_fOnTheRun = false;

                System.err.println ("Error in AutoTimer: " + t);
                t.printStackTrace();
              }
            }
          }
        }
      }.start();
    }
  }

  /**
    * Stop the timer.
    *
    * @override Never
    */
  public void stop() {
    m_fOnTheRun = false;
  }

  /**
    * Set the delay between timer ticks.
    *
    * <p>If the AutoTimer is currently running, the new delay takes effect after the next tick.</p>
    *
    * @override Never
    *
    * @param lMilliSecs the delay in milliseconds. Must be > 0.
    */
  public void setDelay (long lMilliSecs) {
    if (lMilliSecs > 0) {
      m_lDelay = lMilliSecs;
    }
  }

  /**
    * Get the current delay between timer ticks in milliseconds.
    *
    * @return a long value representing the delay
    */
  public long getDelay () {
    return m_lDelay;
  }
}