////////////////////////////////////////////////////
//
// JOURNEY_PLANNER.JS
//
// This page defines javascript for the journey
// planner page. 
// 
//
// Created 02/07/2009 by Chris Jones
//
////////////////////////////////////////////////////
// AMENDMENT 0.1
//
// Amendment Details ...
//
// Amended dd/mm/yyyy by ...
//
////////////////////////////////////////////////////
// ==============
// Initialisation
// ==============

// Declare any global variables
// ----------------------------
var googleMapScriptLoaded = 0;
var pageLoaded = 0;
var GMapTestCounter = 0;
var map = new Object();
var bounds = new Object();
var geo = new Object();
var reasons = new Object();
var GMapIcon = new Object();
var path = new Object();
var active = new Object();
var gmarkers = new Object();
var addresses = new Object();
var state = new Object();
var gdir = new Object();
var poly = new Object();


// ======
// Events
// ======

/***************************************************
 * This function is called when the 'ready' event 
 * triggers in the content file.
 ***************************************************/
function initPageSpecific(){
	
	// Setup the map holding area depending on the browser
//	if(jQ.browser.msie){
//		jQ("#map").css("display","block");
//	}else{
//		jQ("#map").css("display","table");
//	}
	
	jQ("#map").show();
	jQ("#multimapOption").hide();
	jQ("#directionAdvice").show();
	jQ("#start").show();
	jQ("#prepath").show();
	
	// Assign click event to the no postcode option
	jQ("#noPostcode").click(function(){
		handleState(2);
		return false;
	});
	
	// Assign event to the start over option
	jQ("#startOver").children("a").click(function(){
		jQ("#path").children().slideUp(500, function(){
			jQ("#path").empty();
		});
		mapStartPoint();
		handleState(0);
		return false;
	});
	
	// Assign click event to the option
	jQ("#showDirections a").click(function(){
		jQ("#showDirections").slideUp(500);
		jQ("#directionAdvice").slideDown(500);
		jQ("#start").slideDown(500);
		jQ("#startOver").slideDown(500);
		return false;
	});
	
	// Assign unload event to the window
	jQ(window).unload(function(){
		GUnload();
	});
}


/***************************************************
 * This function is called when the 'onload' event 
 * triggers in the content file.
 ***************************************************/
function onLoadPageSpecific(){
	// Provide the user with an option to get directions 
	// (incase the onReady event missed it)
	jQ("#showDirections").show();
	
	// Load the the Google map
	showGoogleMap();
}

// =========
// FUNCTIONS
// =========

// -----------------------------
// Functions for journey planner
// -----------------------------

/***************************************************
 * This function handles the state of each div
 ***************************************************/
function handleState(state) {
	if(state==0){	// Start Over
		jQ("#directionAdvice").slideDown(500);
		jQ("#start").slideDown(500);
		jQ("#address").slideUp(500);
		jQ("#drag").slideUp(500);
		jQ("#systemError").slideUp(500);
		jQ("#startOver").slideDown(500);
		jQ("#prepath").slideDown(500);
		jQ("#path").slideUp(500);
	}
	if(state==1){
//		jQ("#address").slideUp(500);
//		jQ("#start").slideUp(500);
//		jQ("#drag").slideDown(500);
//		jQ("#directionAdvice").slideUp(500);
//		jQ("#systemError").slideUp(500);
//		jQ("#address").hide();
//		jQ("#drag").hide();
//		jQ("#start").show();
//		jQ("#directionAdvice").hide();
//		jQ("#systemError").hide();
	}  
	if(state==2){	// No Postcode
		jQ("#directionAdvice").slideDown(500);
		jQ("#start").slideUp(500);
		jQ("#address").slideDown(500);
		jQ("#drag").slideUp(500);
		jQ("#systemError").slideUp(500);
		jQ("#startOver").slideDown(500);
		jQ("#prepath").slideDown(500);
		jQ("#path").slideUp(500);
	}  
	if(state==3){	// No Errors on address
		jQ("#directionAdvice").slideUp(500);
		jQ("#start").slideUp(500);
		jQ("#address").slideUp(500);
		jQ("#drag").slideDown(500);
		jQ("#systemError").slideUp(500);
		jQ("#startOver").slideDown(500);
		jQ("#prepath").slideUp(500);
		jQ("#path").slideDown(500);
	}
}

/***************************************************
 * This function 
 ***************************************************/
function displayOnLoadFailure(){
	jQ('#multimapOption').show();
	jQ("#directionAdvice").hide();
	jQ("#start").hide();
	jQ("#address").hide();
	jQ("#drag").hide();
	jQ("#map").hide();
	jQ("#startOver").hide();
	jQ("#prepath").hide();
	jQ("#path").hide();
}

/***************************************************
 * This function 
 ***************************************************/
//Plot Directions
function directions() {
	if (addresses[0]) {var a=addresses[0] + "@" + path[0].toUrlValue(6)}
	else {var a = path[0].toUrlValue(6)} 
	if (addresses[4]) {var b=addresses[4] + "@" + path[4].toUrlValue(6)}
	else {var b = path[4].toUrlValue(6)} 
	for (var i=3; i>0; i--) {
		if (active[i]) {
			b = path[i].toUrlValue(6) +" to: "+b;
		}
	}
	var a = "from: "+a + " to: " + b;
	gdir.load(a, {getPolyline:true});
}

//Handle Address string input/validation
function getAddress(){
	//Get form values
	var errors=false;
	var address1=jQ("input#address1").val();
	var address2=jQ("input#address2").val();
	var town=jQ("input#town").val();
	var county=jQ("input#county").val();
	//Check for one of the values
	if(address1=="") {
		jQ("#systemError .mapError").text("An address must be provided.");
		jQ("#systemError").slideDown(500);
		errors=true;
	}
	if(!errors){
		var dataString=address1+','+address2+','+town+','+county;
		usePointFromPostcode(dataString,doStart,directions);
		state=3;
		handleState(state);
	}
}

//Handle postcode string input/validation
function getPostcode(){
	var errors=false;
	var postcode=jQ("#search").val();    		
	if(!postit(postcode)) errors=true;
	if(!errors){
		usePointFromPostcode(postcode,doStart,directions);
		state=3;
		handleState(state);
	}
}

//Validate the postcode
function postit(postcode){
	size=postcode.length;
	//Change to uppercase
	postcode=postcode.toUpperCase();
	//Add a space if missing
	if(postcode.substr(size-4,1) != " ") {
		postcode=postcode.substr(0,size-3) + " " + postcode.substr(size-3);
		size=postcode.length;
	}
	//Strip leading spaces
	while(postcode.slice(0,1)==" ") {
		postcode=postcode.substr(1,size-1);
		size=postcode.length;
	}
	//Strip trailing spaces
	while(postcode.slice(size-1,size)==" ") {
		postcode=postcode.substr(0,size-1);
		size=postcode.length;
	}
	//Write the formatted postcode back to the field
	jQ("#search").val(postcode);
	 //No postcode given
	if(size == 0){
		jQ("#systemError .mapError").text("No postcode was supplied.");
		jQ("#systemError").slideDown(500);
		jQ("#search").focus();
		return false;
	}
	 //Code length rule
	if(size<6 || size>8){
		jQ("#systemError .mapError").text(postcode + " is not a valid postcode - wrong length.");
		jQ("#systemError").slideDown(500);
		jQ("#search").focus();
		return false;
	}
	//leftmost character must be alpha character rule
	if(!(isNaN(postcode.charAt(0)))){
		jQ("#systemError .mapError").text(postcode + " is not a valid postcode - cannot start with a number.");
		jQ("#systemError").slideDown(500);
		jQ("#search").focus();
		return false;
	}
	if(isNaN(postcode.charAt(size-3))){ //first character of inward code must be numeric rule
		jQ("#systemError .mapError").text(postcode + " is not a valid postcode - alpha character in wrong position.");
		jQ("#systemError").slideDown(500);
		jQ("#search").focus();
		return false;
	}			
	//second character of inward code must be alpha rule
	if(!(isNaN(postcode.charAt(size-2)))){
		jQ("#systemError .mapError").text(postcode + " is not a valid postcode - number in wrong position.");
		jQ("#systemError").slideDown(500);
		jQ("#search").focus();
		return false;
	}			
	//third character of inward code must be alpha rule			
	if(!(isNaN(postcode.charAt(size-1)))){
		jQ("#systemError .mapError").text(postcode + " is not a valid postcode - number in wrong position.");
		jQ("#systemError").slideDown(500);
		jQ("#search").focus();
		return false;
	}
	//space in position length-3 rule
	if(!(postcode.charAt(size-4)==" ")){
		jQ("#systemError .mapError").text(postcode + " is not a valid postcode - no space or space in wrong position.");
		jQ("#systemError").slideDown(500);
		jQ("#search").focus();
		return false;
	}
	count1=postcode.indexOf(" ");
	count2=postcode.lastIndexOf(" ");
	//only one space rule
	if(count1 != count2){
		jQ("#systemError .mapError").text(postcode + " is not a valid postcode - only one space allowed.");
		jQ("#systemError").slideDown(500);
		jQ("#search").focus();
		return false;
	}
	return true;
}

//Create a marker on the map
function createMarker(point,i,icon) { 
	if(i==4) var isDraggable=false;
	else var isDraggable=true;
	var marker=new GMarker(point,{draggable:isDraggable,icon:icon,bouncy:true}); 
	gmarkers[i]=marker; 
	GEvent.addListener(marker, "dragend",function() { 
		path[i] = marker.getPoint(); 
		if (!active[i]) { 
			setTimeout('swapMarkers('+i+')',1000); 
		} 
		active[i]=true; 
		addresses[i]=""; 
	}); 
	if(i==4){
		GEvent.addListener(marker,"click",function(){
			marker.openInfoWindowHtml("<b>Liverpool Waters</b><br/>Liverpool Docks<br />L5 9XN");
		});
	}
	map.addOverlay(marker); 
}



function doStart(point, callbackFunction) {
	//Plot the start point
	createMarker(point,0,GMapIcon[1]);
	path[0]=point;        
	//Plot the intermediary points
	for (var i=1; i<4; i++) {        	
		var lat=(path[0].lat()*(4-i) + path[4].lat()*i)/4;
		var lng=(path[0].lng()*(4-i) + path[4].lng()*i)/4;
		var p=new GLatLng(lat,lng);          
		createMarker(p,i,GMapIcon[4]);
		path[i]=p;
	}       
	bounds.extend(path[0]);
	bounds.extend(path[4]);
	map.setZoom(map.getBoundsZoomLevel(bounds));
	map.setCenter(bounds.getCenter());             
	state=1;
	handleState(state);
	
	if(callbackFunction){
		callbackFunction();
	}
}

function doEnd(point) {
	createMarker(point,4,GMapIcon[3]);
	path[4]=point;
	if (path[0] != undefined){
		for (var i=1; i<4; i++) {
			var lat=(path[0].lat()*(4-i) + path[4].lat()*i)/4;
			var lng=(path[0].lng()*(4-i) + path[4].lng()*i)/4;
			var p=new GLatLng(lat,lng);
			createMarker(p,i,GMapIcon[4]);
			path[i]=p;
		}
		bounds.extend(path[0]);
		bounds.extend(path[4]);
		map.setZoom(map.getBoundsZoomLevel(bounds));
		map.setCenter(bounds.getCenter());
	}
}

//Use a different approach to obtaining coordinates rather 
//than using geo.getLatLng.
function usePointFromPostcode(postcode,callbackFunction, cbcallbackFunction) {
	//Hide errors
	localSearch.setSearchCompleteCallback(null,function() {
		if(localSearch.results[0]) {    
			var resultLat=localSearch.results[0].lat;
			var resultLng=localSearch.results[0].lng;
			var point=new GLatLng(resultLat,resultLng);
			if(cbcallbackFunction){
				callbackFunction(point, cbcallbackFunction);
			} else {
				callbackFunction(point, null);
			}
		} else {
			jQ("#systemError .mapError").text('Could not find "'+postcode+ '"');
			jQ("#systemError").slideDown(500);
		}
	});  
	localSearch.execute(postcode + ", UK");
}

function swapMarkers(i) { 
	map.removeOverlay(gmarkers[i]); 
	createMarker(path[i],i,GMapIcon[2]); 
} 

// Refresh the map to start
function mapStartPoint(){
	// Clear old path
	path=[];
	// Clear all markers
	map.clearOverlays();
	//Center the map
	map.setCenter(new GLatLng(53.4457,-2.9998),12);
	//Plot postcode initially
	usePointFromPostcode(thisPostcode,doEnd);
}

function showGoogleMap(){

	//Is the browser compatible?
	if (GBrowserIsCompatible()) {
		thisPostcode='L5 9XN';			

		//Invoke Googles search engine
		localSearch=new GlocalSearch();

		if (typeof window.GMap == "function" || GMapTestCounter > 5){
			if (Ghndl != undefined){
				window.clearTimeout(Ghndl);
			}
			
			map=new GMap(document.getElementById("map"));
			var topRight = new GControlPosition(G_ANCHOR_TOP_RIGHT, new GSize(10,105));
			var topLeft = new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(10,105));
			map.addControl(new GLargeMapControl(), topLeft);
			map.addControl(new GMapTypeControl(), topRight);
			bounds=new GLatLngBounds(); 

			//Create client geocoder
			geo=new GClientGeocoder(new GGeocodeCache()); 

			//Map errors
			reasons=[]; 
			reasons[G_GEO_SUCCESS]            = "Success"; 
			reasons[G_GEO_MISSING_ADDRESS]    = "Missing Address: The address was either missing or had no value."; 
			reasons[G_GEO_UNKNOWN_ADDRESS]    = "Unknown Address:  No corresponding geographic location could be found for the specified address."; 
			reasons[G_GEO_UNAVAILABLE_ADDRESS]= "Unavailable Address:  The geocode for the given address cannot be returned due to legal or contractual reasons."; 
			reasons[G_GEO_BAD_KEY]            = "Bad Key: The API key is either invalid or does not match the domain for which it was given"; 
			reasons[G_GEO_TOO_MANY_QUERIES]   = "Too Many Queries: The daily geocoding quota for this site has been exceeded."; 
			reasons[G_GEO_SERVER_ERROR]       = "Server error: The geocoding request could not be successfully processed."; 
			reasons[G_GEO_BAD_REQUEST]        = "A directions request could not be successfully parsed."; 
			reasons[G_GEO_MISSING_QUERY]      = "No query was specified in the input."; 
			reasons[G_GEO_UNKNOWN_DIRECTIONS] = "The GDirections object could not compute directions between the points."; 

			//Map icons
			var baseIcon=new GIcon(G_DEFAULT_ICON); 
				baseIcon.iconSize=new GSize(24,38); 
			var icon1=G_START_ICON; 
			var icon2=G_PAUSE_ICON;
			var icon3=G_END_ICON; 
			var icon4=new GIcon(baseIcon,"http://labs.google.com/ridefinder/images/mm_20_white.png"); 
				icon4.shadow="http://labs.google.com/ridefinder/images/mm_20_shadow.png"; 
				icon4.iconSize=new GSize(12, 20); 
				icon4.shadowSize=new GSize(22, 20); 
				icon4.iconAnchor=new GPoint(6, 20); 
				icon4.infoWindowAnchor=new GPoint(5, 1);
			GMapIcon = [];
			GMapIcon[0] = baseIcon;
			GMapIcon[1] = icon1;
			GMapIcon[2] = icon2;
			GMapIcon[3] = icon3;
			GMapIcon[4] = icon4;

			//Plot the starting point
			mapStartPoint();
			
			//Arrays
			active=[true,false,false,false,true];
			gmarkers=[];
			addresses=[];
			
			gdir=new GDirections(map,document.getElementById("path"));
			GEvent.addListener(gdir,"error", function() {
				var code=gdir.getStatus().code;
				var reason="Code "+code;
				if (reasons[code]) reason = "Code "+code +" : "+reasons[code]
				jQ("#systemError .mapError").text("Error in calculating your route. Error code "+code+" has been returned.");
				jQ("#systemError").slideDown(500);
			});
			
			poly = null;
			GEvent.addListener(gdir,"load",function() {
				if (poly) map.removeOverlay(poly);
				poly=gdir.getPolyline();
				map.addOverlay(poly);
			});
		} else {
//			if(GMapTestCounter >= 5) {
//				window.clearTimeout(Ghndl);
//				displayOnLoadFailure();
//			} else {
//				GMapTestCounter += 1;
				var Ghndl = window.setTimeout("showGoogleMap()", 100);
//			}
		}
	
	//Error with browser
	} else {
//		jQ("#systemError .mapError").text("Google Maps API is not compatible with this browser.");
//		jQ("#systemError").slideDown(500);
		displayOnLoadFailure();
	}
}
