import java.util.*;

/**
 * Simple example of Timer that reports
 * progress to TimerListeners. This is a count-down
 * timer.
 *
 * @author Owen Astrachan
 */

public class Timer implements Runnable
{
    /**
     * Create timer with default duration of 3 seconds
     */
    public Timer()
    {
	this(3);
    }

    /**
     * Create timer with specified duration in seconds
     * @param duration is number of seconds to countdown
     */
    public Timer(int duration)
    {
	myListeners = new ArrayList();
	myRunning = false;
	myThread = null;
	myTickPause = 1000;	
	myDuration = duration*1000;
    }

    /**
     * Sets pause in milliseconds, this is rate
     * at which timer fires
     * @param pause is # milliseconds between timer firings
     */
    
    public void setPause(int pause)
    {
	myTickPause = pause;
    }

    /**
     * Add a listener to receive timer events
     * @param tl is the listener added
     */
    public void addListener(TimerListener tl)
    {
	myListeners.add(tl);
    }

    /**
     * Remove a listener (error if not listening)
     * @param tl is the listener removed
     */
    public void removeListener(TimerListener tl)
    {
	myListeners.remove(tl);
    }

    /**
     * Starts this timer should notify listeners, but doesn't
     */
    public void start()
    {
	myThread = new Thread(this);
	myStartTime = new Date();
	myThread.setDaemon(true);
	myThread.start();
    }

    /**
     * Stops timer, notifies listeners that timer has stopped
     */
    public void stop()
    {
	myRunning = false;
	myStopTime = new Date();
	for(int k=0; k < myListeners.size(); k++) {
	    ( (TimerListener) myListeners.get(k)).timerStopped(this);
	}
    }

    /**
     * Does work, notifies listeners when ticking occurs
     */
    public void run()
    {
	myRunning = true;

	while (myRunning) {

	    try {
		pause();
	    }
	    catch (InterruptedException e) {
		myRunning = false;
		return;
	    }
	    
	    handleTicks();

	    if (isExpired()) {
		stop();
	    }
	}
    }

    /**
     * Returns true if timer has expired.
     * @return true iff this timer has expired
     */
    
    public boolean isExpired()
    {
	myStopTime = new Date();
	return timeRemaining() <= 0;
    }

    /**
     * Returns # milliseconds left before timer expires.
     * @return milliseconds remaining before isExpired() returns true
     */
    public long timeRemaining()
    {
	myStopTime = new Date();
	long secs = myStopTime.getTime() - myStartTime.getTime();
	long value = myDuration - secs;
	return value < 0 ? 0 : value;
    }

    private void pause() throws InterruptedException
    {
	myThread.sleep(myTickPause);
    }

    /**
     * Helper function to notify listeners
     */
    private void handleTicks()
    {
	for(int k=0; k < myListeners.size(); k++) {
	    ( (TimerListener) myListeners.get(k)).timerTicked(this);
	}
    }

    private boolean myRunning;
    private long    myDuration;
    private Date    myStartTime;
    private Date    myStopTime;
    private Thread  myThread;
    private int     myTickPause;
    
    private List myListeners;
}
