//= require <mootools-core-1.3-full-nocompat>
//= require <uiframework/UICEvent>
//= require <uiframework/UIDUtil>
/**
 * Framework event dispatcher with support for
 * native DOM events.
 */
var EventDispatcher = function(){

	this.initialize = function(target)
	{
		this.target = target ? target : this;
		this.nonCaptureEvents = {};
		this.captureEvents = {};
		this.boundEvents = {};
	};

	/**
     * Adds an event listener.  Native DOM events
     * are routed to the MooTools framework but
     * are caught internally and converted to a UICEvent.
     *
     * @param type String The type of event
     * @param listener Function used to process the event.
     * @param useCapture Boolean indicating whether or not to listen for this even during the capturing phase.
     * @param priority integer indicating the priority of the listener. The integer can be a negative or positive number.
     * @param scope Object The scope of 'this' in the context of the listener function. Defaults to window.
     */
	this.addEventListener = function(type, listener, useCapture, priority, scope)
	{
		if (!listener)
			throw new Error("Event listener is null");
		var o = this.target ? this.target : this;
		if (!priority) priority = 0;
		if (!scope) scope = window;
		// Do not add it if we already have it.
		var hash = useCapture ? this.captureEvents : this.nonCaptureEvents;
		var listeners = hash[type];
		if (listeners && listeners.length)
		{
			var i = listeners.length;
			while(i--)
			{
				if (listeners[i].listener == listener)
					return;
			}
		}
		var eventID = UIDUtil.createUID();
		if (!listeners)
			hash[type] = [];
		hash[type].push({
			id:eventID,
			listener:listener,
			priority:priority,
			scope:scope
		});
		hash[type].sort(function(obj1, obj2){
			return obj2.priority - obj1.priority;
		});
		// Native events get routed to the framework
		if ((Element.NativeEvents[type] || Element.Events[type]) && o.element)
		{
			var hook = this.eventDispatcherHook.bind(o, type);
			if (!this.boundEvents[type])
				this.boundEvents[type] = [];
			this.boundEvents[type][priority] = ({
				id:eventID,
				listener:hook
			});
			o.element.addEvent(type, hook);
		}
	};

	/**
     * Removes an event listener.
     * 
     * @param type String The type of event
     * @param listener Function used to process the event.
     * @param useCapture Boolean indicating whether or not the listener to be removed uses capture.
     */
	this.removeEventListener = function(type, listener, useCapture)
	{
		var hash = useCapture ? this.captureEvents : this.nonCaptureEvents;
		var listeners = hash[type];
		if (!listeners || !listeners.length)
			return;
		var i = listeners.length;
		var eventData;
		while(i--)
		{
			if (listeners[i].listener == listener)
			{
				eventData = listeners[i];
				listeners.splice(i, 1);
				break;
			}
		}
		// Remove it from the framework
		listeners = this.boundEvents[type];
		if (!listeners || !listeners.length)
			return;
		i = listeners.length;
		while(i--)
		{
			if (listeners[i].id == eventData.id)
			{
				this.element.removeEvent(type, listeners[i].listener);
				listeners.splice(i, 0);
				break;
			}
		}
	};

	/**
     * Hook for catching native DOM events and
     * passing them through the UIC framework.
     * 
     * @param event MooTools Event object
     * @param type String representing the type of Event
     */
	this.eventDispatcherHook = function(type, event)
	{
		var e = new UICEvent(type, false, false, event);
		e.type = type;
		this.dispatchEvent(e);
	};

	/**
     * Dispatches an event into the event flow
     * @param event UICEvent to dispatch.
     */
	this.dispatchEvent = function(event)
	{
		var o = event.target = this.target || this;
		//Build the path
		var path = [o];
		var parent = o.parentComponent;
		while(parent)
		{
			if (parent)
			{
				path.push(parent);
				parent = parent.parentComponent;
			}
		}
		// Listener iterator
		var listenerIterator = function(listeners)
		{
			if (!listeners)
				return;
			var len = listeners.length;
			// Traverse highest priority first
			for (var j = 0; j < len; j++)
			{
				if (event.immediatePropagationStopped)
					return;
				var evtObj = listeners[j];
				//console.log(evtObj.id);
				evtObj.listener.call(evtObj.scope, event);
			}
		}
		// Capturing phase
		event.eventPhase = UICEvent.CAPTURING_PHASE;
		var i = path.length;
		while(i--)
		{
			if (event.propagationStopped)// Node Level
				return;
			var uic = path[i];
			if (!uic.hasEventListener(event.type) || !uic.captureEvents[event.type] || uic == this)
				continue;
			event.currentTarget = uic;
			listenerIterator(uic.captureEvents[event.type]);
		}
		// At Target and Bubbling phases.
		var len2 = path.length;
		for (i = 0; i < len2; i++)
		{
			if (event.propagationStopped)// Node Level
				return;
			var uic2 = path[i];
			if (!uic2.hasEventListener(event.type) || !uic2.nonCaptureEvents[event.type])
				continue;
			if (uic2 == o)
				event.eventPhase = UICEvent.AT_TARGET;
			else
				event.eventPhase = UICEvent.BUBBLING_PHASE;
			if (event.bubbles == false && event.eventPhase == UICEvent.BUBBLING_PHASE)
				break;
			listenerIterator(uic2.nonCaptureEvents[event.type]);
		}
	};

	/**
     * Determines if this object has the event type
     * registered with it.
     * @return Boolean true if the event is registered with this object, false otherwise
     */
	this.hasEventListener = function(type)
	{
		return (this.nonCaptureEvents[type] || this.captureEvents[type]);
	};

	this.willTrigger = function(type)
	{
	
	};
};
EventDispatcher = new Class(new EventDispatcher());
