var theMap = null;
var mapListener = null;
var theMarkerControls = null;
var destinationsArray;
var startID = null;
var endID = null;
var defaultZoomLevel = null;
var theMapCenter = null;

function cMapClickState() {
	var enableLogging = defaultParameter(arguments, 1, false);
	var testMode = defaultParameter(arguments, 2, false);

	this.cMapClickState = function(enableLogging, testMode) {
		this.mState = null;
		this.mLogState = enableLogging;
		this.mTestMode = testMode;

		this.reset();
	}
	
	this.getState = function() {
		return this.mState;
	}
	
	this.reset = function() {
		this.mState = 'reset';

		if(this.mLogState)
			error_log('reset()->reset');
	}
	
	this.updateState = function(input) {
		if(this.mTestMode) {
			error_log('Received input ' + input);
			return;
		}
		
		switch(input) {
			case 'sclick': 	// Clicked on the start marker
				this.processSClick();
				break;
				
			case 'eclick': 	// Clicked on the end marker
				this.processEClick();
				break;
			
			case 'click': 	// Clicked on a marker that is neither the start nor the end
				this.processClick();
				break;
				
			case 'dropdownChangeStart':
				this.processDropdownChangeStart();
				break;

			case 'dropdownChangeEnd':
				this.processDropdownChangeEnd();
				break;

			default:
				alert('Bad input = ' + input);
				break;
		}
		
		return this.getState();
	}

	this.processEClick = function() {
		var originalState = this.mState;
		switch(this.mState) {
			case 'bothAreSet':
				this.mState = 'startIsSet';
				break;
				
			default:
				alert('Bad state transition in processClick(): ' + this.mState);
				break;
		}

		if(this.mLogState)
			error_log(originalState + '->' + clickType + '->' + this.mState);
	}

	this.processSClick = function() {
		var originalState = this.mState;
		switch(this.mState) {
			case 'startIsSet':
				this.mState = 'reset';
				break;
				
			case 'bothAreSet':
				this.mState = 'endIsSet';
				break;
				
			default:
				alert('Bad state transition in processClick(): ' + this.mState);
				break;
		}

		if(this.mLogState)
			error_log(originalState + '->' + clickType + '->' + this.mState);
	}

	this.processClick = function() {
		var originalState = this.mState;
		switch(this.mState) {
			case 'reset':
				this.mState = 'startIsSet';
				break;
				
			// Note that we don't check for whether the click was on the start marker or the
			// end marker, because if it were, we wouldn't be here. We'd be in sclick or eclick.
			case 'endIsSet':
			case 'startIsSet':
				this.mState = 'bothAreSet';
				break;
				
			case 'bothAreSet': 	// No change; if both are set, we don't care about non-route marker clicks
				break;
				
			default:
				alert('Bad state transition in processClick(): ' + this.mState);
				break;
		}

		if(this.mLogState)
			error_log(originalState + '->' + clickType + '->' + this.mState);
	}
	
	this.processDropdownChangeStart = function() {
		var originalState = this.mState;
		switch(this.mState) {
			case 'reset':
				this.mState = 'startIsSet';
				break;

			case 'endIsSet':
				this.mState = 'bothAreSet';
				break;
				
			case 'startIsSet':
			case 'bothAreSet':
				break;
				
			default:
				alert('Bad state transition in processClick(): ' + this.mState);
				break;
		}
	}

	this.processDropdownChangeEnd = function() {
		var originalState = this.mState;
		switch(this.mState) {
			case 'reset':
				this.mState = 'endIsSet';
				break;

			case 'startIsSet':
				this.mState = 'bothAreSet';
				break;
				
			case 'endIsSet':
			case 'bothAreSet':
				break;

			default:
				alert('Bad state transition in processClick(): ' + this.mState);
				break;
		}

		if(this.mLogState)
			error_log(originalState + '->' + clickType + '->' + this.mState);
	}

	this.cMapClickState(enableLogging, testMode);
}

var theMapClickState = new cMapClickState();

function announceCity(selectTag) {
	theMapClickState.updateState((selectTag.id == 'start') ? 'dropdownChangeStart' : 'dropdownChangeEnd');

	theMarkerControls.resetFlag(selectTag.id, selectTag.value);
	changeStartEnd(selectTag.id, selectTag.value);
}

function changeStartEnd(tagID, dbID) {
	theMarkerControls.setFlag(tagID, dbID);

	// If we have a route, erase it when the user clicks; we'll probably be drawing a new route
	if(theRoute != null) {
		theRoute.restartAll();
	}

	//document.getElementById('totalDistance').innerHTML = '&nbsp;';

	theMap.setCenter(theMapCenter);
	theMap.setZoom(defaultZoomLevel);
}

function checkClick() {
	startID = document.getElementById('start').value;
	endID = document.getElementById('end').value;
	if(startID == 0) {
		alert('You must select a start point.');
	} else if(endID == 0) {
		alert('You must select an end point.');
	} else if(startID == endID) {
		alert('The start point and end point must be different destinations.');
	} else {
		return true;
	}

	return false;
}

function getAllCities() {
	var httpRequest = makeHTTPRequest();

	httpRequest.open("POST", "http://" + serverName + "/vamos/admins/ajaxfunctions.php", false);
	httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');

	var encoded = 'function=getDestinations';

	httpRequest.send(encoded);
	
	/*
	<FunctionResult>
		<FunctionStatus success="1" message="Request processed" />
		<Destinations>
			<Destination dbID="12" latitude="9.5478" longitude="-89.7328" />
		</Destinations>
	</FunctionResult>
	*/
	
	destinationsArray = new Array();
	
	if (httpRequest.status == 200) {
		var xml = httpRequest.responseXML;
		if(xml) {
			var functionResultNode = xml.getElementsByTagName('FunctionResult');
			if(functionResultNode.length > 0) {
				var functionStatuses = functionResultNode[0].getElementsByTagName('FunctionStatus');
				if(functionStatuses.length > 0) {
					var functionStatusNode = functionStatuses[0];
			
					var success = functionStatusNode.attributes.getNamedItem("success").value;
					var message = stripslashes(functionStatusNode.attributes.getNamedItem("message").value);

					if(success == 1) {
						var destinationsArrayNode = functionResultNode[0].getElementsByTagName('Destinations');
						if(destinationsArrayNode.length > 0) {
							var destinations = destinationsArrayNode[0].getElementsByTagName('Destination');

							for(var i = 0; i < destinations.length; ++i) {
								var destinationNode = destinations[i];
								var name = stripslashes(destinationNode.attributes.getNamedItem("name").value);
								var address = stripslashes(destinationNode.attributes.getNamedItem("address").value);
								var city = stripslashes(destinationNode.attributes.getNamedItem("city").value);
								var latitude = destinationNode.attributes.getNamedItem("latitude").value;
								var longitude = destinationNode.attributes.getNamedItem("longitude").value;
								var notes = stripslashes(destinationNode.attributes.getNamedItem("notes").value);
								var dbID = destinationNode.attributes.getNamedItem("dbID").value;
								var mapIconID = destinationNode.attributes.getNamedItem("mapIconID").value;

								destinationsArray.push(new cDestination(theMap, name, address, city,
																			longitude, latitude, notes, mapIconID, dbID));
							}

							return true;
						}
					}
				}
			}
		}
	}

	error_log("There was a communications error: " + httpRequest.responseText);
	alert("There was a communications error: " + httpRequest.responseText);
	
	return false;
}

function getCityByDBID(dbID) {
	for(var i in destinationsArray) {
		if(destinationsArray[i].mDatabaseID == dbID)
			return destinationsArray[i];
	}
	
	return null;
}

function getNearestCity(latLng, excludeTagName) {
	var httpRequest = makeHTTPRequest();

	httpRequest.open("POST", "http://" + serverName + "/vamos/admins/ajaxfunctions.php", false);
	httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');

	var encoded = 'function=getDestinations';

	httpRequest.send(encoded);
	
	/*
	<FunctionResult>
		<FunctionStatus success="1" message="Request processed" />
		<Destinations>
			<Destination dbID="12" latitude="9.5478" longitude="-89.7328" />
		</Destinations>
	</FunctionResult>
	*/
	
	if (httpRequest.status == 200) {
		var xml = httpRequest.responseXML;
		if(xml) {
			var functionResultsTags = xml.getElementsByTagName('FunctionResult');
			if(functionResultsTags.length > 0) {
				var functionResults = functionResultsTags[0];
				var functionStatusTags = functionResults.getElementsByTagName('FunctionStatus');
				if(functionStatusTags.length > 0) {
					var functionStatus = functionStatusTags[0];

					var success = functionStatus.attributes.getNamedItem("success").value;
					var message = stripslashes(functionStatus.attributes.getNamedItem("message").value);

					if(success == 1) {
						var destinationsArrayTags = functionResults.getElementsByTagName('Destinations');
						if(destinationsArrayTags.length > 0) {
							var destinationsArray = destinationsArrayTags[0];
							var destinations = destinationsArray.getElementsByTagName('Destination');

							var closestDBID = false;
							var smallestDistance = -1;
							
							// Caller doesn't want to know that the click was closest to the start or
							// end point. Get the next closest one.
							var excludeDBID = (excludeTagName ? document.getElementById(excludeTagName).value : false);

							for(var i = 0; i < destinations.length; ++i) {
								var destination = destinations[i];
								var dbID = destination.attributes.getNamedItem("dbID").value;
								
								if(dbID != excludeDBID) {
									var destinationLatitude = destination.attributes.getNamedItem("latitude").value;
									var destinationLongitude = destination.attributes.getNamedItem("longitude").value;
									var destinationLatLng = new GLatLng(destinationLatitude, destinationLongitude);

									var distanceFromClick = latLng.distanceFrom(destinationLatLng);
									if((smallestDistance == -1) || (distanceFromClick < smallestDistance)) {
										closestDBID = dbID;
										smallestDistance = distanceFromClick;
									}
								}
							}

							return closestDBID;
						}
					}
				}
			}
		}
	}

	error_log("There was a communications error: " + httpRequest.responseText);
	alert("There was a communications error: " + httpRequest.responseText);
	
	return false;
}

function handleMapClick(overlay, latLng, overlayLatLng) {
	var originalClickState = theMapClickState.getState();
	var startTag = document.getElementById('start');
	var endTag = document.getElementById('end');
	
	var action = false;
	var csAction = 'click';
	if(overlay && overlay.openInfoWindow) {	// Some marker, not the map, not an info window
		// Ask the marker controls whether the start or end was clicked
		switch(theMarkerControls.getClicked(overlayLatLng)) {
			case 'start':
				action = 'sclick';
				csAction = 'sclick';
				break;

			case 'end':
				action = 'eclick';
				csAction = 'eclick';
				break;
				
			case 'neither':
				action = 'mkclick';
				break;
		}
	} else if(overlay) {
		action = 'oclick';					// Not any marker but some overlay; probably info window
	} else {
		action = 'mpclick';					// Map click
	}
	
	theMapClickState.updateState(csAction);
	var latLngToUse = ((overlay == null) ? latLng : overlayLatLng);
	
	if(action != 'oclick') {				// We never do anything about an info window click
		switch(originalClickState) {
			case 'reset':
				var dbID = getNearestCity(latLngToUse, false);
				changeStartEnd(startTag.id, dbID);
				break;
			
			case 'startIsSet':
				switch(action) {
					case 'sclick': 	// Click on the start flag means reset the start flag
						changeStartEnd(startTag.id, 0);
						break;
					
					case 'eclick': 	// Will never happen
						alert('This should not happen--eclick when end flag not set');
						break;

					default: 		// Any other click means set the end flag
						var dbID = getNearestCity(latLngToUse, 'start');
						changeStartEnd(endTag.id, dbID);
						break;
				}
				break;
			
			case 'endIsSet': 	// Means that ONLY the end is set and start is not
				switch(action) {
					case 'eclick': 	// Click on the end flag means reset the end flag
						changeStartEnd(endTag.id, 0);
						break;
				
					case 'sclick': 	// Will never happen
						alert('This should not happen--sclick when start flag not set');
						break;

					default: 		// Any other click means set the start flag
						var dbID = getNearestCity(latLngToUse, 'end');
						changeStartEnd(startTag.id, dbID);
						break;
				}
				break;
		
			case 'bothAreSet':
				switch(action) {
					case 'sclick': 	// Click on the start flag means reset the start flag
						changeStartEnd(startTag.id, 0);
						break;
				
					case 'eclick': 	// Click on the end flag means reset the end flag
						changeStartEnd(endTag.id, 0);
						break;
				}
				break;
		}
	}
}

function handleMarkerDoubleClick(latlng) {
	error_log('mdclick');
}

function load() {
	if(GBrowserIsCompatible()) {
		theMap = new GMap2(document.getElementById("map"));
		theMap.setCenter(new GLatLng(0, 0));
		
		mapListener = GEvent.addListener(theMap, "click", handleMapClick);
		
		theMarkerControls = new cMarkerControls(null, null, handleMarkerDoubleClick);
		
		var llBounds = new GLatLngBounds();
		llBounds.extend(new GLatLng(11.216122,-85.610962));		// Northernmost point
		llBounds.extend(new GLatLng(9.542521,-82.555389));		// East
		llBounds.extend(new GLatLng(8.032969,-82.89691));		// South
		llBounds.extend(new GLatLng(10.887254,-85.951538));		// West
		
		getAllCities();
		for(var i in destinationsArray) {
			destinationsArray[i].showMarker();
		}

		startID = document.getElementById('start').value;
		if(startID != 0) {
			theMarkerControls.setFlags(startID);

			endID = document.getElementById('end').value;
			if(endID != 0) {
				theMarkerControls.setFlags(endID);
			}
		}

		// Zoom such that we can see all of the destinations
		defaultZoomLevel = theMap.getBoundsZoomLevel(llBounds);
		theMap.setZoom(8);

		// Center such that we can see all of the destinations
		var southWest = llBounds.getSouthWest();
		var northEast = llBounds.getNorthEast();
		var lngCenter = Math.min(northEast.lng(), southWest.lng()) + ((northEast.lng() - southWest.lng()) / 2);
		var latCenter = Math.min(northEast.lat(), southWest.lat()) + ((northEast.lat() - southWest.lat()) / 2);
		theMapCenter = new GLatLng(latCenter, lngCenter);
		theMap.setCenter(theMapCenter);

		// Add controls
		theMap.addControl(new GLargeMapControl());
		theMap.addControl(new GMapTypeControl());
		theMap.addMapType(G_PHYSICAL_MAP);
		theMap.setMapType(G_PHYSICAL_MAP);
	}
}
