

/**
 *
 * ABmapLocator
 *
 * This script powers a google map and related textfields which
 * together allow a user to pinpoint a location either on the
 * map, or by entering an address, lat/long, easting/northing [Britain Only],
 * or address.
 *
 * The script assumes that the lat and long fields are filled with the 
 * place the map should be initialised to, and these values should be the values
 * stored in a DB on submission of the form containing the locator.
 *
 * For the time being, a hardcoded selection of controls is placed on the map.
 */

document.ABmapLocator = Array();

/**
 *	@param string map_container			the div which the map is to appear in
 	@param string lat_field				the name of the textfield containing the latitude - set to null if not used.
 	@param string long_field			the name of the textfield containing the longitude - set to null if not used.
	@param string address_field			the name of the textfield containing the address - set to null if not used.
	@param string country_code_field	the name of the textfield containing the country code - set to null if not used 
	@param int	  zoom					initial zoom level of the map
 */
function ABmapLocator_initialise(    map_container,
									 map_lat_field,
									 map_long_field,
									 map_address_field,
									 map_country_code_field,
									 map_state_code_field,
									 zoom) {

	// store the fields used for later access
	var locator = document.ABmapLocator;
	
	locator.lat_el 		= document.getElementsByName(map_lat_field)[0];
		
	locator.long_el 		= document.getElementsByName(map_long_field)[0];
		
	locator.address_el 	= document.getElementsByName(map_address_field)[0];

	// default country code:
	locator.current_cc = 'gb';
	locator.current_sc = '';
	
	if (map_country_code_field != null) {
		locator.country_code_el = document.getElementsByName(map_country_code_field)[0];
		locator.current_cc 		= locator.country_code_el.value;
	}
	if (map_state_code_field != null) {
		locator.state_code_el = document.getElementsByName(map_state_code_field)[0];
		locator.current_sc 		= locator.state_code_el.value;
	}
	
	// Create the google map
	var map = new GMap2(document.getElementById(map_container));
	locator.map = map;


	// Extract the Latitude and Longitude and initialise the map:
	var latlong = ABmapLocator_get_latlong_from_latlong_fields();
	map.setCenter(latlong, zoom);

	map.addControl( new GLargeMapControl3D() );
	
	if (zoom != 1)
		ABmapLocator_update_map_from_latlong(true);

	GEvent.addListener(map, "click", function (marker, point) {	
										if (document.ABmapLocator.click_timer != null) {
											// Another click came very quickly. Ignore as it's a doubleclick, and cancel the timer
											clearTimeout(document.ABmapLocator.click_timer);
											document.ABmapLocator.click_timer = null;
										}
										else {
											if (!marker)
											{
												// Schedule a marker to be moved (if a doubleclick doesn't come along
												document.ABmapLocator.point_clicked = point;
												document.ABmapLocator.click_timer=setTimeout(function() {ABmapLocator_do_click()}, 200);
											}
										}
									}
						);

	// create google service connections:
	locator.geocoder = new GClientGeocoder();
	locator.localsearch = new GlocalSearch();
}


function ABmapLocator_do_click() {
	document.ABmapLocator.click_timer = null;
	ABmapLocator_set_latlong_fields(document.ABmapLocator.point_clicked);
	ABmapLocator_update_map_from_latlong(false);	
}

/**
 * If a latlng is passed in, then the latlong fields are
 * updated and the country and state updated too where possible.
 *
 * If a latlng is not passed in, then the existing latlong fields
 * values are used as the lat long and just the country and
 * state fields are updated.
 */
 
function ABmapLocator_set_latlong_fields(latlng) {
	var locator = document.ABmapLocator;
	var	show_errors_to_user;
	
	if (latlng != null) {
		locator.lat_el.value  = latlng.lat();
		locator.long_el.value = latlng.lng();
		show_errors_to_user = true;
	}
	else {
		latlng = ABmapLocator_get_latlong_from_latlong_fields();
		show_errors_to_user = true;
	}

	// If the lat and long aren't set, then don't go any further.
	if (latlng.lat() == 0 && latlng.lng() == 0)
		return;
	
	// additionally, try to update the country code by doing a geocode lookup on the latlng passed in:
	// This is needed for people who do not use the search box, but instead just click on the map.
	var cc_el = locator.country_code_el;
	var sc_el = locator.state_code_el;
	if (cc_el != null) {
		var address_string = latlng.lat() + ", " + latlng.lng();
	
		locator.geocoder.getLocations(address_string, 
					  function (response){
							if (!response || response.Status.code != 200) {
								if (show_errors_to_user)
									alert("Couldn't identify that place - \n\nTry something more specific, or add your country or region.");
							} else {
								var place = response.Placemark[0];
								var latlong = new  GLatLng(place.Point.coordinates[1],place.Point.coordinates[0]);
								
								// extract the 2 letter countrycode from the place and set the country_code el's current value to it:							
								var new_cc = place.AddressDetails.Country.CountryNameCode;

								// Google is reporting London as a country!
								if (new_cc == 'London')
									new_cc = 'GB';
									
								//console.log(place.AddressDetails);
								// set the country field to the new country code:
								cc_el.value = new_cc;
								// If there's a state code, set that too.
								if (sc_el != null && ABmapLocator_country_requires_state(new_cc)) {
									var new_sc = place.AddressDetails.Country.AdministrativeArea.AdministrativeAreaName;
									sc_el.value = new_sc;
								}
								else {
									// Clear the state for countries not requiring state to be defined
									(sc_el.value=null);
								}
							}
						}
		);
	}
}


/** 
 * Defines which countries we want the state from
 */
function ABmapLocator_country_requires_state(country_code) {
	return (country_code == 'US' || country_code == 'CA');
}

function ABmapLocator_get_latlong_from_latlong_fields() {
	var lat  = parseFloat(document.ABmapLocator.lat_el.value);
	var long =  parseFloat(document.ABmapLocator.long_el.value);
	return new GLatLng(lat, long);
}

/**
 * Update the map and address on entry of a Geo lat long by the user in the latlong fields
 *
 */
function ABmapLocator_update_map_from_latlong(relocate_map_centre) {
	latlong = ABmapLocator_get_latlong_from_latlong_fields();
	
	
	var map = document.ABmapLocator.map;

	if (relocate_map_centre) {
		// if the current zoom level is way out, then assume that we want to zoom close in.
		if (map.getZoom() < 4)
			map.setCenter(latlong, 14);
		else
			map.panTo(latlong);
	}
	//  remove the old marker
	map.clearOverlays();

	// then set the new marker
	var marker = new GMarker(latlong);
	map.addOverlay(marker);
	
	// try to look up the address
}

/**
 * Interpret the address the user has entered and attempt to go to the location identified
 */
function ABmapLocator_update_map_from_address() {
	var locator = document.ABmapLocator;
	var address_string = locator.address_el.value;
	
	var geocoder = locator.geocoder;
	var localsearch = locator.localsearch;
	
	// get the currently selected country and use it's code - this will make the place search 
	// more accurate for people outside the UK
	var cc_el = locator.country_code_el;
	var cc = locator.current_cc;
	//console.log('cc = ' + cc);
	if (cc != null && cc.length > 0)
		geocoder.setBaseCountryCode(cc);
	
	// if the country code is set to the UK, and the first thing in the string is a postcode,
	// then, to ensure full accuracy, we need to use the google localsearch API to do the
	// postcode lookup, as the geocoder lookup is limited to one character of the outcode (second part)
	// of the postcode.
	
	// split the address into bits, and try the first bit, or the first two bits - as these might
	// be postcode fragments:
	
	var address_bits = address_string.split(" ");
	if ((cc == 'gb' || cc == 'uk') && 
	     (JSPostcode_checkPostCode(address_bits[0]) ||
	      JSPostcode_checkPostCode(address_bits[0]+" "+address_bits[1]))) { 
		// The user is probably using a postcode - use google localsearch
		// instead of geocoder, as it gives better accuracy on postcodes.
		
		localsearch.setSearchCompleteCallback(null, 
			function() {
				
				if (localsearch.results[0])
				{		
					var searchlat  = localsearch.results[0].lat;
					var searchlong = localsearch.results[0].lng;
					var latlong = new GLatLng(searchlat,searchlong);
					ABmapLocator_set_latlong_fields(latlong);
					ABmapLocator_update_map_from_latlong(true);
				}else{
					alert("Postcode not found - try entering the street and city");
				}
			});	
			
		// note: I use the whole string in case other relevant details follow it
		localsearch.execute(address_string + ", UK");		

} else { // Use the geocoder for everything that isn't a UK postcode
		geocoder.getLocations(address_string, 
					  function (response){
							if (!response || response.Status.code != 200) {
								alert("Couldn't identify that place - \n\nTry something more specific, or add your country or region.");
							} else {
								var place = response.Placemark[0];
								var latlong = new  GLatLng(place.Point.coordinates[1],place.Point.coordinates[0]);
								
								ABmapLocator_set_latlong_fields(latlong);
								ABmapLocator_update_map_from_latlong(true);
								
								// extract the 2 letter countrycode from the place and set the country_code el's current value to it:
								if (cc_el != null)
								{
									var new_cc = place.AddressDetails.Country.CountryNameCode;
									// set the country field to the new country code:
									if (new_cc == 'London')
										new_cc = 'GB';
									
									locator.country_code_el.value = new_cc;
								}
								if (sc_el != null) {
									var new_sc = place.AddressDetails.Country.AdministrativeArea.AdministrativeAreaName;
									locator.state_code_el.value = new_sc;
								}
								
							}
						}
		);
	}
}



