(function(jQuery)
{
	
	var countdownDefaultOptions =
	{
		milliSeconds:		true,
		zeroCallback:		null
	};
	
	var countdownDefaultClasses =
	{
		days:					'countdown-days',
		hours:					'countdown-hours',
		minutes:				'countdown-minutes',
		seconds:				'countdown-seconds',
		millseconds:			'countdown-milli-seconds'
	};
	
	var countdownData =
	{
		DATA_NAME:				'countdowndata',
		
		// set is used to assign data to the specified element
		set:					function(element, data)
								{
									if (data != undefined)
									{
										var oldData = countdownData.get(element);
										var extendedData = jQuery.extend({}, oldData, data);
										jQuery.data(element.get(0), countdownData.DATA_NAME, extendedData);
										return data;
									}
									return false;
								},
				
		// get is used to return saved data from the specified element				
		get:					function(element)
								{
									return jQuery.data(element.get(0), countdownData.DATA_NAME) || {};
								},								
								
		opts:					function(element, options)
								{
									if (options != undefined) return countdownData.set(element, {opts: options}).opts;
									return countdownData.get(element).opts;	
								},
					
		clss:					function(element, classes)
								{
									if (classes != undefined) return countdownData.set(element, {clss: classes}).clss;
									return countdownData.get(element).clss;	
								},
								
		timestamp:				function(element, timestamp)
								{
									if (timestamp != undefined) return countdownData.set(element, {timestamp: timestamp}).timestamp;
									return countdownData.get(element).timestamp || 0;	
								}
	};
	
	var counterFind =
	{
		daysElement:			function(countdownContainer, classes)
								{
									return countdownContainer.find('.' + classes.days);
								},
								
		hoursElement:			function(countdownContainer, classes)
								{
									return countdownContainer.find('.' + classes.hours);
								},
								
		minutesElement:			function(countdownContainer, classes)
								{
									return countdownContainer.find('.' + classes.minutes);
								},
								
		secondsElement:			function(countdownContainer, classes)
								{
									return countdownContainer.find('.' + classes.seconds);
								},
								
		milliSecondsElement:	function(countdownContainer, classes)
								{
									return countdownContainer.find('.' + classes.milliseconds);
								}
	};
	
	// this is the starting point of the plugin
	jQuery.fn.countdown = function(command, args)
	{
		// loop through each of the passed elements and then return them for chaining
		return this.each(function()
		{
			countdownContainer = $(this);
			
			// determine what command was given and act on it appropriately
			
			if (typeof(command) == 'string')
			{
				switch (command)
				{
					case 'initialize': case 'init':
						initialize(countdownContainer, args.timestamp, args.date, args.options, args.classes, args.callbacks);
						break;
					case 'stop':
						stopLoop(countdownContainer);
						break; 
				}
			}
		});
	};
	
	// function initialize
	function initialize(countdownContainer, timestamp, date, options, classes, callbacks)
	{
		// extend the default options with the specified options and then store them
		options = countdownData.opts(countdownContainer, jQuery.extend({}, countdownDefaultOptions, options));
		
		// extend the default classes with the specified classes and then store them
		classes = countdownData.clss(countdownContainer, jQuery.extend({}, countdownDefaultClasses, classes));
		
		timestamp = countdownData.timestamp(countdownContainer, getTimestamp(countdownContainer, timestamp, date));
		
		// get countdown elements
		var daysElement = getDaysElement(countdownContainer);
		var hoursElement = getHoursElement(countdownContainer);
		var minutesElement = getMinutesElement(countdownContainer);
		var secondsElement = getSecondsElement(countdownContainer);
		var milliSecondsElement = getMillSecondsElement(countdownContainer);
		
		// start loop
		update(countdownContainer, options, classes, timestamp, daysElement, hoursElement, minutesElement, secondsElement, milliSecondsElement);
		startLoop(countdownContainer, options, classes, timestamp, daysElement, hoursElement, minutesElement, secondsElement, milliSecondsElement);
	};
	
	// function update
	function update(countdownContainer, options, classes, timestamp, daysElement, hoursElement, minutesElement, secondsElement, millSecondsElement)
	{		
		options = getOptions(countdownContainer, options);
		classes	= getClasses(countdownContainer, classes);
		timestamp = getTimestamp(countdownContainer, timestamp);
		
		// get countdown elements
		daysElement = getDaysElement(countdownContainer, daysElement);
		hoursElement = getHoursElement(countdownContainer, hoursElement);
		minutesElement = getMinutesElement(countdownContainer, minutesElement);
		secondsElement = getSecondsElement(countdownContainer, secondsElement);
		millSecondsElement = getMillSecondsElement(countdownContainer, millSecondsElement);
		
		var dt = new Date(timestamp);
		var now = new Date();
		
		var days = 0;
		var hours = 0;
		var minutes = 0;
		var seconds = 0;
		var milli = 0;

		// make sure dates are within same timezone
		//var offset = now.getTimezoneOffset() / 60;
		//now.setHours(now.getHours() - offset);

		if (dt > now)
		{			
			var diff = new Date(dt - now);
		
			var days = Math.floor((dt - now) / (1000 * 60 * 60 * 24));
			var hours = diff.getHours();
			var minutes = diff.getMinutes();
			var seconds = diff.getSeconds() + 1;
			var milli = diff.getMilliseconds();	
		}

		// update elements
		daysElement.html(days);
		hoursElement.html(hours);
		minutesElement.html(minutes);
		secondsElement.html(seconds);
		
		if (options.zeroCallback !== null)
		{
			if (dt-now <= 0) options.zeroCallback();
		}
	};
	
	// function startLoop
	function startLoop(countdownContainer, options, classes, timestamp, daysElement, hoursElement, minutesElement, secondsElement, milliSecondsElement)
	{
		options = getOptions(countdownContainer, options);
		
		var interval = 1000;
		if (options.milliSeconds === true) interval = 1;
		
		countdownContainer.get(0).loop = window.setInterval(
			function()
			{
				update(countdownContainer, options, classes, timestamp, daysElement, hoursElement, minutesElement, secondsElement, milliSecondsElement);
			}, interval);
	};
	
	// function restartLoop
	function restartLoop()
	{
		stopLoop();
		startLoop();
	}

	// function stopLoop
	function stopLoop(countdownContainer)
	{
		clearInterval(countdownContainer.get(0).loop);
	};
	
	// function getOptions
	function getOptions(countdownContainer, options)
	{
		if (options === undefined) options = countdownData.opts(countdownContainer);
		return options;
	};
	
	// function getClasses
	function getClasses(countdownContainer, classes)
	{
		if (classes === undefined) classes = countdownData.clss(countdownContainer);
		return classes;
	};
	
	// function getTimestamp
	function getTimestamp(countdownContainer, timestamp, date)
	{
		if (timestamp === undefined) timestamp = countdownData.timestamp(countdownContainer);
		
		if (timestamp === 0 || (date !== undefined && timestamp > 0))
		{
			var dt = new Date(date);
			timestamp = dt.getTime();
		}
		
		return timestamp;
	};
	
	// function getDaysElement
	function getDaysElement(countdownContainer, daysElement, classes)
	{
		classes = getClasses(countdownContainer, classes);
		if (daysElement === undefined) daysElement = counterFind.daysElement(countdownContainer, classes);
		return daysElement;
	};
	
	// function getHoursElement
	function getHoursElement(countdownContainer, hoursElement, classes)
	{
		classes = getClasses(countdownContainer, classes);
		if (hoursElement === undefined) hoursElement = counterFind.hoursElement(countdownContainer, classes);
		return hoursElement;
	};
	
	// function getMinutesElement
	function getMinutesElement(countdownContainer, minutesElement, classes)
	{
		classes = getClasses(countdownContainer, classes);
		if (minutesElement === undefined) minutesElement = counterFind.minutesElement(countdownContainer, classes);
		return minutesElement;
	};
	
	// function getSecondsElement
	function getSecondsElement(countdownContainer, secondsElement, classes)
	{
		classes = getClasses(countdownContainer, classes);
		if (secondsElement === undefined) secondsElement = counterFind.secondsElement(countdownContainer, classes);
		return secondsElement;
	};
	
	// function getMillSecondsElement
	function getMillSecondsElement(countdownContainer, milliSecondsElement, classes)
	{
		classes = getClasses(countdownContainer, classes);
		if (milliSecondsElement === undefined) milliSecondsElement = counterFind.milliSecondsElement(countdownContainer, classes);
		return milliSecondsElement;
	};
	
})(jQuery);
