/**
 * Returns true if this date resides under DST.
 *
 * @return boolean true if this date is DST, false otherwise
 **/
Date.prototype.isCurrentlyDST = function()
{
	var offsetInJuly = new Date(2004, 6).getTimezoneOffset();
	var offsetNow = this.getTimezoneOffset();

	return (offsetNow == offsetInJuly);
};

/**
 * Returns a new date object based off this one but converted to another time zone.
 *
 * @param hourOffset float offset in hours for the new time zone from GMT (-5 for EST, -8 for PST, etc.)
 * @return Date new Date object based off this one but converted to another time zone
 **/
Date.prototype.toTimeZone = function(hourOffset)
{
	var offset = this.getTimezoneOffset();
	var estOffset = (this.isCurrentlyDST() ? -hourOffset - 1 : -hourOffset) * 60 * 60 * 1000;
	var thisEpoch = this.getTime();
	var thenEpoch = thisEpoch + (offset * 60 * 1000) - estOffset;

	return new Date(thenEpoch);
};

var sale = Class.create({
	initialize: function(length, flyer, timer, end, close, timeZone, format) {
		if(!timeZone) {
			timeZone = -5;
		}
		if(!close)
			this.closeButton = $('close');
		else
			this.closeButton = close;

		if(!format)
		{
			this.format = '%H:%i';
		}
		else
		{
			this.format = format;
		}

		this.running = true;
		this.flyer = flyer;
		this.timer = timer;

		this.tzOffset = timeZone;
		this.makeDate(end);

		this.utPE = new PeriodicalExecuter(this.updateTimer.bind(this), 0.25);
		this.hPE = new PeriodicalExecuter(this.hide.bind(this), length);

		this.closeButton.observe('click', this.close.bind(this));
 	},

	makeDate: function(info) {
		var month = {
			january: 0,
			february: 1,
			march: 2,
			april: 3,
			may: 4,
			june: 5,
			july: 6,
			august: 7,
			september: 8,
			october: 9,
			november: 10,
			december: 11
		};

		if(!info.date.year) {
			var n = new Date();
			info.date.year = n.getYear();
		}

		if(info.date.month.constructor == String)
		{
			info.date.month = info.date.month.toLowerCase();
			info.date.month = month[info.date.month];
		}
		else
		{
			info.date.month -= 1;
		}
		
		if(info.time.meridian)
		{
			if(info.time.meridian == 'pm')
			{
				if(info.time.hour < 12)
					info.time.hour += 12;
			}
			else
			{
				if(info.time.hour == 12)
					info.time.hour = 0;
			}
		}

		this.end = new Date();
		this.end.setFullYear(info.date.year, info.date.month, info.date.day);
		this.end.setHours(info.time.hours, info.time.minutes, info.time.seconds);
	},

	updateTimer: function(pe) {
		var now = new Date().toTimeZone(this.tzOffset);
		var diff = this.end - now;
		
		diff = Math.ceil(diff/1000);
		
		this.timer.update(
			this.formatDiff(diff)
		);

		if(this.running && !this.flyer.visible())
			this.flyer.show();

		if(this.running == false)
			pe.stop();
 	},

	hide: function(pe) {
		this.flyer.hide();
		this.running = false;
		pe.stop();
	},

	close: function() {
		this.running = false;
		this.utPE.stop();
		this.hPE.stop();
		this.flyer.hide();
	},

	formatDiff: function (diff)
	{
		var m = 60;
		var h = 60 * 60;
		var d = 60 * 60 * 24;
		var w = 60 * 60 * 24 * 7;

		this.diff = {};
		
		if(diff && diff > 0)
		{
			if(this.format.include('%w') || this.format.include('%W'))
			{
				this.diff.weeks = Math.floor(diff/w);
				diff -= (this.diff.weeks * w);
			}

			if(this.format.include('%d') || this.format.include('%j'))
			{
				this.diff.days = Math.floor(diff/d);
				diff -= (this.diff.days * d);
			}

			if(this.format.include('%H') || this.format.include('%h'))
			{
				this.diff.hours = Math.floor(diff/h);
				diff -= (this.diff.hours * h);
			}

			if(this.format.include('%i'))
			{
				this.diff.mins = Math.floor(diff/m);
				diff -= (this.diff.mins * m);
			}

			if(this.format.include('%s'))
			{
				this.diff.secs = Math.floor(diff);
			}
		}
		else
		{
			this.diff.weeks = '0';
			this.diff.days = '0';
			this.diff.hours = '0';
			this.diff.mins = '0';
			this.diff.secs = '0';
		}

		var returnStr = this.format;
		var codes = this.format.match(/%[a-z]{1,3}/ig);
		sale.diff = this.diff;
		sale.lastCode = {
			code: '',
			value: ''
		};
		codes.each(
			function (code)
			{
				/* If the code isn't in the template string (i.e. already replaced) skip it */
				if(!returnStr.include(code))
				{
					return;
				}

				var replace = '';
				if(sale.replaceChars[code])
				{
					replace = sale.replaceChars[code].call();
				}
				else
				{
					replace = code;
				}

				if(code != '%lbl')
				{
					returnStr = returnStr.gsub(code, replace);
				}
				else
				{
					returnStr = returnStr.sub(code, replace);
				}

				sale.lastCode = {
					code: code,
					value: replace
				};

			}.bind(this)
		);

		return returnStr;
	}
});

sale.replaceChars = {
	/* Weeks, no leading "0" */
	'%w': function ()
	{
		return sale.diff.weeks;
	},
	/* Weeks, with leading "0" */
	'%W': function ()
	{
		return ''+(sale.diff.weeks < 10 ? '0' : '')+sale.diff.weeks;
	},
	/* Days, with leading "0" */
	'%d': function ()
	{
		return ''+(sale.diff.days < 10  ? '0' : '')+sale.diff.days;
	},
	/* Days, no leading "0" */
	'%j': function ()
	{
		return sale.diff.days;
	},
	/* Hours, no leading "0" */
	'%h': function ()
	{
		return sale.diff.hours;
	},
	/* Hours, with leading "0" */
	'%H': function ()
	{
		return ''+(sale.diff.hours < 10 ? '0' : '')+sale.diff.hours;
	},
	/* Minutes */
	'%i': function ()
	{
		return ''+(sale.diff.mins < 10 ? '0' : '')+sale.diff.mins;
	},
	/* Seconds */
	'%s': function ()
	{
		return ''+(sale.diff.secs < 10 ? '0' : '')+sale.diff.secs;
	},
	/* Label (to display proper label of immediate predecessor code */
	'%lbl': function ()
	{
		if(sale.formatLabels[sale.lastCode.code])
		{
			return sale.formatLabels[sale.lastCode.code].call(this);
		}
		return '';
	}
};

sale.formatLabels = {
	'%w': function ()
	{
		return 'Week'+(sale.lastCode.value != 1 ? 's' : '');
	},
	'%W': function ()
	{
		return sale.formatLabels['%w'].call(this);
	},
	'%d': function ()
	{
		return 'Day'+(sale.lastCode.value != 1 ? 's' : '');
	},
	'%j': function ()
	{
		return sale.formatLabels['%d'].call(this);
	},
	'%h': function ()
	{
		return 'Hour'+(sale.lastCode.value != 1 ? 's' : '');
	},
	'%H': function ()
	{
		return sale.formatLabels['%h'].call(this);
	},
	'%i': function ()
	{
		return 'Minute'+(sale.lastCode.value != 1 ? 's' : '');
	},
	'%s': function ()
	{
		return 'Second'+(sale.lastCode.value != 1 ? 's' : '');
	},
	'%lbl': function ()
	{
		return '';
	}
}
