var YARDS_IN_MILE = 1760;
var YARDS_IN_METRE = 1.0989010989;
var YARDS_IN_KILOMETRE = 1098.9010989;
var SECS_IN_HOUR = 3600;
var SECS_IN_MINUTE = 60;
var MSECS_IN_SECOND = 1000;

var STANDARD_ACCELERATION = 6.5;  // yards per second per second
var STANDARD_RETARDATION = -8.75; // yards per second per second

/*
 * Hides the value shown in the result field.
 */
function HideValue()
{
	document.calculator.timelost.value = "--:--:--";
}

/*
 * Defines information required for an example
 */
function Example( optimumSpeed, restrictedSpeed, duration, durationUnits, includeChangeOfSpeed )
{
	this.optimumSpeed = optimumSpeed;
	this.restrictedSpeed = restrictedSpeed;
	this.duration = duration;
	this.durationUnits = durationUnits;
	this.includeChangeOfSpeed = includeChangeOfSpeed;
}

/*
 * An array of examples
 */
var example = new Array();
example[0] = new Example( 40, 25, 200, 0, true );
example[1] = new Example( 70, 50,   5, 2, true );
example[2] = new Example( 70, 4,  300, 0, true );
example[3] = new Example( 70, 20,  10, 5, true );

/*
 * Fill out the calulate with example data and auto-calculate the value.
 */
function show_example( exampleNumber )
{
	var theExample = example[exampleNumber];

	// set-up form values
	var calcForm = document.calculator;
	calcForm.optspeed.value = theExample.optimumSpeed;
	calcForm.optspeedunits.selectedIndex = 0;
	calcForm.restspeed.value = theExample.restrictedSpeed;
	calcForm.restspeedunits.selectedIndex = 0;
	calcForm.duration.value = theExample.duration;
	calcForm.durationunits.selectedIndex = theExample.durationUnits;
	calcForm.includecos.checked = theExample.includeChangeOfSpeed;

	Calculate();
}

/*
 * Pulls the input data from the form and calls the applicable calulcate method.
 */
function Calculate()
{
	var calcForm = document.calculator;


	// parse values
	var optimumSpeed = parseFloat( calcForm.optspeed.value );
	var restrictedSpeed = parseFloat( calcForm.restspeed.value );
	var duration = parseFloat( calcForm.duration.value );

	// check if values entered are valid
	if (   (isNaN( optimumSpeed ))
			|| (optimumSpeed <= 0)
			|| (isNaN( restrictedSpeed ))
			|| (restrictedSpeed <= 0)
			|| (restrictedSpeed > optimumSpeed)
			|| (isNaN( duration ))
			|| (duration <= 0)  )
	{
		calcForm.timelost.value = "--:--:--";
		return;
	}


	// convert speeds to m.p.h.
	optimumSpeed = ConvertSpeedToMPH( optimumSpeed, calcForm.optspeedunits.selectedIndex );
	restrictedSpeed = ConvertSpeedToMPH( restrictedSpeed, calcForm.restspeedunits.selectedIndex );


	var calculatedDelay;
	var durationUnits = calcForm.durationunits.selectedIndex;
	if (durationUnits < 4)
	// duration specified in distance
	{
		var distance;
		if (durationUnits == 0)	// yards
			distance = duration;
		if (durationUnits == 1)	// metres
			distance = duration * YARDS_IN_METRE;
		if (durationUnits == 2)	// miles
			distance = duration * YARDS_IN_MILE;
		if (durationUnits == 3)	// kilometres
			distance = duration * YARDS_IN_KILOMETRE;

		calculatedDelay  = CalculateDelayFromDistance( optimumSpeed, restrictedSpeed, distance );
	}
	else
	// duration specified in time
	{
		var time;
		if (durationUnits == 4)	// seconds
			time = duration;
		if (durationUnits == 5)	// minutes
			time = duration * SECS_IN_MINUTE;

		calculatedDelay  = CalculateDelayFromDuration( optimumSpeed, restrictedSpeed, time );
	}

	if (calcForm.includecos.checked)
		calculatedDelay += CalculateDelayDuringChangeOfSpeed( optimumSpeed, restrictedSpeed );


	// convert time in seconds to time past midnight 1970
	// (NB: add 0.5 so that when the time is truncated to seconds, it is rounded to nearest whole second)
	var timeData = new Date( (calculatedDelay + 0.5) * MSECS_IN_SECOND );

	var timeHrs = timeData.getHours();
	var timeMins = timeData.getMinutes();
	var timeSecs = timeData.getSeconds();
	calcForm.timelost.value = PaddedValue( timeHrs, '0', 2 )
													 + ":"
													 + PaddedValue( timeMins, '0', 2 )
													 + ":"
													 + PaddedValue( timeSecs, '0', 2 );

	// show the how-much-later form
	show_tool_form();
}

/*
 * Calculates delay during changing speed from the optimum speed to the restricted speed and back again.
 *
 *	optimumSpeed    : number		optimum speed in miles per hour
 *	restrictedSpeed : number		actual speed in miles per hour
 */
function CalculateDelayDuringChangeOfSpeed( optimumSpeed, restrictedSpeed )
{
	// convert mph to yards per second
	var optimumSpeedYps    = (optimumSpeed    * YARDS_IN_MILE) / SECS_IN_HOUR;
	var restrictedSpeedYps = (restrictedSpeed * YARDS_IN_MILE) / SECS_IN_HOUR;

	// compute how long it takes to slow down and speed up (at a constant retardation/acceleration)
	var timeSpentRetarding = (restrictedSpeed - optimumSpeed) / STANDARD_RETARDATION;
	var timeSpentAccelerating = (optimumSpeed - restrictedSpeed) / STANDARD_ACCELERATION;

	// compute time lost while retarding
	var distanceTravelledDuringRetardation = (optimumSpeed * timeSpentRetarding) + ((STANDARD_RETARDATION * timeSpentRetarding * timeSpentRetarding) / 2);
	var timeToTravelRetardDistAtOptimumSpeed = distanceTravelledDuringRetardation / optimumSpeed;
	var timeLostWhileRetarding = timeSpentRetarding - timeToTravelRetardDistAtOptimumSpeed;

	// compute time lost while accelerating
	var distanceTravelledDuringAcceleration = (restrictedSpeed * timeSpentAccelerating) + ((STANDARD_ACCELERATION * timeSpentAccelerating * timeSpentAccelerating) / 2);
	var timeToTravelAccelerationDistAtOptimumSpeed = distanceTravelledDuringAcceleration / optimumSpeed;
	var timeLostWhileAccelerating = timeSpentAccelerating - timeToTravelAccelerationDistAtOptimumSpeed;

//alert( "Slow time: " + timeSpentRetarding + "s\n" +
//       "Slow dist: " + distanceTravelledDuringRetardation + "yd\n" +
//       "Slow opt time: " + timeToTravelRetardDistAtOptimumSpeed + "s\n" +
//       "Slow time lost: " + timeLostWhileRetarding + "s\n" +
//       "Speed time: " + timeSpentAccelerating + "s\n" +
//       "Speed dist: " + distanceTravelledDuringAcceleration + "yd\n" +
//       "Speed opt time: " + timeToTravelAccelerationDistAtOptimumSpeed + "s\n" +
//       "Speed time lost: " + timeLostWhileAccelerating + "s" );

	return (timeLostWhileRetarding + timeLostWhileAccelerating);
}

/*
 * Calculates delay from the duration spent at the restricted speed.
 *
 *	optimumSpeed    : number		optimum speed in miles per hour
 *	restrictedSpeed : number		actual speed in miles per hour
 *	timeTaken       : number		time spent travelling at the restricted speed, in seconds
 */
function CalculateDelayFromDuration( optimumSpeed, restrictedSpeed, timeTaken )
{
	// convert mph to yards per second
	var optimumSpeedYps    = (optimumSpeed    * YARDS_IN_MILE) / SECS_IN_HOUR;
	var restrictedSpeedYps = (restrictedSpeed * YARDS_IN_MILE) / SECS_IN_HOUR;

	// compute distance travelled in this duration
	var distanceCovered = restrictedSpeedYps * timeTaken;

	// compute time it would have taken to cover this distance at the optimum speed
	var timeItShouldHaveTaken = distanceCovered / optimumSpeedYps;

	// the delay is the difference between the two
	return timeTaken - timeItShouldHaveTaken;
}

/*
 * Calculates delay from the distance travelled at the restricted speed.
 *
 *	optimumSpeed      : number		optimum speed in miles per hour
 *	restrictedSpeed   : number		actual speed in miles per hour
 *	distanceTravelled : number		distance travelled at the restricted speed, in yards
 */
function CalculateDelayFromDistance( optimumSpeed, restrictedSpeed, distanceTravelled )
{
	// convert mph to yards per second
	var optimumSpeedYps    = (optimumSpeed    * YARDS_IN_MILE) / SECS_IN_HOUR;
	var restrictedSpeedYps = (restrictedSpeed * YARDS_IN_MILE) / SECS_IN_HOUR;

	// compute time it took to cover this distance
	var timeItTook = distanceTravelled / restrictedSpeedYps;

	// compute time it took to cover this distance
	var timeItShouldHaveTaken = distanceTravelled / optimumSpeedYps;

	// the delay is the difference between the two
	return timeItTook - timeItShouldHaveTaken;
}

/*
 * Calculates speed in m.p.h.
 */
function ConvertSpeedToMPH( speed, units )
{
	if (units == 0)	// MPH
		return speed;
	if (units == 1)	// KPH
		return speed * 0.62;
}

/*
 * Pads a number to a specfic width.
 */
function PaddedValue( value, padChar, width )
{
	var valueStr = "" + value;
	while (valueStr.length < width)
		valueStr = padChar + valueStr;

	return valueStr;
}

/*
 * Scrolls the window so that the tool is visible
 */
function show_tool_form()
{
	// scroll to the top of the page
	window.scrollTo( 0, 0 );
}


/*
 * Implements the "send to a friend" feature
 */
var OPTIMUM_SPEED_MAGNITUDE_NAME = "optspeed";
var OPTIMUM_SPEED_UNITS_NAME = "optspeedunits";
var RESTRICTED_SPEED_MAGNITUDE_NAME = "restspeed";
var RESTRICTED_SPEED_UNITS_NAME = "restspeedunits";
var DURATION_MAGNITUDE_NAME = "duration";
var DURATION_UNITS_NAME = "durationunits";
var INCLUDE_CHANGE_OF_SPEED_NAME = "includecos";

function tell_a_friend()
{
	var urlStr = window.location.href;

	// remove any parameters attached to old URL
	var pageURLIndex = urlStr.indexOf( '?' );
	if (pageURLIndex > -1)
		urlStr = urlStr.substring( 0, pageURLIndex );

	// construct URL containing data from form
	urlStr += "?" + OPTIMUM_SPEED_MAGNITUDE_NAME + "=" + document.calculator.optspeed.value;
	urlStr += "&" + OPTIMUM_SPEED_UNITS_NAME + "=" + document.calculator.optspeedunits.selectedIndex;
	urlStr += "&" + RESTRICTED_SPEED_MAGNITUDE_NAME + "=" + document.calculator.restspeed.value;
	urlStr += "&" + RESTRICTED_SPEED_UNITS_NAME + "=" + document.calculator.restspeedunits.selectedIndex;
	urlStr += "&" + DURATION_MAGNITUDE_NAME + "=" + document.calculator.duration.value;
	urlStr += "&" + DURATION_UNITS_NAME + "=" + document.calculator.durationunits.selectedIndex;
	urlStr += "&" + INCLUDE_CHANGE_OF_SPEED_NAME + "=" + document.calculator.includecos.checked;

	var msgWin = window.open( "", "", "width=600,height=190,menubar=no,location=no,resizable=yes,status=no,titlebar=no,toolbar=no" );
	msgWin.document.write( "<html><head><title>How Much Later? Calculator - URL</title></head>" );
	msgWin.document.write( "<body>" );
	msgWin.document.write( "The URL to view this calculation is:" );
	msgWin.document.write( "<p><a href='" + urlStr + "' target='_blank'><font size=2><code>" + urlStr + "</code></font></a>" );
	msgWin.document.write( "<form><br><table width=100% border=0 cellpadding=0 cellspacing=0><td>By entering the URL into a web-browsers' location textbox, you will be taken straight to the result of the calculation.<br>You can copy, save or send this URL to a friend.</td><td width=60></td><td align=right valign=bottom><input type='button' value='close' onclick='window.close();'></td></table></form>" );
	msgWin.document.write( "</body></html>" );
	msgWin.document.close();
}

function handle_auto_calculate()
{
	var allDataSet = true;
	var urlParser = new URLParser();
	var urlValue;

	// set optimum speed value, if one is specified
	urlValue = urlParser.get_string_parameter( OPTIMUM_SPEED_MAGNITUDE_NAME );
	if (urlValue != null)
		document.calculator.optspeed.value = urlValue;
	else
		allDataSet = false;

	// set optimum speed units, if one is specified
	urlValue = urlParser.get_integer_parameter( OPTIMUM_SPEED_UNITS_NAME );
	if (urlValue != null)
		document.calculator.optspeedunits.selectedIndex = urlValue;
	else
		allDataSet = false;

	// set restricted speed value, if one is specified
	urlValue = urlParser.get_string_parameter( RESTRICTED_SPEED_MAGNITUDE_NAME );
	if (urlValue != null)
		document.calculator.restspeed.value = urlValue;
	else
		allDataSet = false;

	// set restricted speed units, if one is specified
	urlValue = urlParser.get_integer_parameter( RESTRICTED_SPEED_UNITS_NAME );
	if (urlValue != null)
		document.calculator.restspeedunits.selectedIndex = urlValue;
	else
		allDataSet = false;

	// set duration value, if one is specified
	urlValue = urlParser.get_string_parameter( DURATION_MAGNITUDE_NAME );
	if (urlValue != null)
		document.calculator.duration.value = urlValue;
	else
		allDataSet = false;

	// set duration units, if one is specified
	urlValue = urlParser.get_integer_parameter( DURATION_UNITS_NAME );
	if (urlValue != null)
		document.calculator.durationunits.selectedIndex = urlValue;
	else
		allDataSet = false;

	// set include change of speed flag, if one is specified
	urlValue = urlParser.get_boolean_parameter( INCLUDE_CHANGE_OF_SPEED_NAME );
	if (urlValue != null)
		document.calculator.includecos.checked = urlValue;
	else
		allDataSet = false;

	if (allDataSet)
		Calculate();
}

window.onload = handle_auto_calculate;