var map;
var geocoder = null;
var markerOptions = null;
var lastGeoResp = null;
var keyboardHandler = null;
var rootCategoryIds = new Array();
var experienceTags = new Hash();
var destinationTags = new Hash();
var pointMarkers = new Hash();
var activeMarkers = new Array();
var destinationMarkerGroups = new Hash();
var experienceMarkerGroups = new Hash();
var agentPopupHtml;
var nearestPopupHtml;
var tagJson;
var pointJson;

if (typeof agentExternalUrlRoot == 'undefined') {
	agentExternalUrlRoot = '';
}

if (typeof zoom == 'undefined') {
	zoom = 4;
}
if (typeof centerLat == 'undefined') {
	centerLat = -27.804398;
}
if (typeof centerLong == 'undefined') {
	centerLong = 133.775136;
}

if (typeof minZoom == 'undefined') {
	minZoom = zoom;
}

// enforce minZoom
G_NORMAL_MAP.getMinimumResolution = function () { return minZoom };

// enforce maxZoom
if (typeof maxZoom != 'undefined') {
	G_NORMAL_MAP.getMaximumResolution = function () { return maxZoom };
}	

//---------------------------------------
// Data loading and initialization
//---------------------------------------

function map_init() {

	if (GBrowserIsCompatible() && pointDataKey && tagDataKey) {
		
		geocoder = new GClientGeocoder();
		map = new GMap2(document.getElementById("map_canvas"));
		map.addControl(new GLargeMapControl());
		map.setCenter(new GLatLng(centerLat, centerLong), zoom);
		GEvent.addListener(map, "click", mapClickListener);
		keyboardHandler = new GKeyboardHandler(map);
		initMarkerOptions();

		// load tag data
		loadJs(getSpreadsheetUrl(tagDataKey, "loadTags"));

		// load agent popup template
		if (typeof agentPopupUrl != 'undefined') {
		
			var agentPopupRequest = GXmlHttp.create();
			agentPopupRequest.open("GET", agentPopupUrl, true);
			agentPopupRequest.onreadystatechange = function() {

				if (agentPopupRequest.readyState == 4) {

					agentPopupHtml = agentPopupRequest.responseText;

				}

			}
			agentPopupRequest.send(null);

		}
		
		// load nearest popup template
		if (typeof nearestPopupUrl != 'undefined') {
		
			var nearestPopupRequest = GXmlHttp.create();
			nearestPopupRequest.open("GET", nearestPopupUrl, true);
			nearestPopupRequest.onreadystatechange = function() {

				if (nearestPopupRequest.readyState == 4) {

					nearestPopupHtml = nearestPopupRequest.responseText;

					var passedPostcode = getUrlParam('postcode');
					if (passedPostcode && passedPostcode != '') {
						var postcodeEl = document.getElementById('map_postcode');
						if (postcodeEl != null && typeof postcodeEl != undefined) {
							postcodeEl.value = passedPostcode;
							recenterMap();
						}
					}

				}

			}
			nearestPopupRequest.send(null);

		}
	
	}

}

function initMarkerOptions() {

	var markerIcon = new GIcon(G_DEFAULT_ICON);
	if (typeof pathToMarker != 'undefined') {
		markerIcon.image = pathToMarker;
	}
	if (typeof markerWidth != 'undefined' || typeof markerHeight != 'undefined') {	
		markerIcon.iconSize = new GSize(markerWidth, markerHeight);
		markerIcon.iconAnchor = new GPoint(markerWidth/2,markerHeight);
		markerIcon.infoWindowAnchor = new GPoint(markerWidth/2, markerHeight);
	}
	if (typeof pathToShadow != 'undefined') {
		markerIcon.shadow = pathToShadow;
	}
	if (typeof shadowWidth != 'undefined' || typeof shadowHeight != 'undefined') {	
		markerIcon.shadowSize = new GSize(shadowWidth, shadowHeight);
	}	
	markerOptions = { icon: markerIcon };

}

function getSpreadsheetUrl(key, callback) {

	return "http://spreadsheets.google.com/feeds/list/" + key 
		+ "/od6/public/values?alt=json-in-script&callback=" + callback;

}

function loadTags(tagJson) {

	for (var i = 0; i < tagJson.feed.entry.length; i++) {
	
		var tagid = tagJson.feed.entry[i].gsx$id.$t;
		var tagtype = tagJson.feed.entry[i].gsx$type.$t;
		var taglist = null;
		
		if (tagtype == 'Experience') {
			taglist = experienceTags;
		} else if (tagtype == 'Destination') {
			taglist = destinationTags;
		}

		if (taglist != null) {
			taglist.setItem(tagid, {
				id: tagid,
				name: tagJson.feed.entry[i].gsx$name.$t
			});
		}
			
	}
	
	// update select boxes with content in the two taglists
	populateSelectBox('map_destinations', destinationTags);
	populateSelectBox('map_experiences', experienceTags);

	loadJs(getSpreadsheetUrl(pointDataKey, "loadPoints"));


}

function populateSelectBox(selectId, taglist) {

	var sel = document.getElementById(selectId);
	
	if (sel && typeof sel != 'undefined') {
		for (var i = 0; i < taglist.items.length; i++) {
			var opt = document.createElement("OPTION");
			try {
			opt.text = taglist.items[i].name;
			opt.value = taglist.items[i].id;
			sel.options.add(opt);
			} catch (e) {}
		}
	}

}

function loadPoints(pointJson) {

	for (var i = 0; i < pointJson.feed.entry.length; i++) {

		var point = getPoint(pointJson.feed.entry[i]);	
		if (point != null) {
			var marker = new GMarker(point, markerOptions);
			marker.json = pointJson.feed.entry[i];
			addToTags(marker);
			var id = pointJson.feed.entry[i].gsx$id.$t;
			pointMarkers.setItem(id, marker);
		}

	}

	drawMarkers();

}

function addToTags(marker) {

	var destinationField = marker.json.gsx$destination.$t;

	if (destinationField.length > 0) {
		
		var destinations = destinationField.split(",");

		for (var i = 0; i < destinations.length; i++) {

			var cleanTagId = destinations[i].replace(/^\s+|\s+$/g, '');
			addMarkerToTag(marker, cleanTagId, destinationMarkerGroups);

		}

	}
	
	var experienceField = marker.json.gsx$experience.$t;
	
	if (experienceField.length > 0) {
		
		var experiences = experienceField.split(",");

		for (var i = 0; i < experiences.length; i++) {

			var cleanTagId = experiences[i].replace(/^\s+|\s+$/g, '');
			addMarkerToTag(marker, cleanTagId, experienceMarkerGroups);

		}

	}

}

function addMarkerToTag(marker, tagid, tagmarkergroups) {

	var markerArray;

	if (tagmarkergroups.hasItem(tagid)) {

		markerArray = tagmarkergroups.getItem(tagid);

	} else {

		markerArray = new Array();

	}

	markerArray.push(marker);
	tagmarkergroups.setItem(tagid, markerArray);

}

function getPoint(jsonEntry) {

	var lat = parseFloat(jsonEntry.gsx$latitude.$t);
	var lng = parseFloat(jsonEntry.gsx$longitude.$t);

	if (isNaN(lat) || isNaN(lng)) {
		return null;
	} else {
		return new GLatLng(lat, lng);
	}

}

function drawMarkers() {

	map.clearOverlays();
	
	var destinationsSpecified = typeof activeDestinations != 'undefined' &&
		activeDestinations.length > 0 && activeDestinations[0] != 'all';
		
	var experiencesSpecified = typeof activeExperiences != 'undefined' &&
		activeExperiences.length > 0 && activeExperiences[0] != 'all';
		 
	if (!destinationsSpecified && !experiencesSpecified) {
	
		activeMarkers = pointMarkers.toArray();

	} else {
	
		destinationMarkerArray = new Array();

		if (destinationsSpecified) {
			for (var i = 0; i < activeDestinations.length; i++) {
				// get markers for each active destination
				var aMarkerArray = destinationMarkerGroups.getItem(activeDestinations[i]);
				if (typeof aMarkerArray != 'undefined') {
					destinationMarkerArray = destinationMarkerArray.concat(aMarkerArray);
				}
			}
		}
		
		experienceMarkerArray = new Array();

		if (experiencesSpecified) {
			for (var i = 0; i < activeExperiences.length; i++) {
				// get markers for each active experience
				var aMarkerArray = experienceMarkerGroups.getItem(activeExperiences[i]);
				if (typeof aMarkerArray != 'undefined') {
					experienceMarkerArray = experienceMarkerArray.concat(aMarkerArray);
				}
			}
		}		
		
		if (destinationMarkerArray.length > 0 && experienceMarkerArray.length == 0) {
			activeMarkers = destinationMarkerArray;
		} else if (destinationMarkerArray.length == 0 && experienceMarkerArray.length > 0) {
			activeMarkers = experienceMarkerArray;
		} else if (destinationMarkerArray.length > 0 && experienceMarkerArray.length > 0) {
			// activeMarkers == UNION of both arrays
			activeMarkers = new Array();
			for (var i = 0; i < destinationMarkerArray.length; i++) {
				try {
					destId = destinationMarkerArray[i].json.gsx$id.$t;
					for (var j = 0; j < experienceMarkerArray.length; j++) {
						if (experienceMarkerArray[i].json.gsx$id.$t == destId) {
							activeMarkers.push(destinationMarkerArray[i]);
						}
					}
				} catch (e) {}
			}	
		} else {
			activeMarkers = pointMarkers.toArray();
		}
		
	}
	
	for (var i = 0; i < activeMarkers.length; i++) {
		try {
			map.addOverlay(activeMarkers[i]);

		} catch (e) {  }
	}

}

//---------------------------------------
// Form interaction
//---------------------------------------

function resetMap() {

	resetDestinations();
	
	resetExperiences();
	
	resetPostcode();
	
	drawMarkers();

}

function resetDestinations() {

	var destSelect = document.getElementById('map_destinations');
	destSelect.selectedIndex = 0;
	activeDestinations = new Array(destSelect.options[0].value);
	
}

function resetExperiences() {

	var expSelect = document.getElementById('map_experiences');
	expSelect.selectedIndex = 0;
	activeExperiences = new Array(expSelect.options[0].value);
	
}

function resetPostcode() {

	var postcode = document.getElementById('map_postcode');
	postcode.value = '';
	
	var messageDiv = document.getElementById('map_message');
	messageDiv.innerHTML = '';
	
	map.setCenter(new GLatLng(centerLat, centerLong), zoom);
	
}

function updateDestinations() {

	var select = document.getElementById('map_destinations');
	activeDestinations = new Array();
	activeDestinations.push(select.options[select.selectedIndex].value);
	
	resetExperiences();

	drawMarkers();

}

function updateExperiences() {

	var select = document.getElementById('map_experiences');
	activeExperiences = new Array();
	activeExperiences.push(select.options[select.selectedIndex].value);
	
	resetDestinations();

	drawMarkers();

}

function recenterMap() {

	var postcode = document.getElementById('map_postcode');
	if (postcode != null && typeof postcode != undefined) {
		var paddedPostcode = postcode.value;
		while (paddedPostcode.length < 4) {
			paddedPostcode = "0" + paddedPostcode;
		}
		centerMap(paddedPostcode);
	}

}

function showPostcodeError() {
	alert("Please check your postcode");
}

function centerMap(addr, onCompleteFunc) {

	if (geocoder) {

		geocoder.getLocations(
			addr + ' AU',
			function(resp) {
				
				lastGeoResp = resp;
				var pm = null;
				try {
					pm = resp.Placemark[0];
				} catch (e) {
					showPostcodeError();
					return;
				}
				
				if (pm != null) {
				
					try {
						var foundPostcode = pm.AddressDetails.Country.PostalCode.PostalCodeNumber;
						if (foundPostcode != addr) {
							showPostcodeError();
							return;
						} else {

							var foundPoint = new GLatLng(pm.Point.coordinates[1], pm.Point.coordinates[0]);

							var closestPoints = getClosestMarkers(foundPoint, 3);

							var bounds = new GLatLngBounds();

							bounds.extend(foundPoint);

							for (var i = 0; i < closestPoints.length; i++) {

								bounds.extend(closestPoints[i].marker.getLatLng());

							}
							var newZoom = map.getBoundsZoomLevel(bounds)-1;

							if (newZoom < zoom) {

								newZoom = zoom;

							}
							window.setTimeout(map.setCenter(bounds.getCenter(), newZoom), 2000);

							//map.setCenter(bounds.getCenter(), newZoom);

							window.setTimeout(showNearest(addr, closestPoints), 2000);
							
							//showNearest(addr, closestPoints);
												
			
							
						}
					} catch (e) {
						//showPostcodeError();
						return;
					}
					
				} else {
				
					//showPostcodeError();
					return;
					
				}
				
				if (onCompleteFunc) {
					onCompleteFunc();
				}
				
			}
		);
   

    }

}

function getClosestMarkers(point, numMarkers) {

	var distances = new Array()

	var dist;

	for (var i = 0; i < activeMarkers.length; i++) {

		try {
			dist = point.distanceFrom(activeMarkers[i].getLatLng());
			distances.push({ distance: dist, marker: activeMarkers[i] });
		} catch (e) { }
	
	}

	distances.sort(function(a,b) {
		return a.distance - b.distance;
	});

	if (distances.length > numMarkers) {
		distances.length = numMarkers;
	}
	
	return distances;

}

//---------------------------------------
// Create and manage popups
//---------------------------------------

function mapClickListener(overlay, point) {

	if (overlay) {

		if (overlay.json) {

			map.closeInfoWindow();
			overlay.openInfoWindowHtml(parseMasks(agentPopupHtml, overlay, ''), getPopupOpts());
	
		}

	}

}

function showNearest(postcode, nearestAgentMarkers) {

	if (nearestAgentMarkers && nearestAgentMarkers.length > 1) {
	
		var messageDiv = document.getElementById('map_message');
		messageDiv.innerHTML = parseNearestMasks(nearestPopupHtml, postcode, nearestAgentMarkers);
	}
	
}

function getPopupOpts() {

	var width = map.getSize().width;
	width = width * .8;
	
	return { maxWidth: width }
	
}		

function parseMasks(htmlStr, point, fieldSuffix) {

	var parsedStr = removeComments(htmlStr);
	try {
	parsedStr = parseMask(parsedStr, "Id" + fieldSuffix, point.json.gsx$id.$t);
	parsedStr = parseMask(parsedStr, "Name" + fieldSuffix, point.json.gsx$name.$t);
	parsedStr = parseMask(parsedStr, "BusinessName" + fieldSuffix, point.json.gsx$businessname.$t);
	parsedStr = parseMask(parsedStr, "AddressLine1" + fieldSuffix, point.json.gsx$addressline1.$t);
	parsedStr = parseMask(parsedStr, "AddressLine2" + fieldSuffix, point.json.gsx$addressline2.$t);
	parsedStr = parseMask(parsedStr, "Suburb" + fieldSuffix, point.json.gsx$suburb.$t);
	parsedStr = parseMask(parsedStr, "City" + fieldSuffix, point.json.gsx$city.$t);
	parsedStr = parseMask(parsedStr, "State" + fieldSuffix, point.json.gsx$state.$t);
	parsedStr = parseMask(parsedStr, "Postcode" + fieldSuffix, point.json.gsx$postcode.$t);
	parsedStr = parseMask(parsedStr, "InternalUrl" + fieldSuffix, point.json.gsx$internalurl.$t);
	parsedStr = parseMask(parsedStr, "ExternalUrl" + fieldSuffix, agentExternalUrlRoot + point.json.gsx$externalurl.$t);
	parsedStr = parseMask(parsedStr, "ShortDescription" + fieldSuffix, point.json.gsx$shortdescription.$t);
	var ToRegExp = new RegExp("<ToHere" + fieldSuffix + ">", "g");
	var FromRegExp = new RegExp("<FromHere" + fieldSuffix + ">", "g");
	parsedStr = parsedStr.replace(ToRegExp, getToDirLink(point));
	parsedStr = parsedStr.replace(FromRegExp, getFromDirLink(point));
	} catch (e) { }
	return parsedStr;

}

function parseNearestMasks(htmlStr, postcode, distancemarkers) {

	var parsedStr = htmlStr;
	
	parsedStr = parseMask(parsedStr, "Postcode", postcode); 
	
	for (var i = 0; i < distancemarkers.length; i++) {
		parsedStr = parseMasks(parsedStr, distancemarkers[i].marker, i + 1);
		parsedStr = parseMask(parsedStr, "Distance" + (i+1), 
			(distancemarkers[i].distance/1000).toFixed(1));
	}
	
	return parsedStr;
	
}

function removeComments(htmlStr) {

	var commentRegexp = new RegExp("<!--(.|[\\n\\r])*-->", "gi");
	return htmlStr.replace(commentRegexp, '');

}

function parseMask(htmlStr, mask, value) {

	var parsedStr = htmlStr;
	
	if (typeof value == 'string') {
		value = value.trim();
	}
	
	var maskRegexp = new RegExp("<" + mask + ">", "gi");

	if (value != null && value != '') {

		// replace mask with value
		parsedStr = parsedStr.replace(maskRegexp, value);
	
		// find any "IfNoMask" blocks and remove them
		var ifNoBlockRegexp = new RegExp("<IfNo" + mask + ">(.|[\\n\\r])*?</IfNo" + mask + ">", "gi");
		parsedStr = parsedStr.replace(ifNoBlockRegexp, '');

		// remove "<IfMask>" and "</IfMask>"
		var ifRegexp = new RegExp("</?If" + mask + ">", "gi");
		parsedStr = parsedStr.replace(ifRegexp, '');

	} else {

		// replace mask with blank
		parsedStr = parsedStr.replace(maskRegexp, '');

		// find any "IfMask" blocks and remove them
		var ifBlockRegexp = new RegExp("<If" + mask + ">(.|[\\n\\r])*?</If" + mask + ">", "gi");
		parsedStr = parsedStr.replace(ifBlockRegexp, '');

		// remove "<IfNoMask>" and "</IfNoMask"
		var ifNoRegexp = new RegExp("</?IfNo" + mask + ">", "gi");
		parsedStr = parsedStr.replace(ifNoRegexp, '');

	}

	return parsedStr;

}

function getToDirLink(point) {

	return "http://maps.google.com/maps?q=to+" + point.getLatLng().lat() + "," + point.getLatLng().lng();

}

function getFromDirLink(point) {

	return "http://maps.google.com/maps?q=from+" + point.getLatLng().lat() + "," + point.getLatLng().lng();

}


//---------------------------------------
// Utility functions and classes
//---------------------------------------

function loadJs(src) {

	var docHead = document.getElementsByTagName('head').item(0);
	var newJS = document.createElement('script');
	newJS.setAttribute('language','javascript');
	newJS.setAttribute('type','text/javascript');
	newJS.setAttribute('src',src);
	docHead.appendChild(newJS);

}

function getUrlParam(name) {

	var query = window.location.search.substring(1);

	var vars = query.split("&");

	for (var i = 0; i < vars.length; i++) {
		var pair = vars[i].split("=");
		if(pair[0]==name) {
			return pair[1];
		}
	}

	return "";

}

function Hash() {

	this.length = 0;
	this.items = new Array();
	for (var i = 0; i < arguments.length; i += 2) {
		if (typeof(arguments[i + 1]) != 'undefined') {
			this.items[arguments[i]] = arguments[i + 1];
			this.length++;
		}
	}
   
	this.removeItem = function(in_key) {
		var tmp_value;
		if (typeof(this.items[in_key]) != 'undefined') {
			this.length--;
			var tmp_value = this.items[in_key];
			delete this.items[in_key];
		}
	   
		return tmp_value;
	}

	this.getItem = function(in_key) {
		return this.items[in_key];
	}

	this.setItem = function(in_key, in_value) {
		if (typeof(in_value) != 'undefined') {
			if (typeof(this.items[in_key]) == 'undefined') {
				this.length++;
			} 

			this.items[in_key] = in_value;
		} 
	   
		return in_value;
	}

	this.hasItem = function(in_key) {
		return typeof(this.items[in_key]) != 'undefined';
	}
	
	this.toArray = function() {
		var ret = new Array();
		var i = 0;
		for(key in this.items) {
			i++;
			ret.push(this.items[key]);
		}
		return ret;
	}

}

if (!String.prototype.lTrim) {
    String.prototype.lTrim = function() { return this.replace(/^\s*/, ''); }
}
if (!String.prototype.rTrim) {
    String.prototype.rTrim = function() { return this.replace(/\s*$/, ''); }
}
if (!String.prototype.trim) {
    String.prototype.trim = function() { return this.lTrim().rTrim(); }
}

//---------------------------------------
// Load the map
//---------------------------------------

GEvent.addDomListener(window, "load", map_init);
GEvent.addDomListener(window, "unload", GUnload);

