// Copyright 1996, Marimba Inc. All Rights Reserved.


// @(#)Timer.java, 1.19, 01/05/97





package marimba.util;





/**


 * An entry in the Timer queue.


 */


class TimerEntry {


    long tm;


    TimerClient client;


    Object arg;


    TimerEntry next;


}





/**


 * A Timer for keeping track of repeated time events.


 *


 * @author	Arthur van Hoff


 * @version 	1.19, 01/05/97


 */


public class Timer implements Runnable {


    /**


     * The master clock.


     */


    public static Timer master = new Timer();





    TimerEntry first;


    Thread thread;





    /**


     * Add a client to this clock.


     */


    public void add(TimerClient client) {


	add(client, System.currentTimeMillis(), null);


    }





    /**


     * Add a client to this clock.


     */


    public void add(TimerClient client, long tm, Object arg) {


	//System.out.println("ADD: " + client);


	TimerEntry entry = new TimerEntry();


	entry.tm = tm;


	entry.client = client;


	entry.arg = arg;


	add(entry);


    }





    /**


     * Add an entry to this clock.


     */


    synchronized void add(TimerEntry entry) {


	// Double check that this is the only entry


	TimerEntry prev = null;


	for (TimerEntry e = first ; e != null ; prev = e, e = e.next) {


	    if ((e.client == entry.client) && (e.arg == entry.arg)) {


		if (prev != null) {


		    prev.next = e.next;


		} else {


		    first = e.next;


		}


		break;


	    }


	}





	if ((first == null) || (first.tm > entry.tm)) {


	    entry.next = first;


	    first = entry;


	} else {


	    TimerEntry e = first;


	    for (; (e.next != null) && (entry.tm >= e.next.tm) ; e = e.next);


	    entry.next = e.next;


	    e.next = entry;


	}





	if (thread == null) {


	    thread = ThreadUtil.forkSystem("Timer", this);


	} else {


	    notify();


	}


    }





    /**


     * Remove a client from this Timer.


     */


    public void remove(TimerClient client) {


	remove(client, null);


    }





    /**


     * Remove a client from this clock.


     */


    public synchronized void remove(TimerClient client, Object arg) {


	//System.out.println("REMOVE: " + client);


	if (first != null) {


	    if ((first.client == client) && (first.arg == arg)) {


		first = first.next;


		notify();


	    } else {


		for (TimerEntry c = first ; c.next != null ; c = c.next) {


		    if ((c.next.client == client) && (c.next.arg == arg)) {


			c.next = c.next.next;


			notify();


			return;


		    }


		}


	    }


	}


    }





    /**


     * The clock is ticking....


     */


    public synchronized void run() {


	try {


	    while (true) {


		while (first == null) {


		    wait(1000);


		    if (first == null) {


			//System.out.println("Stop the clock!");


			thread = null;


			return;


		    }


		}


		long tm = first.tm - System.currentTimeMillis();


		if (tm > 0) {


		    //System.out.println("Waiting: " + tm);


		    wait(tm);


		} else {


		    Thread.sleep(20);


		}





		tm = System.currentTimeMillis();


		while ((first != null) && (first.tm <= tm)) {


		    //System.out.println("click: " + first.client);


		    TimerEntry entry = first;


		    first = entry.next;


		    entry.next = null;





		    Thread.yield();





		    try {


			entry.tm = entry.client.tick(entry.tm, entry.arg);


			if (entry.tm > 0) {


			    add(entry);


			}


		    } catch (ThreadDeath e) {


			throw e;


		    } catch (Throwable e) {


			e.printStackTrace();


		    }


		}


	    }


	} catch (InterruptedException e) {


	}


    }


}





