package cnp.ew.util;

public final class CpTimer implements Runnable {

    public static CpTimer defaultTimer = null;

    public static CpTimer getDefaultTimer()
    {
        if (defaultTimer == null) {
            defaultTimer = new CpTimer();
        }
        return defaultTimer;
    }

    CpTimerItem root;


    CpTimer() {
	    Thread thread = new Thread(this);
	    thread.start();
    }


    public void register(CpTimerUser timerUser)
    {
	    register(timerUser, 0, -1);
    }

    public void register(CpTimerUser timerUser, long wakeUpTimeDelta)
    {
        register(timerUser, wakeUpTimeDelta, -1);
    }

    public void register(CpTimerUser timerUser, long wakeUpTimeDelta, int timerType)
    {
    	CpTimerItem item = new CpTimerItem();
    	item.wakeUpTime = wakeUpTimeDelta + System.currentTimeMillis();
    	item.timerUser = timerUser;
    	item.timerType = timerType;
    	register(item);
    }

    synchronized void register(CpTimerItem newItem)
    {
//System.out.println("registering item: " + newItem);
    	CpTimerItem curItem = root;

    	while (curItem != null) {
    	    if (curItem.timerUser == newItem.timerUser && curItem.timerType == newItem.timerType) {
    	        curItem.wakeUpTime = newItem.wakeUpTime;
    	        return;
    	    }
    	    curItem = curItem.nextItem;
    	}

    	if (root == null || root.wakeUpTime > newItem.wakeUpTime) {
    	    newItem.nextItem = root;
    	    root = newItem;
    	} else {
    	    curItem = root;
            while (curItem != null) {
                if (curItem.nextItem == null) {
                    curItem.nextItem = newItem;
                    break;
                } else if (newItem.wakeUpTime < curItem.nextItem.wakeUpTime) {
                        newItem.nextItem = curItem.nextItem;
                        curItem.nextItem = newItem;
                        break;
                } else {
                    curItem = curItem.nextItem;
                }
            }
        }
    	notify();
    }


    public void unregister(CpTimerUser timerUser)
    {
	    unregister(timerUser, -1);
    }


    public synchronized void unregister(CpTimerUser timerUser, int timerType)
    {
        //System.out.println("Unregistering user: " + timerUser.getClass().getName() + " timerType: " + timerType);
    	if (root != null) {
    	    if ((root.timerUser == timerUser) && (root.timerType == timerType)) {
    		    root = root.nextItem;
    	        //System.out.println("UNREG: removing root, root now : " + root);
    		    return;
    	    } else {
    	        CpTimerItem curItem = root;
    	        while (curItem.nextItem != null) {
    	            //System.out.println("UNREG: traversing " + curItem);
    	            if ((curItem.nextItem.timerUser == timerUser) && (curItem.nextItem.timerType == timerType)) {
    	                //System.out.println("UNREG: removing " + curItem.nextItem);
    	                curItem.nextItem = curItem.nextItem.nextItem;
    	                return;
    	            }
    	            curItem = curItem.nextItem;
    	        }
    	    }
    	}
    	//System.out.println("Didn't find user in unregister");
    }


    public synchronized void run()
    {
    	try {
    	    while (true) {
    	        //System.out.println("RUN: waiting for root to be non null");
        		while (root == null) {
        		    wait();
        		}
        		long wakeUpTimeDelta = root.wakeUpTime - System.currentTimeMillis();
    	        //System.out.println("RUN: root now not null: " + root + "  delta: " + wakeUpTimeDelta);
        		if (wakeUpTimeDelta > 0) {
        		    wait(wakeUpTimeDelta);
        		} else {
        		    Thread.sleep(20);
        		}

        		long curTime = System.currentTimeMillis();
        		while ((root != null) && (root.wakeUpTime <= curTime)) {
        		    CpTimerItem curItem = root;
    	        //System.out.println("RUN: in loop, curItem: " + curItem);
        		    root = curItem.nextItem;
        		    curItem.nextItem = null;

        		    Thread.yield();

        		    try {
        		        //System.out.println("RUN beginning timer event for " + curItem);
            			wakeUpTimeDelta = curItem.timerUser.timerEvent(curItem.timerType);
        		        //System.out.println("RUN ended timer event for " + curItem + " delta: " + wakeUpTimeDelta);
            			if (wakeUpTimeDelta > 0) {
            			    curItem.wakeUpTime = System.currentTimeMillis() + wakeUpTimeDelta;
            			    register(curItem);
            			}
        		    } catch (ThreadDeath e) {
        			    throw e;
        		    } catch (Throwable e) {
        			    e.printStackTrace();
        		    }
        		}
    	    }
    	} catch (InterruptedException e) {
    	}
    }
}

