package sale;

import sale.events.*;

import util.*;

/**
  * This is a rather simple implementation of the {@link Timer Timer} interface.
  *
  * <p>It requires a Time object to work with. Every increasing of the timer has
  * to be explicitly triggered by the {@link #goAhead goAhead()} method.</p>
  *
  * @author Stephan Gambke
  * @version 2.0 11/06/1999
  * @since v2.0
  */
public class StepTimer extends Object implements Timer {

  /**
    * The current Time.
    *
    * @serial
    */
  protected Time m_tTime;

  /**
    * The current interval for the {@link #goAhead} method.
    *
    * @serial
    */
  private Object m_oInterval;

  /**
    * The listeners registered with this timer.
    *
    * @serial
    */
  protected ListenerHelper m_lhListeners = new ListenerHelper();

  /**
    * The number of the current time stamp.
    *
    * @serial
    */
  private long m_lTimeStamps;

  /**
    * Create a new timer wich uses an instance of {@link Step Step} as the time object.
    */
  public StepTimer() {
    this (new Step());
  }

  /**
    * Create a new timer wich uses the given {@link Time Time} object.
    *
    * @param tTimeObject the time object to use
    */
  public StepTimer (Time tTimeObject) {
    super();

    m_tTime = tTimeObject;
    m_oInterval = m_tTime.getDefaultInterval();
  }

  /**
    * Set the time and fire a <code>timeSet</code> event.
    *
    * <p>The time has to be in a format that can be processed by the {@link Time Time} object.</p>
    *
    * @override Never
    *
    * @param oTime the time to set
    *
    * @exception IllegalArgumentException if the set time does not meet the time object's class or format
    * requirements
    */
  public void setTime (Object oTime) throws IllegalArgumentException {
    m_tTime.setTime (oTime);
    fireTimeSet();
  }

  /**
    * Get the current time. This is delegated to the {@link Time} object.
    *
    * @override Never
    *
    * @return an Object representing the current time, the exact type depends on the time object
    */
  public Object getTime() {return m_tTime.getTime();}

  /**
    * Set the interval to be used by the {@link #goAhead goAhead()} method.
    *
    * <p>The given interval has to be of a proper type to be used with the time object's
    * {@link Time#goAhead goAhead()} method.</p>
    *
    * @override Never
    *
    * @param oInterval the interval to be used
    */
  public void setInterval (Object oInterval) {
    m_oInterval = oInterval;
    fireIntervalSet();
  }

  /**
    * Get the current interval.
    *
    * @override Never
    *
    * @return an Object representing the current interval
    */
  public Object getInterval() {return m_oInterval;}

  /**
    * Add a {@link sale.events.TimerListener TimerListener} which will receive
    * {@link sale.events.TimerEvent TimerEvents} triggered by this timer.
    *
    * @override Never
    *
    * @param tlListener the listener to be registered.
    */
  public void addTimerListener (TimerListener tlListener) {
    m_lhListeners.add (TimerListener.class, tlListener);
  }

  /**
    * Remove the given {@link sale.events.TimerListener TimerListener}.
    *
    * @override Never
    *
    * @param tlListener the listener to be deregistered.
    */
  public void removeTimerListener (TimerListener tlListener) {
    m_lhListeners.remove (TimerListener.class, tlListener);
  }

  /**
    * Increase the time by the current interval.
    *
    * <p>If no interval has been set yet, the interval given by the {@link Time#getDefaultInterval()} method
    * of the time object is used.</p>
    *
    * @override Never
    *
    * @exception IllegalArgumentException if the interval does not meet the time object's class or format
    * requirements
    */
  public void goAhead() throws IllegalArgumentException {
    m_tTime.goAhead (m_oInterval);
    m_lTimeStamps = 0;
    fireGoneAhead();
  }

  /**
    * Create and return a fresh time stamp.
    *
    * @override Never
    *
    * @return a fresh time stamp.
    */
  public Comparable getTimeStamp() {
    return m_tTime.getTimeStamp (++m_lTimeStamps);
  }

  /**
    * Fire a <code>timeSet</code> event.
    *
    * @override Never
    */
  protected void fireTimeSet() {
    Object[] listeners = m_lhListeners.getListenerList();
    TimerEvent e = null;

    for (int i = listeners.length-2; i>=0; i-=2) {
      if (listeners[i] == TimerListener.class) {
        if (e == null) {
          e = new TimerEvent (this);
        }
        ((TimerListener) listeners[i+1]).onTimeSet (e);
      }
    }
  }

  /**
    * Fire an <code>intervalSet</code> event.
    *
    * @override Never
    */
  protected void fireIntervalSet() {
    Object[] listeners = m_lhListeners.getListenerList();
    TimerEvent e = null;

    for (int i = listeners.length-2; i>=0; i-=2) {
      if (listeners[i] == TimerListener.class) {
        if (e == null) {
          e = new TimerEvent (this);
        }
        ((TimerListener) listeners[i+1]).onIntervalSet (e);
      }
    }
  }

  /**
    * Fire a <code>goneAhead</code> event.
    *
    * @override Never
    */
  protected void fireGoneAhead() {
    Object[] listeners = m_lhListeners.getListenerList();
    TimerEvent e = null;

    for (int i = listeners.length-2; i>=0; i-=2) {
      if (listeners[i] == TimerListener.class) {
        if (e == null) {
          e = new TimerEvent (this);
        }
        ((TimerListener) listeners[i+1]).onGoneAhead (e);
      }
    }
  }
}