//
// Library for handling user<-->system time format conversions.
//
//
// See the UTC time format definition at http://www.w3.org/TR/NOTE-datetime
//

// time formats
//
// 0 - h:mm tt
// 1 - hh:mm tt
// 2 - H:mm
// 3 - HH:mm
// 4 - h:mm:ss tt
// 5 - hh:mm:ss tt
// 6 - H:mm:ss
// 7 - HH:mm:ss

// Localizable part of time
var sAM = "AM";
var sPM = "PM";

// Converts a string in one of the supported time formats to a date object.
// sTime - the time in string format
// iFormat - the time format the time is in.
// Returns - a Date() object with the given time.  The date portion should be ignored.
function parseTime( sTime, iFormat )
{
	// convert time string to lowercase so we can test for am/pm accurately.
	var sTime = sTime.toLowerCase( );
	var rValidTimeElement = /^[0-9]{1,2}$/
	
	var iHour;
	var iMinute;
	var iSecond;

	var iHourDiv = sTime.indexOf( ":" );
	var sHour = sTime.substring( 0, iHourDiv );

	if( ! sHour.match( rValidTimeElement ) )
	{
		// Invalid time!
		return new Date( NaN );
	}
	
	var iEndOfDigits;
	if( iFormat == 4 ||
	    iFormat == 5 ||
	    iFormat == 6 ||
	    iFormat == 7 )
	{
		// Seconds
		
		var iMinDiv = sTime.indexOf( ":", iHourDiv+1 );
		var sMinute = sTime.substring( iHourDiv+1, iMinDiv );
		
		if( ! sMinute.match( rValidTimeElement ) )
		{
			// Invalid time!
			return new Date( NaN );
		}
		
		iMinute = parseInt( sMinute, 10 );

		var sSecond;
		if( iFormat == 4 ||
			iFormat == 5 )
		{
			// AM/PM is on the end, make sure to trim that off
			var iSecDiv = sTime.indexOf( sAM.toLowerCase( ), iMinDiv+1 );
			if( iSecDiv == -1 )
			{
				iSecDiv = sTime.indexOf( sPM.toLowerCase( ), iMinDiv+1 );
			}
			
			sSecond = sTime.substring( iMinDiv+1, iSecDiv );
		}
		else
		{
			// no AM/PM, just take the rest of the string.
			sSecond = sTime.substring( iMinDiv+1, sTime.length );
		}
		
		// Remove any blanks
		sSecond = sSecond.replace( / /, "" );
		if( ! sSecond.match( rValidTimeElement ) )
		{
			// Invalid time!
			return new Date( NaN );
		}
		
		iSecond = parseInt( sSecond, 10 );
		iEndOfDigits = iSecDiv + sSecond.length + 1;
	}
	else
	{
		// No seconds;
		var sMinute;
		if( iFormat == 2 ||
			iFormat == 3 )
		{
			// Minutes terminated by end of string
			sMinute = sTime.substring( iHourDiv+1, sTime.length );
		}
		else
		{
			// Minutes terminated by AM/PM
			var iMinDiv = sTime.indexOf( sAM.toLowerCase( ), iHourDiv+1 );
			if( iMinDiv == -1 )
			{
				iMinDiv = sTime.indexOf( sPM.toLowerCase( ), iHourDiv+1 );
			}
			sMinute = sTime.substring( iHourDiv+1, iMinDiv );
		}
		
		// Remove any blanks
		sMinute = sMinute.replace( / /, "" );
		if( ! sMinute.match( rValidTimeElement ) )
		{
			// Invalid time!
			return new Date( NaN );
		}

		iMinute = parseInt( sMinute, 10 );

		iSecond = 0;
		iEndOfDigits = iMinDiv + sMinute.length + 1;
	}
	
	if( iFormat == 0 ||
	    iFormat == 1 ||
	    iFormat == 4 ||
	    iFormat == 5 )
	{
		// 12 hour time with AM/PM at the end.
		iHour = parseInt( sHour, 10 );

		// Check specific to 12 hour times; we want to further constrain
		// the time from 0..23 to 1..12 hours for these formats.		
		if( iHour <= 0 || iHour > 12 )
		{
			return new Date( NaN );
		}
		
		// 12:30 AM --> 0:30 UTC
		if( iHour == 12 )
		{
			iHour = 0;
		}
		
		// check for AM/PM
		if( -1 < sTime.indexOf(sPM.toLowerCase()) )
		{	
			// Here we have an hour between 0 and 11; if there was a 'PM' tag, move the
			// clock forward 12 hours to be 12-23 hours.
			iHour += 12;
		}
		else if( -1 < sTime.indexOf(sAM.toLowerCase()) )
		{
			// Do Nothing.
		}
		else
		{
			// Invalid string!
			return new Date( NaN );
		}
	}
	else
	{
		// 24 hour time 
		iHour = parseInt( sHour, 10 );
	}
	
	if( iHour > 23 || iHour < 0 || 
	    iMinute > 59 || iMinute < 0 ||
	    iSecond > 59 || iSecond < 0 )
	{
		// The user has entered an invalid time.
		return new Date( NaN );
	}
	
	// Reparse the string to take advantage of the Date objects built in error detection features
	// If we don't do the above work, though, we cannot gaurentee that the physical format is correct;
	// the date object will accept a time like "12:022:24", which is not actually valid.
	return new Date( 2000, 0, 1, iHour, iMinute, iSecond, 0 );
}

// Converts the time portion of a UTC time to a date object.  This function
// ignores the offset if it is present.
// sTime - the time in UTC format.  This is the piece of the string following the 'T'.
// Returns - a Date() object with the given time.
function parseUTCTime( sTime )
{
	
	// If there is a UTC offset, ignore it.
	// This may be 00:00:00Z or 00:00:00-1:00
	var ss = sTime.split( '-' );
	var s = ss[ 0 ];
	
	// Split again on '+' to cover positive UTC offsets.
	ss = s.split( '+' );
	s = ss[ 0 ];
	
	// split again on '.' to remove any miliseconds which may be present.
	ss = s.split( '.' );
	var s = ss[ 0 ];
		
	// Remove the 'Z' if present.
	s = s.replace( /Z/, "" );
	
	var oDate = new Date( "1/1/00 " + s );
	
	// If there were miliseconds, we should add them back in at this point.
	if( ss.length == 2 )
	{
		sMilliseconds = ss[ 1 ];
		oDate.setMilliseconds( parseInt( sMilliseconds ) );
	}
	
	return oDate;
}

// Converts a Date() object to a time string using the given format.
// oTime - a Date() object.  The date portion is ignored.
// iFormat - the date format to use.
// Returns - the date as a string in the requested format.
function timeToString( oTime, iFormat )
{
	switch( iFormat )
	{
		case 0:
		{
			return get12HourClockHours( oTime ) + ":" + makeTwoDigitString( oTime.getMinutes() ) + " " + makeAMPM( oTime );
		}
		
		case 1:
		{
			return makeTwoDigitString( get12HourClockHours( oTime ) ) + ":" + makeTwoDigitString( oTime.getMinutes() ) + " " + makeAMPM( oTime );
		}
		
		case 2:
		{
			return oTime.getHours() + ":" + makeTwoDigitString( oTime.getMinutes() );
		}
		
		case 3:
		{
			return makeTwoDigitString( oTime.getHours() ) + ":" + makeTwoDigitString( oTime.getMinutes() );
		}
		
		case 4:
		{
			return get12HourClockHours( oTime ) + ":" + makeTwoDigitString( oTime.getMinutes() ) + ":" + makeTwoDigitString( oTime.getSeconds() ) + " " + makeAMPM( oTime );
		}
		
		case 5:
		{
			return makeTwoDigitString( get12HourClockHours( oTime ) ) + ":" + makeTwoDigitString( oTime.getMinutes() ) + ":" + makeTwoDigitString( oTime.getSeconds( ) ) + " " + makeAMPM( oTime );
		}
		
		case 6:
		{
			return oTime.getHours( ) + ":" + makeTwoDigitString( oTime.getMinutes() ) + ":" + makeTwoDigitString( oTime.getSeconds( ) );
		}
		
		case 7:
		{
			return makeTwoDigitString( oTime.getHours( ) ) + ":" + makeTwoDigitString( oTime.getMinutes() ) + ":" + makeTwoDigitString( oTime.getSeconds( ) );
		}
	}
}

// Converts a Date() object to a time string using the UTC format.
// oTime - a Date() object.  The date portion is ignored.
// Returns - a string or null.
function timeToUTCString( oTime )
{
	if (isNaN(oTime))
	{
		return null;
	}
	
	var sRVal = makeTwoDigitString( oTime.getHours() ) + ":" 
	          + makeTwoDigitString( oTime.getMinutes() ) 
	          + ":" + makeTwoDigitString( oTime.getSeconds() );
	          
	// If there are milliseconds, append them.  Otherwise don't
	// Commented out since the platform doesn't support milliseconds
	/*if( oTime.getMilliseconds( ) > 0 )
	{
		sRVal += "." + oTime.getMilliseconds( );  // Milliseconds can be 0..n digits
	}*/
	
	return sRVal;
}

// Takes a number that is 1 or 2 digits and outputs a string that is 2 digits long.
// iNumber = a number between 0 and 99.  Other numbers, the results are undefined.
// Returns - a two-digit string.
function makeTwoDigitString( iNumber )
{
	if( iNumber > 9 )
	{
		return iNumber.toString( );
	}
	else
	{
		return "0" + iNumber.toString( );
	}
}

// Returns hours 1..12 instead of 0..23
// oTime - time to get hours from
// Returns an hour string 1..12
function get12HourClockHours( oTime )
{
	if( oTime.getHours() > 12 )
	{
		return oTime.getHours() - 12;
	}
	else if( oTime.getHours() == 0 )
	{
		return 12;
	}
	else 
	{
		return oTime.getHours();
	}
}

// Returns 'AM' or 'PM' depending on the time
// oTime - the time to evaluate for AM/PM
// returns - 'AM' or 'PM'
function makeAMPM( oTime )
{
	if( oTime.getHours() >= 12 )
	{
		return sPM;
	}
	else
	{
		return sAM;
	}
}


//	format float, kill > a 10th
function formatFloat(sNum) 
{
	var sBase = "";
	var sRem = "";
	var bRem = false;;
				
	for(i = 0; i < sNum.length; i++) 
	{
				
		curChar = sNum.charAt(i);

		if(bRem) 
		{
			sRem += curChar;
			if(sRem.length == 2) break;
		} 
		else 
		{
			sBase += curChar;
		}

		if(curChar == '.') 
		{ 
			bRem = true;
			continue;
		}
	}

	return sBase + sRem;
}

// Converts input value to a user-friendly time string and returns a string with the updated duration.
// If this logic changes, the logic in duration.htc must also change this was copied from there 
function formatDuration( iMinutes ) 
{
	if( isNaN( parseInt(iMinutes, 10) ) || ( iMinutes < 0 ) ) 
	{
		iMinutes = 0;
	}

	var rVal;
	if(iMinutes < 60) 
	{
		if(iMinutes == 1) 
		{
			rVal = ""+iMinutes +" minute";
		} 
		else 
		{
			rVal = ""+iMinutes+" minutes";
		}
	} else if( iMinutes >= 60 && iMinutes < 1440 ) 
	{
		var iHours = iMinutes / 60;
			
		if(iHours == 1) 
		{
			rVal = ""+formatFloat( iHours.toString() )+" hour";
		} 
		else 
		{
			rVal = ""+formatFloat( iHours.toString() )+" hours";
		}
	} else if( iMinutes >= 1440 ) 
	{
		var iHours = iMinutes / 60;
		var iDays = iHours / 24;
			
		if(iDays == 1) 
		{
			rVal = ""+formatFloat( iDays.toString() )+" day";
		} 
		else 
		{
			rVal = ""+formatFloat( iDays.toString() )+" days";
		}
	}
	
	return rVal;
}
