/* Copyright 2009 Palm, Inc.  All rights reserved. */

//Number of days in each month of a leap year
var leapYearDaysInMonth =    [31,  29,  31,  30,  31,  30,  31,  31,  30,  31,  30,  31];

//Number of days since January 1 at the end of each month of a leap year
var leapYearDaysCumulative = [31,  60,  91, 121, 152, 182, 213, 244, 274, 305, 335, 366];

//Number of days in each month of a regular year
var regularYearDaysInMonth =    [31,  28,  31,  30,  31,  30,  31,  31,  30,  31,  30,  31];

//Number of days since January 1 at the end of each month of a regular year
var regularYearDaysCumulative = [31,  59,  90, 120, 151, 181, 212, 243, 273, 304, 334, 365];

//Number of days until December 31 at the start of each month of a leap year
var leapYearDaysCumulativeReverse =    [366, 335, 306, 275, 245, 214, 184, 153, 122,  92,  61,  31];

//Number of days until December 31 at the start of each month of a regular year
var regularYearDaysCumulativeReverse = [365, 334, 306, 275, 245, 214, 184, 153, 122,  92,  61,  31];

//Returns true if the year is a leap year
function isLeapYear(year) {
	if(year % 4)
		return false;
	if(year % 400 == 0)
		return true;
	if(year % 100 == 0)
		return false;
	return true;	
}

//finds # of days between January 1 and the given date, in the year of the given date.
//Timezone agnostic
function howManyDaysSinceJanuaryFirst(date) {
	var howManyDays = 0;
	var day = date.getDate();
	var year = date.getFullYear();
	var previousMonth = date.getMonth() - 1;

	//add the days for all previous months
	if(previousMonth >= 0) {
		if(isLeapYear(year))
			howManyDays = leapYearDaysCumulative[previousMonth];
		else	
			howManyDays = regularYearDaysCumulative[previousMonth];
	}
	
	//add the days for the current month
	howManyDays += (day-1);	
	
	return howManyDays;
}

//finds # of days between the given date and December 31, in the year of the given date.
//Timezone agnostic
function howManyDaysTillDecember31(date) {
	var howManyDays = 0;
	var day = date.getDate();
	var year = date.getFullYear();
	var month = date.getMonth();
	var nextMonth = date.getMonth()+1;
	var daysInMonthArray = regularYearDaysInMonth;

	//add the days for all months after the current month
	if(nextMonth < 12) {
		if(isLeapYear(year)){
			daysInMonthArray = leapYearDaysInMonth;
			howManyDays = leapYearDaysCumulativeReverse[nextMonth];
		}
		else {
			howManyDays = regularYearDaysCumulativeReverse[nextMonth];	
		}
	}
	
	//add the days left in the current month
	howManyDays += (daysInMonthArray[month] - day);	
	
	return howManyDays;
}

//finds # of days between two given dates.
//Require: dates are within the same year
//Require: startRange < endRange
//Timezone agnostic		
function howManyDaysBetweenShortRange(startDate, endDate) {
	var howManyDays = 0;
	var daysInMonthArray = regularYearDaysInMonth;
	var cumulativeArray = regularYearDaysCumulative;
	var startMonth = startDate.getMonth();
	var startDay = startDate.getDate();
	var year = startDate.getFullYear();

	var endMonth = endDate.getMonth();
	var endDay = endDate.getDate();
	var monthDiff = endMonth - startMonth;
	
	//if we are within the same month
	if(monthDiff == 0) {
		return (endDay - startDay);
	} else {
		if(isLeapYear(year)) {
			daysInMonthArray = leapYearDaysInMonth;
			cumulativeArray = leapYearDaysCumulative;
		}
	
		//add days left in the start month
		howManyDays += (daysInMonthArray[startMonth] - startDay);	
		
		//add days past in the end month
		howManyDays += endDay;                                     
		
		//add days in months in between
		if(monthDiff > 1)      
			howManyDays += (cumulativeArray[endMonth-1] - cumulativeArray[startMonth]);
	}
	
	return howManyDays;
}

//finds # of days between two given dates.
//Require: startRange < endRange
//Timezone agnostic
function howManyDaysBetween(startDate, endDate){
	var howManyDays = 0;
	
	var startYear = startDate.getFullYear();
	var endYear = endDate.getFullYear();
	if(startYear == endYear){
		howManyDays = howManyDaysBetweenShortRange(startDate, endDate);
	} else {
		howManyDays += howManyDaysTillDecember31(startDate);
		howManyDays += howManyDaysSinceJanuaryFirst(endDate) +1;
		
		for(var year = startYear+1; year < endYear; year++){
			if(isLeapYear(year))
				howManyDays += 366;
			else
				howManyDays += 365;
		}
	}
	return howManyDays;
}

//finds # of whole months between two given dates.
//Require: startDate < endDate
//Timezone agnostic
function howManyMonthsBetween(startDate, endDate){
	var howManyMonths = 0;
	
	var startMonth = startDate.getMonth();  
	var startYear = startDate.getFullYear();

	var endMonth = endDate.getMonth();  
	var endYear = endDate.getFullYear();

	if(startYear == endYear) {
		return endMonth - startMonth;
	} else {
		howManyMonths += (12 - startMonth);  
		howManyMonths += endMonth;          
		howManyMonths += (endYear - startYear - 1) * 12;  
	}
	return howManyMonths;
}

//Dissects the period specified by startDate and endDate into the 
//number of years, months, days, hours, and minutes between the two.
//Returns an object formatted: {years, months, days, hours, minutes}.
function getTimePeriodParts(startDate, endDate, isAllDay){
	
	var period = {years: 0, months: 0, days: 0, hours: 0, minutes: 0};	
		
	var monthsBetween = howManyMonthsBetween(startDate, endDate);
	if(startDate.getDate() > endDate.getDate())
		monthsBetween--;
	period.years = Math.floor(monthsBetween / 12);
	period.months = monthsBetween % 12;
	
	if (endDate.getDate() >= startDate.getDate()) 
		period.days = endDate.getDate() - startDate.getDate();
	else {
		var jumpDate = startDate.getDate();
		var jumpMonth = endDate.getMonth() -1;
		var jumpYear = endDate.getFullYear();
		if (jumpMonth < 0) {
			jumpMonth = 11;
			jumpYear--;
		}		
		var tempDate = new Date(jumpYear, jumpMonth, jumpDate);
		period.days = howManyDaysBetween(tempDate, endDate);
	}
	
	if (endDate.getHours() >= startDate.getHours()) 
		period.hours = endDate.getHours() - startDate.getHours();
	else {
		period.hours = endDate.getHours() + 24 - startDate.getHours();
		period.days--;
	}
		
	if (endDate.getMinutes() >= startDate.getMinutes()) 
		period.minutes = endDate.getMinutes() - startDate.getMinutes();
	else {
		period.minutes = endDate.getMinutes() + 60 - startDate.getMinutes();
		period.hours--;
	} 	
	
	if(isAllDay){
		period.days++;
		period.hours = 0;
		period.minutes = 0;	
	}
			
	return period;
}

//Dissects the alarmString into the number of weeks, days, hours, minutes, and seconds.
//It also determines if the alarm was positive or negative.
//param: alarmString a string that specifies an RFC5545 duration. Example: -PT5M (5 minutes before).
//Returns an object formatted: {weeks, days, hours, minutes, seconds, negative}.
function getAlarmPartsFromString(alarmString){
	
	var duration = {weeks: 0, days: 0, hours: 0, minutes: 0, seconds: 0, negative: (alarmString[0] == "-")};
		
	//regexp matches one or more digits followed by the letters W|D|H|M|S.
	//This regexp will allow duration strings that do not follow the duration definition - 
	//for example, it does not disallow other components if the week component is present.
	//There's probably a way to write this regexp to enforce the standard more strictly.
	//There's also probably a way to write this so that you don't have to call it in a loop.
	//If your regexp kung-fu is strong, feel free to try to improve this.
	 
	var regexp = new RegExp(/(\d+)([WDHMS])/g);
	var tokens = regexp.exec(alarmString); 
	//tokens[0]: whole parts "5M"
	//tokens[1]: numeric part "5"
	//tokens[2]: character part "M"
	//if tokens.length != 3, the duration string is malformed
	
	while (tokens != null && tokens.length == 3) {
		switch(tokens[2]){
			case "W":
				duration.weeks = parseInt(tokens[1]);
				break;
			case "D":
				duration.days = parseInt(tokens[1]);
				break;
			case "H":
				duration.hours = parseInt(tokens[1]);
				break;
			case "M":
				duration.minutes = parseInt(tokens[1]);
				break;
			case "S":
				duration.seconds = parseInt(tokens[1]);
				break;					
		};
		tokens = regexp.exec(alarmString);	
	}
		
	return duration;
}

