/*
	locator.js
	
	G A Stephen  30 Jun 2008
	
*/

var map;
var localSearch;

// User-entered location
var pc = '';
var pcLatLng = null;
	
// Class to store location details
function Site(name, address, postcode)
{
	this.Name = name;
	this.Address = address;
	this.Postcode = postcode;
	this.LatLng = null;
	this.DistanceMetres = null;					// as the crow files
	this.DistanceMiles = function() {			// as the crow files

		if (this.DistanceMetres == null)
			return null;
		return this.DistanceMetres / 1609.344;

	}
	this.DistanceMetresByRoad = null;			// by road
	this.DistanceMilesByRoad = function() {		// by road

		if (this.DistanceMetresByRoad == null)
			return null;
		return this.DistanceMetresByRoad / 1609.344;

	}
	this.ToHtml = function() {

		var latLngString = "[ null ]";
		if (this.LatLng != null)
			latLngString = this.LatLng.toString();
	
		var distanceString = "";
		if (this.DistanceMetres != null)
			distanceString = "<span class='distance'>" +
				round(this.DistanceMiles(), 1) + " miles / " +
				round(this.DistanceMetres/1000, 1) + " km</span>";

		var distanceByRoadString = "";
		if (this.DistanceMetresByRoad != null)
			distanceByRoadString = "<span class='distance'>" +
				round(this.DistanceMilesByRoad(), 1) + " miles / " +
				round(this.DistanceMetresByRoad/1000, 1) + " km</span>";
			
		return "<a href=\"javascript:markSite('<b>" + this.Name + "</b><br />" + this.Postcode + "', " + 
			this.LatLng.lat() + ", " + this.LatLng.lng() + ");\">&raquo; <b>" + this.Name + "</b></a><br />" +
			this.Address + "<br />" + 
			this.Postcode + "<br />" +
			(distanceByRoadString == "" ? "" : distanceByRoadString + "<br />") +
			(pcLatLng == null ? "" :
			"<a href=\"javascript:getDirections(" + this.LatLng.lat() + ", " + this.LatLng.lng() + 
				", '" + this.Name + "', '" + this.Postcode + "');\">&raquo; Get directions</a><br />");
	}
}


// Set up array of fixed sites 
var sites = new Array();

// this need to be populated appropriately in the host page, e.g:
// sites.push(new Site("Citygate Capitol Way", "Citygate Service Center, Capitol Way, Colindale, London", "NW9 0EW"));

// Place a marker on the map with the given description at the specified location
function markSite(description, lat, lng)
{
	// Display on map
	var point = new GLatLng(lat, lng);
	map.setCenter(point, 15);
	markerOptions = {};
	var marker = new GMarker(point, markerOptions);
	map.addOverlay(marker);
	marker.openInfoWindowHtml(description);
}
		
// Round number to decimalPlaces
function round(number, decimalPlaces)
{
	return Math.round(number * Math.pow(10, decimalPlaces)) / Math.pow(10, decimalPlaces);
}

// Initialise map and geocode the fixed locations
function initialise() 
{
	if (GBrowserIsCompatible())
	{
		map = new GMap2(document.getElementById("map"));
		
		// Initialise map
		map.setCenter(new GLatLng(54.5, -3), 5);
		map.addControl(new GMapTypeControl());
		map.addControl(new GLargeMapControl());
		map.enableGoogleBar();
	}

	// Geocode sites
	// Start by geocoding the first site in the list; the rest will be done in a daisy-chained fashion
	if (sites.length > 0)
		geocodePostcodeSiteLocation(0);
}

// Geocode a site location - function is called in a daisy-chain fashion via the callback
// so that each site is geocoded in turn
function geocodePostcodeSiteLocation(siteIndex)
{
	if (siteIndex < sites.length)
	{
		// Geocode the address using the AJAX Search API
		localSearch = new GlocalSearch();
		localSearch.setSearchCompleteCallback(null, 
			function()
			{
				if (localSearch.results[0])
					geocodeCallbackSetSiteLocation(new GLatLng(localSearch.results[0].lat, localSearch.results[0].lng), siteIndex);
				else
					geocodeCallbackSetSiteLocation(null, siteIndex);
			}
		);
		localSearch.execute(sites[siteIndex].Postcode + ", UK");
	}
	else
	{
		// all the sites have been geocoded
		
		listSites();
		showHideLoadingMessage(false);
	}
}

// Callback for geocoding site location
function geocodeCallbackSetSiteLocation(point, siteIndex)
{
	// Save the geocoded postcode result in the appropriate site object
	sites[siteIndex].LatLng = point;
	
	// Daisy-chain the calls to geocode the sites
	geocodePostcodeSiteLocation(siteIndex + 1);
}

// Geocode the user-entered postcode
function geocodePostcodeInitialPosition(pc)
{
	// Geocode the address using the AJAX Search API
	localSearch = new GlocalSearch();
	localSearch.setSearchCompleteCallback(null,
		function()
		{
			if (localSearch.results[0])
				// Save the location
				pcLatLng = new GLatLng(localSearch.results[0].lat, localSearch.results[0].lng);
			else
				pcLatLng = null;
				
			geocodeCallbackInitialLocation(pcLatLng);
		}
	);
	localSearch.execute(pc + ", UK");
}

// Callback for geocoding the user-entered postcode
function geocodeCallbackInitialLocation(point)
{
	if (!point) 
	{
		alert(pc + " not found.");
		showHideLoadingMessage(false);
		return;
	}
	else
	{
		// Display on map
		markSite("<b>Your location</b><br />" + pc, point.lat(), point.lng());
		
		// Display text details
		document.getElementById("divYourLocation").innerHTML = 
			"<a href=\"javascript:markSite('<b>Your location</b><br />" + pc + "', " +
			point.lat() + ", " + point.lng() + ");\">&raquo; <b>" + pc + "</b></a>";
		
		// Finish processing
		processAfterInitialLocationGeocoded();
	}
}

// Calculate distances between user-entered location and the all the fixed sites
function calculateDistances()
{
	//for (var i = 0; i < sites.length; i++) 
	//	// as-the-crow-files distances
	//	sites[i].DistanceMetres = pcLatLng.distanceFrom(sites[i].LatLng);
	
	// Start the chain of route distance calculations
	calculateDistanceByRoad(0);
}

// Calculate the road distance between the user-entered location and the fixed site given by siteIndex
// Once a directions object has been asynchronously loaded, the next step in the chain is invoked
// by the 'load' event listener
function calculateDistanceByRoad(siteIndex)
{
	if (siteIndex < sites.length)
	{
		// Create a new directions object without attaching it to a map or text panel and load the query
		var directions = new GDirections();
		var query = "from: " + pcLatLng.lat() + ", " + pcLatLng.lng() + " to: " + sites[siteIndex].LatLng.lat() + ", " + sites[siteIndex].LatLng.lng();
		directions.load(query);
		
		// Register listeners for 'load' and 'error' events
		GEvent.addListener(directions, "error", 
			function() 
			{
				alert("GDirections error code: " + directions.getStatus().code);
				showHideLoadingMessage(false);
			}
		);
		GEvent.addListener(
			directions, 
			"load", 
			function() 
			{
				// Save the route distance
				sites[siteIndex].DistanceMetresByRoad = directions.getDistance().meters;

				// Daisy chain the route distance calculations
				calculateDistanceByRoad(siteIndex + 1);
			}
		);
	}
	else
	{
		// all the distances have been calculated

		// Sort sites in ascending order of distance
		sites.sort(function(a, b) { return a.DistanceMetresByRoad - b.DistanceMetresByRoad; });
		
		// Update displayed site list
		listSites();
		showHideLoadingMessage(false);
		
		// Mark the nearest site
		markSite("<b>" + sites[0].Name + "</b><br />" + sites[0].Postcode, sites[0].LatLng.lat(), sites[0].LatLng.lng());
	}
}

// List the sites
function listSites()
{
	document.getElementById("spanSitesDirectionsTitle").innerHTML = "Location";
	var divSites = document.getElementById("divSites");
	divSites.innerHTML = "";
	for (var i = 0; i < sites.length; i++)
		divSites.innerHTML += sites[i].ToHtml() + "<br />";
}

// Rest of code to execute after a user-entered postcode has been geocoded
function processAfterInitialLocationGeocoded() 
{
	if (pcLatLng == null)
		// Quit early if the location could not be geocoded
		return;
	
	// Calculate distance between user-entered location and each fixed site
	calculateDistances();
	
}

// Respond to user-entered postcode
function go()
{
	showHideLoadingMessage(true);
	pc = document.getElementById("txtPostcode").value;
	geocodePostcodeInitialPosition(pc);
}

// Get directions from user-entered location to given position
function getDirections(lat, lng, destinationName, destinationPostcode)
{
	if (pcLatLng != null)
	{
		// Clear any previous map markers
		map.clearOverlays();
		
		// Mark the desintation
		markSite("<b>" + destinationName + "</b><br />" + destinationPostcode, lat, lng);
		
		// Load a directions object attached to the map and a textual panel
		document.getElementById("spanSitesDirectionsTitle").innerHTML = "Directions to " + destinationName;
		var divSites = document.getElementById("divSites");
		divSites.innerHTML = "<a href=\"javascript:listSites();\">&laquo; Back to locations</a><br />";
		var directions = new GDirections(map, divSites);
		var query = "from: " + pcLatLng.lat() + ", " + pcLatLng.lng() + " to: " + lat + ", " + lng;
		directions.load(query);
		
		// Register listeners for the 'load' and 'error' events
		GEvent.addListener(directions, "load", function() { });	
		GEvent.addListener(directions, "error", function() { alert("GDirections error code: " + directions.getStatus().code); });
	}
}

// Toggle the visibility on the 'loading' and content divs
function showHideLoadingMessage(showLoading)
{
	if (showLoading)
	{
		document.getElementById("divLoading1").style.display = "";
		document.getElementById("divForm").style.display = "none";
		document.getElementById("divLoading2").style.display = "";
		document.getElementById("divLocations").style.display = "none";
	}
	else
	{
		document.getElementById("divLoading1").style.display = "none";
		document.getElementById("divForm").style.display = "";
		document.getElementById("divLoading2").style.display = "none";
		document.getElementById("divLocations").style.display = "";
	}
}

// Assign event handlers
window.onunload = GUnload;
window.onload = initialise;




/*

	TODO:
	
	8. revisit styling
	 
*/


