/**
 * mcd-js DOM Utilities
 * 
 * @author   Michael Girouard (mgirouard@mcdpartners.com)
 * @requires mcd.dom
 */
mcd.event = function () {
	
	var ELEMENT_INDEX       = 0;
	var TYPE_INDEX          = 1;
	var ACTION_INDEX        = 2;
	var SCOPED_ACTION_INDEX = 3;
	
	var cachedActions = [
		/* [element, type, origAction, scopedAction] */
	];
	
	var getCachedActionIndex = function (element, type, action) {
		for (var i = 0; i < cachedActions.length; i++) {
			if (cachedActions[i][ELEMENT_INDEX] === element &&
				cachedActions[i][TYPE_INDEX]    === type &&
				cachedActions[i][ACTION_INDEX]  === action) {
				
				return i;
			}
		}
		
		return false;
	};
	
	var getCachedAction = function (element, type, action) {
		var index = getCachedActionIndex(element, type, action);
		
		if (index) {
			return cachedActions[index][SCOPED_ACTION_INDEX];
		}
		else {
			return false;
		}
	}
	
	return {
		/**
		 * Adds an event listener
		 * 
		 * Take note of the self-executing method which will automatically return 
		 * the proper method depending on the user agent's level of DOM support.
		 * 
		 * @param  {String|HTMLElement} element
		 * @param  {String} type
		 * @param  {Function} action
		 * @param  {Object} scope
		 * @return {HTMLElement}
		 */
		add : function () {
			/* W3 DOM */
			if ('addEventListener' in window) {
				return function (element, type, action, scope) {
					element = mcd.dom.getElement(element);
					
					var scopedAction = function (event) {
						scope = scope || element;
						return action.call(scope, event);
					};
					
					cachedActions.push([element, type, action, scopedAction]);
					element.addEventListener(type, scopedAction, false);
					
					return element;
				};
			}
			
			/* Explorer */
			else if ('attachEvent' in window) {
				return function (element, type, action, scope) {
					element = mcd.dom.getElement(element);
					
					var scopedAction = function (event) {
						scope = scope || element;
						return action.call(scope, event);
					};
					
					cachedActions.push([element, type, action, scopedAction]);
					element.attachEvent('on' + type, scopedAction, false);
					
					return element;
				};
			}
			
			/* Unknow or Missing Event Model */
			else {
				return function (element, type, action, scope) {
					return mcd.dom.getElement(element);
				};
			}
		}(),
		
		/**
		 * Removes an event listener
		 * 
		 * Take note of the self-executing method which will automatically return 
		 * the proper method depending on the user agent's level of DOM support.
		 * 
		 * @param  {String|HTMLElement} element
		 * @param  {String} type
		 * @param  {Function} action
		 * @return {HTMLElement}
		 */
		remove : function () {
			/* W3 DOM */
			if ('removeEventListener' in window) {
				return function (element, type, action) {
					element = mcd.dom.getElement(element);
					action  = getCachedAction(element, type, action);
					
					if (action) {
						element.removeEventListener(type, action, false);
						return element;						
					}
				};
			}
			
			/* Explorer */
			else if ('detachEvent' in window) {
				return function (element, type, action) {
					element = mcd.dom.getElement(element);
					action  = getCachedAction(element, type, action);
					
					if (action) {
						element.detachEvent('on' + type, action);
						return element;						
					}
				};
			}
			
			/* Unknown or Missing Event Model */
			else {
				return function (element, type, action) {
					return mcd.dom.getElement(element);
				};
			}
		}(),
		
		/**
		 * Prevents the default behavior of an event
		 * 
		 * @param  {Event} event
		 * @return {Event}
		 */
		preventDefault : function (event) {
			if ('preventDefault' in event) {
				event.preventDefault();
			}
			else {
				event.returnValue = false;
			}
			
			return event;
		},
		
		/**
		 * Stops the bubbling process
		 * 
		 * @param  {Event} event
		 * @return {Event}
		 */
		cancelBubble : function (event) {
			if (event.cancelBubble) {
				event.cancelBubble = true;
			}
			else {
				event.stopPropagation();
			}
			
			return event;
		},
		
		/**
		 * Gets the target element of an event
		 * 
		 * @param  {Event}
		 * @return {HTMLElement}
		 */
		getTarget : function (event) {
			return event.target || event.srcElement;
		}
	};
}();