/**
 * fusionLib JavaScript library to implement timers.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 *
 * Copyright (c) 2007 - 2013 fusionLib. All rights reserved.
 */

(function() {
	var timerIdx = 1,		// Counter for timer IDs
		timerElems = [];	// List of elements with timers

	/**
	 * Private function to add a timer to an element.
	 *
	 * @param domElem element The element to attach the timer to.
	 * @param int interval The ms interval for the timer.
	 * @param string label The label.
	 * @param int times The number of times to call the timer.
	 * @param function fn The function to call.
	 */
	function addTimer(element, interval, label, times, fn) {
		if(typeof label == 'function') {
			fn = label;
			label = interval;
			times = 0;
		}
		else if(typeof times == 'function') {
			fn = times;
			if(typeof label == 'string')
				times = 0;
			else {
				times = label;
				label = interval;
			}
		}

		// Assign the the timer function a timer Id
		fn.flTimerID = fn.flTimerID || timerIdx++;

		// Create store for label
		if(!element.flTimers) {
			element.flTimers = [];
			timerElems.push(element);	// remember the element
		}
		if(!element.flTimers[label]) element.flTimers[label] = [];

		// Create the timer remove previous if present
		var counter = 0;
		removeTimer(element, label, fn);
		element.flTimers[label][fn.flTimerID] = window.setInterval(function() {
			if((++counter > times && times) || fn.call(fusionLib(element), counter) === false)
				removeTimer(element, label, fn);
		}, interval);
	}

	/**
	 * Private function to remove a timer.
	 *
	 * @param domElem element The element the timer attached to.
	 * @param string label The label of the timers to remove.
	 * @param function fn The function called by the timer to remove.
	 */
	function removeTimer(element, label, fn) {
		if(!element.flTimers) return;

		if(typeof label == 'function') {
			fn = label;
			label = undefined;
		}

		if(!label) {
			for(label in element.flTimers)
				removeTimer(element, label, fn);
		}
		else if(element.flTimers[label]) {
			if(fn) {
				var id = typeof fn == 'function' ? fn.flTimerID : fn;
				window.clearInterval(element.flTimers[label][id]);
				delete element.flTimers[label][id];
			}
			else {
				for(var timerID in element.flTimers[label])
					removeTimer(element, label, timerID);
			}
		}
	}

	fusionLib.fn.extend({
		/**
		 * Invoke the function after the given interval repeatedly.
		 *
		 * @param int interval The interval in ms.
		 * @param string label Optional label for the timer if not given interval is used.
		 * @param int times The optional number of times to run or 0 for infinite.
		 * @param function fn The function to invoke.
		 * @return fusionLib This object.
		 */
		timerInterval: function(interval, label, times, fn) {
			return this.each(function() {
				addTimer(this, interval, label, times, fn);
			});
		},

		/**
		 * Invoke the function after the given interval once only.
		 *
		 * @param int interval The interval in ms.
		 * @param string label Optional label for the timer if not given interval is used.
		 * @param function fn The function to invoke.
		 * @return fusionLib This object.
		 */
		timerOnce: function(interval, label, fn) {
			if(typeof label == 'function') {
				fn = label;
				label = interval;
			}
			return this.timerInterval(interval, label, 1, fn);
		},

		/**
		 * Stop the timer.
		 *
		 * @param string label The optional label of the timer(s) to stop.
		 * @param function fn The timer optional function.
		 * @return fusionLib This object.
		 */
		timerStop: function(label, fn) {
			return this.each(function() {
				removeTimer(this, label, fn);
			});
		}
	});

	// Register unload event to remove all timers
	fusionLib(window).bind('unload', function() {
		for(var i=0;i<timerElems.length;i++)
			removeTimer(timerElems[i]);
	});
})();