/**
* @author Kaiser Shahid
* @copyright 2007-2010
* @site www.qaiser.net
*/

// compatibility since this was built for mootools
function $type( obj ) { return typeof obj; }

// for some reason, getTimezoneOffset wasn't returning the negative.
// TODO: verify that getUTCHours() is correct
// TODO: adjust for daylight savings
Date.prototype.getTimezoneOffset = function()
{
	return 330;
	var ltime = this.getHours() * 60 + this.getMinutes();
	var gtime = this.getUTCHours() * 60 + this.getUTCMinutes();
	var delta = ltime - gtime;
	return delta;
}

Date.prototype.getTimezoneGMT= function()
{
	var val = this.getTimezoneOffset() / 60;
	var _s = '-', _p = '';
	
	if ( val >= 0 ) _s = '+';
	else val = 0 - val; // adjust to make it positive now
	if ( val < 10 ) _p = '0'; // leading 0 padding
	
	var _t = val.toString().split( '.' );
	if ( _t.length == 2 )
		val = _s + _p + _t[0] + ( parseFloat( '.' + _t[1] ) * 60 );
	else
		val = _s + _p + _t[0] + '00';
	
	return val;
}

/**
* @param format String Should be similar to strftime() function
* @param timestamp Date [Optional] If not given, use current time.
*/

Date.__format = '%Y-%m-%d %H:%i:%s';
Date.format = function()
{
	var format = '';
	var d = null;
	
	if ( arguments.length > 0 )
		format = arguments[0];
	else
		format = Date.__format;
	
	if ( arguments.length > 1 )
	{
		d = arguments[1];
		if ( $type( d ) == 'number' )
			d = new Date( d );
	}
	else
		d = new Date();
	
	/*if ( !Date.qkopts['format_cache'][format] )
		Date.qkopts['format_cache'][format] = Date.format_compile( format );
	
	var _format = Date.qkopts['format_cache'][format];*/
	
	var _format = format.split( '' );
	var cache = {}, tmp = null, reg = null, val = null;
	var buffer = '';
	
	for ( var i = 0; i < _format.length; i++ )
	{
		token = _format[i];
		
		if ( token == '%' )
		{
			// read-ahead
			token = _format[i+1];
			if ( token == '%' )
			{
				buffer += '%';
				i++;
				continue;
			}
			i++;
		}
		else
		{
			buffer += token;
			continue;
		}
		
		if ( !cache[token] )
		{
			var t = null;
			// omitted placeholders: G, g, E*, O*, Z
			switch ( token )
			{
				case 'A': val = Date.__locale['days'][d.getDay()]; break; // replaced by national representation of the full weekday name
				case 'a': val = Date.__locale['days_abbr'][d.getDay()]; break; // replaced by national representation of the abbreviated weekday name
				case 'B': val = Date.__locale['months'][d.getMonth()]; break; // is replaced by national representation of the full month name
				case 'b': val = Date.__locale['months_abbr'][d.getMonth()]; break; // is replaced by national representation of the abbreviated month name
				case 'C': val = Math.round( d.getFullYear() / 100 ); break; // is replaced by (year / 100) as decimal number; single digits are preceeded by 0
				case 'c': val = Date.format( Date.__locale['datetime'], d ); break; // replaced by national representation of time and date
				case 'D': val = Date.format( '%m/%d/%y', d ); break; // is equivalent to: %m/%d/%y
				case 'd': val = Date.__f_0p( d.getDate() ); break;
				case 'E*': break; // not implemented
				case 'O*': break; // not implemented
				case 'e': val = d.getDate(); break; // replaced by the day of month as a decimal number (1-31); single digits are preceded by a blank
				case 'F': val = Date.format( '%Y-%m-%d', d ); break; // is equivalent to: %Y-%m-%d
				case 'G': break; // is replaced by a year as a decimal number with century.  This year is the one that contains the greater part of the week (Monday as the first day of the week).
				case 'g': break; // is replaced by the same year as in ``%G'', but as a decimal number  without century (00-99).
				case 'H': val = Date.__f_0p( d.getHours() ); break; // is replaced by the hour (24-hour clock) as a decimal number (00-23).
				case 'h': val = Date.__locale['months_abbr'][d.getMonth()]; break; // the same as %b.
				case 'I': // is replaced by the hour (12-hour clock) as a decimal number (01-12).
					val = d.getHours();
					if ( val > 12 ) val = val % 12;
					else if ( val == 0 ) val = 12;
					val = Date.__format_0p( val );
				break;
				//case 'i': val = Date.__f_0p( d.getMinutes() ); break;
				case 'j': val = Date.__f_daysinyear( d ); break; // is replaced by the day of the year as a decimal number (001-366).
				case 'k': val = d.getHours(); break; // is replaced by the hour (24-hour clock) as a decimal number (0-23); single digits are preceded by a blank.
				case 'l': // is replaced by the hour (12-hour clock) as a decimal number (1-12); single digits are preceded by a blank.
					val = d.getHours();
					if ( val > 12 ) val = val % 12;
					else if ( val == 0 ) val = 12;
				break;
				case 'M': val = Date.__f_0p( d.getMinutes() ); break; // is replaced by the minute as a decimal number (00-59).
				case 'm': val = Date.__locale['months_num'][d.getMonth()]; break; // is replaced by the month as a decimal number (01-12).
				case 'n': val = "\n"; break; // is replaced by a newline.
				case 'p': // is replaced by national representation of either "ante meridiem" or "post meridiem" as appropriate.
					var _m = d.getHours();
					if ( _m < 12 ) val = Date.__locale['ampm'][0];
					else val = Date.__locale['ampm'][1];
				break;
				case 'R': val = Date.format( '%H:%M', d ); break; // is equivalent to: %H:%M.
				case 'r': val = Date.format( '%I:%M:%S %p' ); break; // is equivalent to: %I:%M:%S %p.
				case 'S': val = Date.__f_0p( d.getSeconds() ); break; // is replaced by the second as a decimal number (00-60).
				case 's': val = d.getTime(); break; // is replaced by the number of seconds since the Epoch, UTC (see mktime(3)).
				case 'T': val = Date.format( '%H:%M:%S', d ); break; // is equivalent to: %H:%M:%S.
				case 't': val = "	"; break; // is replaced by a tab.
				case 'U': val = Date.__f_weeksinyear( d, 'U' ); break; // is replaced by the week number of the year (Sunday as the first day of the week) as a decimal number (00-53).
				case 'u': val = d.getDay() + 1; break; // is replaced by the weekday (Monday as the first day of the week) as a decimal number (1-7).
				case 'V': val = Date.__f_weeksinyear( d, 'V' ); break; // is replaced by the week number of the year (Monday as the first day of the week) as a decimal number (01-53).  If the week containing January 1 has four or more days in the new year, then it is week 1; otherwise it is the last week of the previous year, and the next week is week 1.
				case 'v': val = Date.format( '%e-%b-%Y', d ); break; // is equivalent to: %e-%b-%Y.
				case 'W': val = Date.__f_weeksinyear( d, 'W' ); break; // is replaced by the week number of the year (Monday as the first day of the week) as a decimal number (00-53).
				case 'w': val = d.getDay(); break; // is replaced by the weekday (Sunday as the first day of the week) as a decimal number (0-6).
				case 'X': val = Date.format( Date.__locale['time'], d ); break; // is replaced by national representation of the time.
				case 'x': val = Date.format( Date.__locale['date'], d ); break; // is replaced by national representation of the date.
				case 'Y': val = d.getFullYear(); break; // is replaced by the year with century as a decimal number.
				case 'y': val = d.getFullYear().toString().substring( 2, 4 ); break; // is replaced by the year without century as a decimal number (00-99).
				case 'Z': val = d.getTimezoneGMT(); break; // is replaced by the time zone name.
				case 'z': val = d.getTimezoneOffset() / 60; break; // is replaced by the time zone offset from UTC; a leading plus sign stands for east of UTC, a minus sign for west of UTC, hours and minutes follow with two digits each and no delimiter between them (common form for RFC 822 date headers).
			}
			
			cache[token] = val;
		}
		
		val = cache[token];
		buffer += val;
	}
	
	return buffer;
};

// TODO: research more locale stuff and have a function to set locale info?
// TODO: be able to load locale dynamically [via ajax or remote scripting]
Date.__locale = {
 	'days': ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']
 	, 'days_abbr': ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
 	, 'daysinmonth': [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
 	, 'months': ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']
	, 'months_abbr': ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
	, 'months_num': ['01','02','03','04','05','06','07','08','09','10','11','12'] // month as a 2-char decimal number
	, 'ampm': ['am', 'pm']
	, 'date': '%Y-%m-%d'
	, 'time': '%H:%M:%S'
	, 'datetime': '%Y-%m-%d %H:%M:%S'
};

// 0-pad single digits
Date.__f_0p = function( num ) { num = num.toString(); if ( num.length == 1 ) num = '0' + num; return num; }

// calculate days in the year [000-366]
Date.__f_daysinyear = function( d )
{
	var _m = d.getMonth();
	var _y = d.getFullYear();
	var _d = d.getDate();
	var tot = 0;
	
	for ( var j = 0; j < _m; j++ )
		tot += Date.__locale['daysinmonth'][j];
	if ( _y % 4 == 0 && _m > 1 ) // leap year
		tot++;
	
	// TODO: pad with 0s if needed (%03d)
	return tot;
}

// TODO: fill in timezone names
// TODO: determine DST
Date.__tzs = {
	'0000': 'GMT'
	, '-0500': 'Eastern'
};

/**
* type is:
* 'U' - week starting on a Sunday, 00-53
* 'V' - week starting on a Monday, 01-53; if four or more days in week starting Jan 1, week is 01, else, 53
* 'W' - week starting on a Monday, 00-53
*/
// TODO: finish this (or at least W)!
Date.__f_weeksinyear = function( d, type )
{
	var n = new Date( d.getTime() );
	// set jan 1.
	return '';
}

// accessible to instances
Date.prototype.format = function()
{
	if ( arguments.length == 0 )
		return Date.format();
	else if ( arguments.length == 1 )
		return Date.format( arguments[0] );
	else
		return Date.format( arguments[0], arguments[1] );
}

//var k = new Date();
//alert( Date.format( '%%A %A %a %B %b | %D | %F | %U %z %Z', k ) );