/*
 * Copyright (c) 2006-2008 MiniMe. Code released under The MIT/X Window System
 * License. Full license text can be found in license.txt
 */
package minime.core;

import java.util.Vector;

import minime.Logger;

/**
 * The EventDispather class is used to dispatcher event.
 * 
 * @author yishu
 * 
 */
public class EventDispatcher implements Runnable
{
    private Vector listeners = null;
    private Vector eventQueue = null;
    private static EventDispatcher instance = null;
    private static Thread engine = null;

    static final Logger LOG = Logger.getLogger("minime.EventDispatcher");

    /**
     * Use static factory method to return a EventDispatcher reference instance.
     * 
     * @return a EventDispatcher reference instance.
     */
    static public EventDispatcher getInstance()
    {
        if (instance == null)
        {
            throw new RuntimeException("EventDispatcher::bootstrap has not been called yet");
        }
        return instance;
    }

    /**
     * Initializes EventDispatcher instance the first time it's called.
     * 
     */
    public static void bootstrap()
    {
    	if(instance == null)
    	{
    		instance = new EventDispatcher();
    	}
        
        engine = new Thread(instance);
        engine.start();
    }

    private EventDispatcher()
    {
        eventQueue = new Vector();
        listeners = new Vector();
    }

    /**
     * Attaches an EventListener object, which can listen event.
     * 
     * @param listener
     *            an EventListener object.
     */
    public void attach(EventListener listener)
    {
        LOG.debug("attach:" + listener);
        synchronized (listeners)
        {
            // insert listner into the list depending of its priority
            int i = listeners.size() - 1;
            if (i == -1)
            {
                i = 0;
            }
            else
            {
                while (i >= 0 && ((EventListener) listeners.elementAt(i)).getPriority() < listener.getPriority())
                {
                    i--;
                }
                i++;
            }
            listeners.insertElementAt(listener, i);
            // listeners.addElement( listener );
        }
    }

    /**
     * Detaches an EventListener object, which can't listen event any more
     * before it attches again.
     * 
     * @param listener
     *            an EventListener object.
     */
    public void detach(EventListener listener)
    {
        LOG.debug("detach:" + listener);
        synchronized (listeners)
        {
            listeners.removeElement(listener);
        }
    }

    /**
     * Fires an event,EventListener instances can linten this event.
     * 
     * @param event
     *            the event object to be fired
     */
    public void fire(Event event)
    {
        synchronized (engine)
        {
            int i = eventQueue.size() - 1;
            if (i == -1)
            {
                i = 0;
            }
            else
            {
                while (i >= 0 && ((Event) eventQueue.elementAt(i)).when < event.when)
                {
                    i--;
                }
                i++;
            }
            eventQueue.insertElementAt(event, i);
            engine.notify();
        }
    }

    public void run()
    {
        while (true)
        {
            Event e = null;
            long now = System.currentTimeMillis();
            long sleepTime = 0;
            synchronized (engine)
            {
                int count = eventQueue.size();
                if (count > 0)
                {
                    e = (Event) eventQueue.elementAt(count - 1);
                    if (e.when <= now)
                    {
                        eventQueue.removeElementAt(count - 1);
                    }
                    else
                    {
                        sleepTime = e.when - now;
                        e = null;
                    }
                }
                if (e == null)
                {
                    try
                    {
                        if (sleepTime < 0)
                        {
                            sleepTime = 0;
                        }
                        engine.wait(sleepTime);
                    }
                    catch (InterruptedException ie)
                    {
                    }
                }
                else
                {
                    dispatch(e);
                    Thread.yield();
                }
            }
        }
    }

    /**
     * Dispatches the event after it has been handled.
     * 
     * @param e
     *            an event object
     */
    private void dispatch(Event e)
    {
        LOG.debug("dispatch:" + e);
        synchronized (listeners)
        {
            int count = listeners.size();
            int i ;
            for (i = count - 1; i >= 0; i--)
            {
                try
                {
                    boolean done = false;
                    EventListener listener = (EventListener) listeners.elementAt(i);
                    if (listener instanceof Component)
                    {
                        if (((Component) listener).isFocused())
                        {
                            LOG.debug("send to:" + listener);
                            done = listener.onEvent(e);
                        }
                    }
                    else
                    {
                        LOG.debug("send to:" + listener);
                        done = listener.onEvent(e);
                    }
                    if (done)
                    {
                        LOG.debug("consumed ..." );
                        break;
                    }
                }
                catch (Throwable t)
                {
                    t.printStackTrace();
                }
            }
            if (i<0)
                LOG.debug("event not consumed:" + e);
        }
    }
}
