/**
	@file This file contains the constants, variables, and functions 
	needed to perform pan/zooms between pre-determined locations/points.

	Depending on the zoom level of the map when a pan/zoom is started, the
	map will need to pan at different intervals of latitude and longitude.

	For zoom levels of 0, 1, or 2, no pan interval is needed as all
	markers are reachable with Google's map.panTo() function.  For all
	other zoom levels, an incremental distance must be specified, else the
	map will no pan, but will jump to the new location instead.  The
	increments are specified by the constants PAN_FACTOR_X, where X is the
	zoom level of the map.

	If the calculated number of incremental pans is greater than MAX_MOVES,
	the map is zoomed to PAN_ZOOM.  The map then pans to the new location
	and zooms in to the zoom level before the pan/zoom started.

	USES:
		/maps/scripts/globals.js
		/maps/scripts/utility.js
		/maps/scripts/markers.js
		/maps/scripts/regions.js

	USED BY:
		/maps/scripts/markers.js

	INCLUDED BY:
		/maps.php
 */

/*--------------------------------------------------------------------------*/
// CONSTANTS /* {{{ */
var MAX_MOVES		= 20;	// maximum number of incremental pans
var PAN_ZOOM		= 4;	// maximum zoom level during a pan/zoom
var NONE			= -1;	// integer constant for no pan factor
var PAN_FACTOR_3	= 45;				
var PAN_FACTOR_4	= 10;
var PAN_FACTOR_5	= 5;
var PAN_FACTOR_6	= 2;
var PAN_FACTOR_7	= 0.75;
var PAN_FACTOR_8	= 0.25;
var PAN_FACTOR_9	= 0.20;
var PAN_FACTOR_10	= 0.10;
var PAN_FACTOR_11	= 0.05;
var PAN_FACTOR_12	= 0.025;
var PAN_FACTOR_13	= 0.0125;
var PAN_FACTOR_14	= 0.00625;
var PAN_FACTOR_15	= 0.003125;
var PAN_FACTOR_16	= 0.0015625;
var PAN_FACTOR_17	= 0.00078125;
var PAN_FACTOR_18	= 0.000390625;
var PAN_FACTOR_19	= 0.0001953125;
var PAN_FACTOR_20	= 0.00009765625;

/* }}} */

/*--------------------------------------------------------------------------*/
/**
	@brief This function will position the map center at the given point.
	@param new_point GLatLng point to center the map at.

	@note This function will pan/zoom to the given point.
	@note This function makes use of setTimeout().
 */
function ShowLocation(new_point) { /* {{{ */

	// if no point given, alert user and return
	if (!new_point) {
		alert("No location given.");
		return;
	}

	// retrieve the current center of map
	var curr_point = g_map.getCenter();

	// check that new point is not current point
	if (!curr_point.equals(new_point)) {

		// retrieve the incremental pan distance
		var pan_factor = GetPanFactor(g_map.getZoom());

		// we are performing a pan/zoom
		g_state.is_pan_zoom = true;

		// if no pan factor, then pan to new point using Google Map API
		if (pan_factor == NONE) {

			g_map.panTo(new_point);
			g_state.is_pan_zoom = false; // pan/zoom has ended

		}
		// ... we have a pan factor, now we need to see if we should pan
		else {

			// retrieve current visible map boundaries
			var bounds = g_map.getBounds(); // returns a GBounds object

			// check if point is on visible portion of map
			if (bounds.contains(new_point)) {

				// if so, pan to it
				g_map.panTo(new_point);
				g_state.is_pan_zoom = false; // pan/zoom has ended
			}
			// ... else we need to pan incrementally
			else {

				// store the current zoom level
				g_state.orig_zoom = g_state.curr_zoom;

				// retrieve coordinates from current map center
				var x1 = parseFloat(curr_point.lng());
				var y1 = parseFloat(curr_point.lat());

				// retrieve coordinates from new point
				var x2 = parseFloat(new_point.lng());
				var y2 = parseFloat(new_point.lat());

				// we need to determine the equation of the line between the two points
				// 		EQUATION: y = mx + b
				// NOTE: we are not worrying about floating point precision issues
				// because we are not looking for 100% accuracy.

				// get the slope of the line between the two points
				var m = calculateSlope(curr_point, new_point);
				// solve for b in the equation for our line using the current map center's coordinates
				var b = calculateB(x1, y1, m);

				// We want to restrict the incremental movements to no move than pan_factor in either
				// the x or y direction.  So we need to determine if solving for y in the line equation
				// will give us an x that is less than pan_factor.  If not, then we want to solve for x
				// and not y.
				var bit_solve_for_y = determineIfSolveForY(curr_point, new_point, m, b, pan_factor);

				// calculate the distance between the two points
				var dist = distance(curr_point, new_point);

				// if the distance between the two points is greater than the distance that can be
				// covered moving incrementally, then we need to zoom out and pan to the new location...
				/*
				if (dist > (MAX_MOVES * pan_factor)) {

					// we use a timeout to allow images to load
					setTimeout(function() {
						panZoomToLocation(curr_point, new_point, m, b, bit_solve_for_y, pan_factor);	// start the zoom out
					}, MAP_ZOOM_DELAY);
					return;
				}
				*/
				// ... else the distance is within the allowed incremental moving distance

				// determine the next incremental point to move to
				var next_point = calculateNextPoint(curr_point, new_point, m, b, bit_solve_for_y, pan_factor);

				// we use a timeout to allow images to load
				setTimeout(function() {
					panToLocation(next_point, new_point, m, b, bit_solve_for_y, pan_factor);	// pan to the next point
				}, MAP_PAN_DELAY);
			}
		}
	}

	return;
} /* }}} */

/*--------------------------------------------------------------------------*/
/**
	@brief This function determines the pan factor for the given zoom
	level.
	@param zoom_level The zoom level to get the pan factor for.
	@return Returns the pan factor (float) for the requested zoom level.
 */
function GetPanFactor(zoom_level) { /* {{{ */

	// initialize pan factor to NONE
	var pan_factor = NONE;

	switch (zoom_level) {

		case 0:
		case 1:
		case 2:
			pan_factor = NONE;
			break;
		case 3:
			pan_factor = PAN_FACTOR_3;
			break;
		case 4:
			pan_factor = PAN_FACTOR_4;
			break;
		case 5:
			pan_factor = PAN_FACTOR_5;
			break;
		case 6:
			pan_factor = PAN_FACTOR_6;
			break;
		case 7:
			pan_factor = PAN_FACTOR_7;
			break;
		case 8:
			pan_factor = PAN_FACTOR_8;
			break;
		case 9:
			pan_factor = PAN_FACTOR_9;
			break;
		case 10:
			pan_factor = PAN_FACTOR_10;
			break;
		case 11:
			pan_factor = PAN_FACTOR_11;
			break;
		case 12:
			pan_factor = PAN_FACTOR_12;
			break;
		case 13:
			pan_factor = PAN_FACTOR_13;
			break;
		case 14:
			pan_factor = PAN_FACTOR_14;
			break;
		case 15:
			pan_factor = PAN_FACTOR_15;
			break;
		case 16:
			pan_factor = PAN_FACTOR_16;
			break;
		case 17:
			pan_factor = PAN_FACTOR_17;
			break;
		case 18:
			pan_factor = PAN_FACTOR_18;
			break;
		case 19:
			pan_factor = PAN_FACTOR_19;
			break;
		case 20:
			pan_factor = PAN_FACTOR_20;
			break;
	}

	return pan_factor;
} /* }}} */

/****************************************************************************
 * FUNCTION:	panToLocation
 *		PARAM move_point:	GLatLng object; current point to pan to
 *		PARAM target_point:	GLatLng object; end point to pan to
 *		PARAM m:	float; Represents the slope of the line between the two
 *					given points.
 *		PARAM b:	float; Represents 'b' in the equation of the line between
 *					the two given points.
 *		PARAM bit_solve_for_y:	bool; If true, equation of line is solved for
 *								y.  If false, equation is solved for x
 *		PARAM pan_factor:	float; Incremental distance to be used for
 *							panning.
 *		RETURN:	none
 *	This function pans the map to the given move point.  If the move point
 *	is not the target point, the next move point is calucated and another
 *	pan is performed.  This continues until the target point is reached.
****************************************************************************/
function panToLocation(move_point, target_point, m, b, bit_solve_for_y, pan_factor) {

	// check if the move point is the target point, if so pan to the target point and return
	if (move_point.equals(target_point)) {
		g_map.panTo(target_point, g_state.curr_zoom);
		g_state.is_pan_zoom = false;					// end pan/zoom
		return;
	}

	// pan to move point
	g_map.panTo(move_point, g_state.curr_zoom);

	// calculate next move point
	var new_point = calculateNextPoint(move_point, target_point, m, b, bit_solve_for_y, pan_factor);

	// pan to next move point
	// NOTE: timeout is used to allow images to load
	setTimeout(function() {
		panToLocation(new_point, target_point, m, b, bit_solve_for_y, pan_factor);
	}, MAP_PAN_DELAY);

	return;
}

/****************************************************************************
 * FUNCTION:	calculateNextPoint
 *		PARAM curr_point:	GLatLng object; The current center of the map.
 *		PARAM target_point:	GLatLng object; The desired center of the map.
 *		PARAM m:	float; Represents the slope of the line between the two
 *					given points.
 *		PARAM b:	float; Represents 'b' in the equation of the line between
 *					the two given points.
 *		PARAM bit_solve_for_y:	bool; If true, equation of line is solved for
 *								y.  If false, equation is solved for x
 *		PARAM pan_factor:	float; Incremental distance to be used for
 *							panning.
 *		RETURN:	GLatLng object representing the next point to pan to.
 *	This function calculates the next point to move to between the current
 *	map center and the target map center.
****************************************************************************/
function calculateNextPoint(curr_point, target_point, m, b, bit_solve_for_y, pan_factor) {

	// the next point to be moved to
	var next_point = null;

	// Flag for whether or not we should return the target point.
	var bit_return_target = false;

	// if we are solving for y...
	if (bit_solve_for_y) {
		// ... then solve for y
		next_point = solveForY(curr_point, target_point, m, b, pan_factor);
	} else {
		// ... else solve for x
		next_point = solveForX(curr_point, target_point, m, b, pan_factor);
	}

	return next_point;
}

/****************************************************************************
 * FUNCTION:	solveForY
 *		PARAM curr_point:	GLatLng object; The current center of the map.
 *		PARAM target_point:	GLatLng object; The desired center of the map.
 *		PARAM m:	float; Represents the slope of the line between the two
 *					given points.
 *		PARAM b:	float; Represents 'b' in the equation of the line between
 *					the two given points.
 *		PARAM pan_factor:	float; Incremental distance to be used for
 *							panning.
 *		RETURN:	GLatLng object representing the next point to pan to.
 *	This function calculates the next point to move to between the current
 *	map center and the target map center.  It does this by solving the
 *	equation of the line between the two points for y, using x coordinate
 *	of the current point.
****************************************************************************/
function solveForY(curr_point, target_point, m, b, pan_factor) {

	// get current x and y coordinates
	var curr_x = parseFloat(curr_point.lng());
	var curr_y = parseFloat(curr_point.lat());

	// get target x and y coordinates
	var targ_x = parseFloat(target_point.lng());
	var targ_y = parseFloat(target_point.lat());

	// x and y coordinates of next point
	// initialize to current x and y so that we only need to increment below
	var x0 = curr_x;
	var y0 = curr_y;

	// If the current point is to the left of the target point...
	if (curr_x < targ_x) {

		// ... we need to increment x
		x0 += pan_factor;

		// if the new x coordinate is to the right of the target x coordinate...
		if (x0 > targ_x) {
			// ... we need to use the target coordinates so we do not pass it
			x0 = targ_x;
			y0 = targ_y;
		} else {
			// ... else we need to solve for y
			y0 = parseFloat((m * x0) + b);
		}

	// ... else we are to the right of the target point...
	} else {

		// ... we need to decrement x
		x0 -= pan_factor;

		// if the new x coordinate is to the left of the target x coordinate...
		if (x0 < targ_x) {
			// ... we need to use the target coordinates so we do no pass it
			x0 = targ_x;
			y0 = targ_y;
		} else {
			// ... else we need to solve for y
			y0 = parseFloat((m * x0) + b);
		}
	}

	return new GLatLng(y0, x0);
}

/****************************************************************************
 * FUNCTION:	solveForX
 *		PARAM curr_point:	GLatLng object; The current center of the map.
 *		PARAM target_point:	GLatLng object; The desired center of the map.
 *		PARAM m:	float; Represents the slope of the line between the two
 *					given points.
 *		PARAM b:	float; Represents 'b' in the equation of the line between
 *					the two given points.
 *		PARAM pan_factor:	float; Incremental distance to be used for
 *							panning.
 *		RETURN:	GLatLng object representing the next point to pan to.
 *	This function calculates the next point to move to between the current
 *	map center and the target map center.  It does this by solving the
 *	equation of the line between the two points for x, using y coordinate
 *	of the current point.
****************************************************************************/
function solveForX(curr_point, target_point, m, b, pan_factor) {

	// get current x and y coordinates
	var curr_x = parseFloat(curr_point.lng());
	var curr_y = parseFloat(curr_point.lat());

	// get target x and y coordinates
	var targ_x = parseFloat(target_point.lng());
	var targ_y = parseFloat(target_point.lat());

	// x and y coordinates of next point
	// initialize to current x and y so that we only need to increment below
	var x0 = curr_x;
	var y0 = curr_y;

	// If the current point is below the target point...
	if (curr_y < targ_y) {

		// ... we need to increment y
		y0 += pan_factor;

		// if the new y coordinate is above the target y coordinate...
		if (y0 > targ_y) {
			// ... we need to use the target coordinates so we do not pass it
			x0 = targ_x;
			y0 = targ_y;
		} else {
			// ... else we need to solve for x
			x0 = parseFloat((y0 - b) / m);
		}

	// ... else we are above the target point...
	} else {

		// ... we need to decrement y
		y0 -= pan_factor;

		// if the new y coordinate is below the target y coordinate...
		if (y0 < targ_y) {
			// ... we need to use the target coordinates so we do not pass it
			x0 = targ_x;
			y0 = targ_y;
		} else {
			// ... else we need to solve for x
			x0 = parseFloat((y0 - b) / m);
		}
	}

	return new GLatLng(y0, x0);
}

