/**
*	Manages an Appellation St. Helena Google Map
*
*	@param	string	mapArea	Name of the HTML element to contain the map.
*/
var geoXml = null;
var geocoder = null;
// Lock used to flow control infowindow requests
var infoName = null;
// Directions instance
var directions = null;
// Error message strings
var MAP_ERR_GOOGLE = 'We are sorry, but the Google Maps functionality is not compatible with your browser';
var MAP_ERR_NOT_CA = 'Either the address you entered is not in California or it is too ambiguous. Please enter a California address.';
var MAP_TO_ADDR = 'Please enter a \'to\' address.';
var MAP_FROM_ADDR = 'Please enter a \'from\' address.';
var MAP_ERR_NOT_VALID = 'We\'re sorry, but it appears that you did not enter a valid address. Please try again.';
var MAP_AMBIGUOUS = 'Your address is ambiguous. Please choose one of the possibilities below, or enter a new address.';
var MAP_GEO_UNKNOWN_ADDRESS = "No corresponding geographic location could be found for one of the specified addresses. This may be due to the fact that the address is relatively new, or it may be incorrect.";
var MAP_GEO_SERVER_ERROR = "A geocoding or directions request could not be successfully processed, yet the exact reason for the failure is not known.";
var MAP_GEO_MISSING_QUERY = "No query was specified in the input.";
var MAP_GEO_BAD_KEY = "The given key is either invalid or does not match the domain for which it was given.";
var MAP_GEO_BAD_REQUEST = "A directions request could not be successfully parsed.";
var MAP_GEO_UNKNOWN = "An unknown error occurred.";

// Create the map, start loading the KML boundaries file, and create the directions instance
function initializeMap() {
	if (!GBrowserIsCompatible()) {
		alert(MAP_ERR_GOOGLE);
	}
	else {
		map = new GMap2($('mapCanvas'));
		geoXml = new GGeoXml(
			MAP_BOUNDS_URL,
			xmlLoaded
		);
		initMapView(G_HYBRID_MAP);
		var topRight = new GControlPosition(G_ANCHOR_TOP_RIGHT, new GSize(10,10));
		var topRight1 = new GControlPosition(G_ANCHOR_TOP_RIGHT, new GSize(100,10));
		map.addControl(new GLargeMapControl(), topRight);
		map.addControl(new GMapTypeControl(), topRight1);
		
		fillPlacemarks();
	
		directions = new Directions();
		geocoder = new GClientGeocoder();
	}
}

function initMapView(mode) {
	map.setCenter(new GLatLng(MAP_CENTER_LAT, MAP_CENTER_LONG), 13, mode );
}

// Set the map attrributes and ask for the placemark info
function xmlLoaded() {
	map.addOverlay(geoXml);
}

// Create the markers and member list
function fillPlacemarks() {
	points.each(
		function(value, index) {
			// Create the list link
			var lnk = document.createElement("a");
			lnk.setAttribute("id", "mem"+value.id);
			lnk.geo = value.geo;
			lnk.onclick = onclickName;
			var txtNode = document.createTextNode(value.name);
			lnk.appendChild(txtNode);
			$("mapMembers").appendChild(lnk);
			// Create the placemarker
			var point = new GLatLng( value.lat, value.long );
			var marker = new GMarker( point, { title: value.name } );
			map.addOverlay(marker);
			points[index].marker = marker;
			GEvent.addListener(
				marker,
				"click",
				function() {
					getInfo(value.name);
				}
			);
		}
	);
}

// Handle a click on a member name
function onclickName(e) {
	if ( arguments.length == 0 )
			e = event;
	var el = e.target ? e.target : e.srcElement;
	var txtNode = el.firstChild;
	getInfo(txtNode.nodeValue);
}

// Get the member's descriptive info from the host
function getInfo(name) {
	if (directions.gettingDirections()) {
		directions.cancel();
	}

	if (infoName == null) {
		infoName = name;
		var request = new Ajax.Request(
			MAP_HANDLER_URL,
			{
				method: "get",
				parameters: "action=getInfo&name="+encodeURIComponent(name),
				onComplete: fillInfo
			}
		);
	}
}

// Create an infowindow for the marker
function fillInfo(transport) {
	var response = transport.responseText;

	// Find the corresponding point
	var point = points.detect(
		function(pt) { return pt.marker.getTitle() == infoName; }
	);

	if (typeof(point) != 'undefined') {
		// Create the infoWindow HTML
		var html = '<h2 style="width:385px;">' + infoName + '</h2>' + response;
	
		if (point.geo != null) {
					html += '<p style="padding-top: 10px;">Directions: ';
					html += '<a href="#" onclick="getDirections(\'' + infoName + '\',\'to\'); return false;" style="text-decoration: underline; color: red;">To here</a>';
					html += '&nbsp;-&nbsp;';
					html += '<a href="#" onclick="getDirections(\'' + infoName + '\',\'from\'); return false;" style="text-decoration: underline; color: red;">From here</a>';
					html += '</p>';
		}
	
		point.marker.openInfoWindowHtml( html, { maxWidth:533 } );
		infoName = null;
	}
}

// Start the process of providing directions
function getDirections(name, dir) {
	// Find the corresponding point
	var point = points.detect(
		function(pt) { return pt.marker.getTitle() == name; }
	);
	if (typeof(point) != 'undefined') {
		directions.askAnchor(point, dir);
	}
}

/**
*	Class to encapsulate providing directions
**/

var Directions = Class.create();

Directions.prototype = {
	initialize:function () {
		this.activeRequest = false;
		this.request = null;
		this.mapOriginalType = null;
		this.point = null;
		this.toFrom = null;
		this.gdir = null;
		this.fromMember = null;
		this.toMember = null;
		$('mapList').style.display = 'block';
		$('mapAskDirections').hide();
		$('mapShowDirections').hide();
	},
	
	gettingDirections:function() {
		return this.activeRequest;
	},
	
	cancel:function() {
		if (this.request) {
			this.request.transport.abort();
		}
		if (this.gdir) {
			this.gdir.clear();
		}
		$('mapDirFromTo').innerHTML = '';		
		$('mapDirections').innerHTML = '';
		initMapView(this.mapOriginalType);
		this.initialize();
	},
	
	errorMsg:function(msg) {
		$('mapAskMsg').innerHTML = msg;
		$('mapAskMsg').style.display = 'block';
	},
	
	clearErrorMsg:function() {
		$('mapAskMsg').innerHTML = '';
		$('mapAskMsg').hide();
	},
	
	askAnchor:function(point, toFrom) {
		map.closeInfoWindow();
		this.mapOriginalType = map.getCurrentMapType();
		map.setMapType(G_NORMAL_MAP);
		$('mapList').hide();
		$('mapAskDirections').style.display = 'block';

		this.point = point;
		this.toFrom = toFrom;
		$('mapAskMsg').hide();
		if (toFrom == 'to') {
			this.toMember = point.name;
			$('fromMemberInfo').innerHTML = this.memberSelectHtml()
																		+	'<p class="mapAskConnector">or</p>'
																		+ '<input type="text" id="fromAddr" name="fromAddr" size="27" />';
			$('toMemberInfo').innerHTML = '<p>' + point.name + '<br />' + point.geo + '</p>';
			$('fromAddr').focus();
		}
		else {
			this.fromMember = point.name;
			$('fromMemberInfo').innerHTML = '<p>' + point.name + '<br />' + point.geo + '</p>';
			$('toMemberInfo').innerHTML = this.memberSelectHtml()
																		+	'<p class="mapAskConnector">or</p>'
																		+ '<input type="text" id="toAddr" name="toAddr" size="27" />';
			$('toAddr').focus();
		}
		$('dirOptions2').checked = true;
		$('mapAskButtonSubmit').onclick = this.checkAddress.bind(this);
		$('mapAskButtonCancel').onclick = this.cancel.bind(this);
	},
	
	checkAddress:function() {
		$('mapAskMsg').hide();
		var addr = $((this.toFrom == 'to') ? 'fromAddr' : 'toAddr').value;
	
		if (addr != '') {
			// Check the address the user provided
			geocoder.getLocations(
					addr,
					this.geocodeResult.bind(this)
				);
		}
		else if ($('addressList').selectedIndex > 0 ) {
			// The user picked another member from the SELECT list.
			// We already know the Geocode address
			var el = $('addressList').options[$('addressList').selectedIndex];
			if (this.toFrom == 'to') {
				this.fromMember = el.text;
			}
			else {
				this.toMember = el.text;
			}
			this.fillDirections(el.value);
		}
		else {
			this.errorMsg((this.toFrom == 'to') ? MSG_FROM_ADDR : MSG_TO_ADDR);
		}
	},
	
	/**
	 *	Gets called when the Geocoder getLocations process is finished.
	 */
	geocodeResult:function(response) {
		if ( response && response.Status.code == 200 && response.Placemark.length == 1 ) {
			// The response is a single Placemark, so the address was valid.
			// Get the formatted version in the Placemark
			var place = response.Placemark[0];
			var state = place.AddressDetails.Country.AdministrativeArea.AdministrativeAreaName;
			if (state != 'CA') {
				this.errorMsg(MAP_ERR_NOT_CA);
			}
			else {
				this.fillDirections(place.address);
			}
		}
		else if (!response || response.Status.code != 200) {
			// Some type of error. Ask the user again.
			this.errorMsg(MAP_ERR_NOT_VALID);
		}
		else {
			// The user's response was ambiguous. Look through the list for CA addresses.
			// If there is only one, use it. If there are none, display an error. Otherwise,
			// display a list of possibilities and ask them to choose.
			var validAddrs = response.Placemark.findAll(
				function(place) { return place.AddressDetails.Country.AdministrativeArea.AdministrativeAreaName == 'CA'; }
			);
			
			if (validAddrs.length == 1) {
				this.fillDirections(validAddrs[0].address);
			}
			else if (validAddrs.length == 0) {
				this.errorMsg(MAP_ERR_NOT_CA);
			} else {
				var addrArea = $('mapAskMsg');
				addrArea.innerHTML = '';
				validAddrs.each(
					function (place) {
						var el = document.createElement("a");
						el.style.display = 'block';
						el.style.cursor = 'pointer';
						el.innerHTML = place.address;
						el.onclick = function() {
							directions.fillDirections(place.address);
						};
						addrArea.appendChild(el);
					}
				);
				new Insertion.Top(addrArea, "<p>" + MAP_AMBIGUOUS + "</p>");
				addrArea.style.display = 'block';
			}
		}
	},
	
	/**
	*	Use the addresses entered by the user to get directions
	**/
	fillDirections:function(otherAddr) {
		var route = ($('dirOptions0').checked || $('dirOptions2').checked) ? map : null;
		var text = ($('dirOptions1').checked || $('dirOptions2').checked) ? $('mapDirections') : null;
		this.gdir = new GDirections(route, text);
		GEvent.addListener(this.gdir, 'load', this.onGDirectionsLoad.bind(this));
		GEvent.addListener(this.gdir, 'error', this.onGDirectionsError.bind(this));
		
		if (this.toFrom == 'to') {
			var address = 'from: ' + otherAddr + ' to: ' + this.point.geo;
		}
		else {
			var address = 'from: ' + this.point.geo + ' to: ' + otherAddr;
		}
		
		this.gdir.load( address, { locale: 'en_US' });
	},
	
	/**
	* Callback: Get information about the latest directions load
	*/
	onGDirectionsLoad:function(){
		var html = '';
		if (this.fromMember) {
			html += '<div><span class="mapDirLabel">From:</span>\n'
						+	'<span id="mapAddrFrom">' + this.fromMember + '</span></div>\n';
		}
		if (this.toMember) {
			html += '<div><span class="mapDirLabel">To:</span>\n'
						+	'<span id="mapAddrTo">' + this.toMember + '</span></div>\n';
		}
		$('mapDirFromTo').innerHTML = html;
		$('mapShowDirErase').onclick = this.cancel.bind(this);
		$('mapAskDirections').hide();
		$('mapShowDirections').style.display = 'block';
	},

	/**
	* Callback: Get information about the latest directions load
	*/
	onGDirectionsError:function(){
	   if (this.gdir.getStatus().code == G_GEO_UNKNOWN_ADDRESS)
		 	this.geoErrorMsg(MAP_GEO_UNKNOWN_ADDRESS);
	   else if (this.gdir.getStatus().code == G_GEO_SERVER_ERROR)
		 	this.geoErrorMsg(MAP_GEO_SERVER_ERROR);
	   else if (this.gdir.getStatus().code == G_GEO_MISSING_QUERY)
		 	this.geoErrorMsg(MAP_GEO_MISSING_QUERY);
	   else if (this.gdir.getStatus().code == G_GEO_BAD_KEY)
		 	this.geoErrorMsg(MAP_GEO_BAD_KEY);
	   else if (this.gdir.getStatus().code == G_GEO_BAD_REQUEST)
		 	this.geoErrorMsg(MAP_GEO_BAD_REQUEST);
	   else
		 	this.geoErrorMsg(MAP_GEO_UNKNOWN);
	},

	geoErrorMsg:function(msg) {
		msg += "<br />Error code: " + this.gdir.getStatus().code;
		this.errorMsg(msg);
	},
	
	/**
	* Create the html for a member select list
	*/
	memberSelectHtml:function() {
		var html = 	'<div id="addressListArea">\n'
							+ '<select name="addressList" id="addressList" style="width: 180px;">\n';
		html += '<option value="" selected="selected">-- ASH Members --</option>\n';
		points.each(
			function(value, index) {
				html += '<option value="' + value.geo + '">' + value.name + '</option>\n';
			}
		);
		html += '</select></div>\n';
		
		return html;
	}
}