// Ben Walker 08/10/2009
// http://torchbox.com
// Based on a file by richard.kingston@manchester.ac.uk

/**
 * HNM object keeps everything out of global scope
 * @constructor
 */
function HNM() {
	
	/* Private variables */
	
	var centreLat = 54.4;
	var centreLon =- 4.3;
	var cTileLayerDir = "";
	var currentIndicatorURL = "";
	var currentIndicatorName = "";
	var LAMaps = new Object; // stores geo and zoom data for region select
	var map;
	var mapBounds = new GLatLngBounds(new GLatLng(49.83,-9.32),new GLatLng(60.76,2.69));
	var opacity = 0.75;
	var ie6Overlay = true;
	var p_buttonText="Terrain";
	var physTileLayers;
	var normalTileLayers;
	
	/* Zoom settings */
	
	var minZoomLevel = 4; // furthest zoom (higher value means closer zoom)
	var maxZoomLevel = 12; // closest zoom
	
	var ZOOM_LA = "11"; // level of Local Authority 
	var ZOOM_BELOW_LA = "12"; // below Local Authority
	var maxZoomLevels = {
		"tiles/h1"  : ZOOM_LA,
		"tiles/h2"  : ZOOM_LA,
		"tiles/h3"  : ZOOM_LA,
		"tiles/h4"  : ZOOM_LA,
		"tiles/h5a" : ZOOM_BELOW_LA,
		"tiles/h5b" : ZOOM_BELOW_LA,
		"tiles/h5c" : ZOOM_BELOW_LA,
		"tiles/h5d" : ZOOM_BELOW_LA,
		"tiles/h6"  : ZOOM_LA,
		"tiles/h7"  : ZOOM_LA,
		"tiles/h8"  : ZOOM_BELOW_LA,
		"tiles/h9"  : ZOOM_LA,
		"tiles/h10" : ZOOM_LA,
		"tiles/n1"  : ZOOM_LA,
		"tiles/n2"  : ZOOM_LA,
		"tiles/n3"  : ZOOM_BELOW_LA,
		"tiles/n4"  : ZOOM_BELOW_LA,
		"tiles/n5"  : ZOOM_LA,
		"tiles/n6"  : ZOOM_LA,
		"tiles/n7"  : ZOOM_LA,
		"tiles/n8"  : ZOOM_LA,
		"tiles/n9"  : ZOOM_BELOW_LA,
		"tiles/n10" : ZOOM_LA
	};
	
	/**
	 * Change the opacity of the map legend
	 * @param {String} className class of elements to change
	 */
	function changeLegendOpacity(className) {
		$("." + className).each(function() {
			if (jQuery.support.opacity) {
				$(this).css("opacity", opacity);
			} else {
				var o = opacity * 100;
				this.style.filter = "alpha(opacity='" + o + "')";
			}
		});
	}
	
	/**
	 * Zoom the map to a chosen region
	 * @param {String} laId Local Authority ID
	 */
	function changeLocation(laId) {
		// Pull lat, long and zoom level from array
		panToLocation(LAMaps['LA'+laId][0], LAMaps['LA'+laId][1], LAMaps['LA'+laId][2]);
	}
	
	/**
	 * Change opacity of map tiles dynamically
	 * @param {Int} op new opacity
	 */
	function changeOpacity(op) {
		opacity = op;
		$("#map img[src*='tiles']").css("opacity", op);
	}
	
	/**
	 * Set new map tile opacity then reload map
	 * (slower function for IE6)
	 * @param {String} v CSS visibility ("visible" || "hidden")
	 */
	function changeOpacityInIE6(v) {
		// set new opacity then reload map
		$("#map div[__src__*='tiles']").css("visibility", v);
	}
	
	/**
	 * See if the enter key was pressed
	 * @param {Event} event the event to check (keypress, keydown etc.)
	 */
	function checkForEnter (event) {
		if (event.keyCode == 13) {
			submitPostcode();
			return false;
		}
	}
	
	/**
	 * Converts tile x,y into keyhole string
	 * @param {Int} a horizontal coordinate of map tile
	 * @param {Int} b vertical coordinate of map tile
	 * @returns {String} filename of the overlay image
	 */
	function customGetTileURL(a,b) {
		if (this.baseName === "") {
			return "blank-tile.png";
		}
		var new_b = 17 - b;
		if (b > 16 || new_b > maxZoomLevels[this.baseName]) {
			return this.baseName + "/blank-tile.png";
		}
		var c = Math.pow(2, b);
		var x = 360 / ( c * (a.x+0.0002) - 180 );
		var y = 180 - 360 / c * a.y;
		var x2 = x + 360 / c;
		var y2 = y - 360 / c;
		var lon = x; //Math.toRadians(x); // would be lon = x+lon0, but lon0 = 0 degrees
		var lat = ( 2.0 * Math.atan( Math.exp( y / 180 * Math.PI )) - Math.PI / 2.0 ) * 180 / Math.PI; //in degrees
		var lon2 = x2;
		var lat2 = ( 2.0 * Math.atan( Math.exp( y2 / 180 * Math.PI )) - Math.PI / 2.0 ) * 180 / Math.PI; //in degrees
		var tileBounds = new GLatLngBounds( new GLatLng(lat2,lon), new GLatLng(lat,lon2) );

		if ( !tileBounds.intersects( this.mapBounds ) ) {
			return this.baseName + "/blank-tile.png";
		}
		var z = a.x;
		var d = z + 0.5;
		var e = a.y;
		var f = "t";
		for(var g = 0; g < b; g++) {
			c = c/2;
			if(e < c) {
				if(d < c) {
					f += "q";
				} else {
					f += "r";
					d -= c;
				}
			} else {
				if(d < c) {
					f += "t";
					e -= c;
				} else {
					f += "s";
					d -= c;
					e -= c;
				}
			}
		}
		return this.baseName + "/" + f + ".png";
	}
	
	/**
	 * Resize the map and toolbar if user later changes browser size
	 */
	function handleResize() {
		var content_height = $(window).height() - 50;
		$("#map, #sidebars, .sidebar").height(content_height);
		$(".sidebar-container").height(content_height - 20);
		$(".sidebar .message").height(content_height - 350);
	}
	
	/**
	 * Create map, set click/change handlers etc. Called on construction.
	 */
	function init() {
		if (GBrowserIsCompatible()) {
			
			// Populate LAMaps with region coords and zoom
			loadRegionData();
			
			// Fill the screen, and listen for resize
			handleResize();
			window.onresize = handleResize;
						
			// Initialise sidebar display
			$("#sidebars").wrapInner("<div id=\"sidebars-wrapper\"></div>");
			$("#inner-sidebar .sidebar-container").prepend("<a href=\"#\" class=\"close\">Back</a>");
			if($.browser.msie && $.browser.version.substr(0,1)=="7") { // IE7 chokes on the accordion animation
				$(".accordion").accordion({ autoHeight: false, animated: false });
			} else {
				$(".accordion").accordion({ autoHeight: false });
			}
			$(".accordion ul li:last-child").addClass("last"); // styling hook for pre-CSS3 browsers
			$("#hide-show").hide();
			
			// Postcode search box hint
			$("#postcode").fieldHint();
			
			// Create opacity slider
			// IE6 doesn't support partial tile opacity in Google Maps
			if ($.browser.msie && $.browser.version.substr(0,1)<7) {
				$("#slider div").append("<input type='button' id='transparencyToggle' value='off' />");
				$("#slider label").text("Toggle overlay");
				$("#transparencyToggle").click(function() {
					if (ie6Overlay) {
						$(this).attr("value", "on").removeClass("active");
						ie6Overlay = false;
						changeOpacityInIE6("hidden");
					} else {
						$(this).attr("value", "off").addClass("active");
						ie6Overlay = true;
						changeOpacityInIE6("visible");
					}
				});
			} else { // clever opacity updating
				$("#slider div").slider({
					min: 0,
					max: 100,
					value: (opacity * 100),
					slide: function(event, ui) {
						changeOpacity(ui.value / 100);
						changeLegendOpacity('scale_colour');
					}
				});
			}
			// ...and hide it
			$("#slider").hide();
			
			/* Sidebar controls */
			
			// Hide sidebar
			$("#button-sidebar-hide").click(function() {
				$("body").removeClass("sidebar-left").addClass("nosidebar");
				$("#sidebars").hide();
				$("#nationalSummaryContent").hide();
			});
			// Show sidebar
			$("#button-sidebar-show").click(function() {
				$("body").removeClass("nosidebar").addClass("sidebar-left");
				$("#sidebars").show();
			});
			// Choose an overlay
			$(".indicator-links a").click(function() {
				setCurrentIndicator(
					$(this).text(),
					$(this).attr("href") );
				map.setMapType(map.getCurrentMapType()); //force reload of map
				$("#instructions").hide();
				$("#slider").show();
				$("#hide-show").show();
				return false;
			});
			// Back to overlay menu
			$("#inner-sidebar .close").click(function() {
				$("#sidebars-wrapper").animate(
					{ left: "0" },
					500,
					"",
					function() {
						setDataSource("");
					});
				$("#slider").hide();
				$("#hide-show").hide();
				$(".panel").hide();
				$(".notes-data").remove();
			});
			
			/* Map controls */
			
			// Postcode form submit
			$("#postcode-submit").click(function() {
				submitPostcode();
			});
			if ($.browser.mozilla) {
				$("#postcode").keypress(checkForEnter);
			} else {
				$("#postcode").keydown(checkForEnter);
			}
			// Region select
			$("#locationselect").change(function() {
				 changeLocation($("option:selected", this).attr("value"), 0);
			});
			// Opacity slider
			$("#slider input").click(function() {
				var newOpacity = $(this).attr("value").split("%")[0] / 100;
				changeOpacity(newOpacity);
				changeLegendOpacity('scale_colour');
			});
			
			/* Popups */
			
			// National summary popup
			$("#additional-links a#national-summary").click(function() {
				$(".panel").hide();
				$("#nationalSummaryContent").show();
				return false;
			});
			$("#nationalSummaryContent a.close").click(function() {
				$("#nationalSummaryContent").hide();
			});
			// Notes popup
			$("#additional-links a#notes").click(function() {
				$(".panel").hide();
				$(".notes-data").show();
				return false;
			});
			// Check for cookie, show instructions if first visit
			if ($.cookie('visited_hnm_before') !== "true") {
				$("#instructions").show();
				$("#instructions .close").click(function() {
					$("#instructions").hide();
				});
				$.cookie('visited_hnm_before', 'true', { expires: 180 });
			}
			
			/* Build the map */
			
			// Build copyright notice and put it on the map
			var copyright = new GCopyright(1, new GLatLngBounds(new GLatLng(-90, -180), new GLatLng(90, 180)), 0, "Design by <a href=\"http://torchbox.com\">Torchbox</a> | Developed by <a href=\"http://www.ppgis.manchester.ac.uk/\">CUPS</a> | GMapCreator <a href=\"http://www.casa.ucl.ac.uk\" >CASA</a>");
			var copyrightCollection = new GCopyrightCollection("");
			copyrightCollection.addCopyright(copyright);

			//create a custom G_PHYSICAL_MAP layer
			/*
			physTileLayers = [ G_PHYSICAL_MAP.getTileLayers()[0], new GTileLayer(copyrightCollection, 0, 17)];
			physTileLayers[1].getTileUrl = customGetTileURL;
			physTileLayers[1].isPng = function() { return true; };
			physTileLayers[1].getOpacity = function() { return opacity; };
			physTileLayers[1].baseName = cTileLayerDir;
			physTileLayers[1].mapBounds = mapBounds;
			var physCustomMap = new GMapType(
				physTileLayers,
				new GMercatorProjection(18),
				p_buttonText,
				{ errorMessage: "Data not available" }
			);
			physCustomMap.getMaximumResolution = function(latlng){ return maxZoomLevel; };
			physCustomMap.getMinimumResolution = function(latlng){ return minZoomLevel; };
			*/
			
			//create a custom G_NORMAL_MAP layer
			normalTileLayers = [ G_NORMAL_MAP.getTileLayers()[0], new GTileLayer(copyrightCollection, 0, 17)];
			normalTileLayers[1].getTileUrl = customGetTileURL;
			normalTileLayers[1].isPng = function() { return true; };
			normalTileLayers[1].getOpacity = function() { return opacity; };
			normalTileLayers[1].baseName = cTileLayerDir;
			normalTileLayers[1].mapBounds = mapBounds;
			var normalCustomMap = new GMapType(
				normalTileLayers,
				new GMercatorProjection(18),
				p_buttonText,
				{ errorMessage: "Data not available" }
			);
			normalCustomMap.getMaximumResolution = function(latlng){ return maxZoomLevel; };
			normalCustomMap.getMinimumResolution = function(latlng){ return minZoomLevel; };
			
			// Display the map, with some controls and set the initial location
			map = new GMap2(document.getElementById("map"), { mapTypes:[normalCustomMap]});
			map.addControl(new GLargeMapControl3D());
			map.addControl(new GOverviewMapControl());
			map.enableDoubleClickZoom();
			map.enableContinuousZoom();
			map.enableScrollWheelZoom();
			map.setCenter(new GLatLng(centreLat, centreLon),map.getBoundsZoomLevel(mapBounds),normalCustomMap);
			GEvent.addListener(map,"load", setCenterToPoint);
			map.setZoom(6);
			
		} else {
			// display a warning if the browser was not compatible
			alert("Sorry, the Google Maps API is not compatible with this browser");
		}
	}
	
	/**
	 * Load a new overlay and related text content
	 * Set currentIndicatorURL before calling
	 */
	function loadContent() {
		// Load the map overlay
		setDataSource(currentIndicatorURL);
		// Load the text content
		writeLegend(currentIndicatorURL);
		// Load the national summary content
		writeSummary(currentIndicatorURL);
	}
	
	/**
	 * Populate the LAMaps object with region data
	 */
	function loadRegionData() {
		// Central Lat-Long location and zoom level of Countries for zooming
		LAMaps.LA0 = new Array(54.1,-3.4, 5);					// Whole of the UK
		LAMaps.LA1 = new Array(52.850059,-1.332092,8);			// England - East Midlands
		LAMaps.LA2 = new Array(53.14152745,-1.614932954,9);		// Derbyshire
		LAMaps.LA3 = new Array(53.13312454,-1.006759541,9);		// Nottinghamshire
		LAMaps.LA4 = new Array(53.11183143,-0.237522539,9);		// Lincolnshire 
		LAMaps.LA5 = new Array(52.66754873,-1.11378283,9);		// Leicestershire
		LAMaps.LA6 = new Array(52.30743892,-0.8657738,9);		// Northamptonshire
		LAMaps.LA7 = new Array(52.269628,0.108833,8);			// England - East of England
		LAMaps.LA8 = new Array(51.80271899,0.581720119,9);		// Essex 
		LAMaps.LA9 = new Array(51.82961736,-0.217365832,9);		// Hertfordshire
		LAMaps.LA10 = new Array(52.07954709,-0.45253912,9);		// Bedfordshire
		LAMaps.LA11 = new Array(52.33947389,0.033718606,9);		// Cambridgeshire
		LAMaps.LA12 = new Array(52.6751718,0.947753889,9);		// Norfolk 
		LAMaps.LA13 = new Array(52.2190856,1.063047403,9);		// Suffolk 
		LAMaps.LA14 = new Array(55.229023,-1.933594,8);			// England - North East
		LAMaps.LA15 = new Array(54.69547638,-1.839890784,9);	// County Durham 
		LAMaps.LA16 = new Array(55.24140137,-2.066374451,8);	// Northumberland
		LAMaps.LA17 = new Array(54.56276057,-1.328964633,10);	// Teeside
		LAMaps.LA18 = new Array(54.9543761,-1.564383515,10);	// Tyne and Wear
		LAMaps.LA19 = new Array(54.134031,-2.758255,7);			// England - North West
		LAMaps.LA20 = new Array(53.18929172,-2.522261398,9);	// Cheshire 
		LAMaps.LA21 = new Array(54.57578374,-2.911730875,9);	// Cumbria 
		LAMaps.LA22 = new Array(53.51353158,-2.231375433,10);	// Greater Manchester
		LAMaps.LA23 = new Array(53.85003142,-2.63173319,9);		// Lancashire 
		LAMaps.LA24 = new Array(53.45880454,-2.887187265,10);	// Merseyside
		LAMaps.LA25 = new Array(51.248002,-0.562363,8);			// England - South East
		LAMaps.LA26 = new Array(51.45292595,-0.983822363,9);	// Berkshire 
		LAMaps.LA27 = new Array(51.78491787,-0.81156246,9);		// Buckinghamshire
		LAMaps.LA28 = new Array(50.94136177,0.27637272,9);		// East Sussex
		LAMaps.LA29 = new Array(50.95366887,-1.287775317,9);	// Hampshire
		LAMaps.LA30 = new Array(51.22284489,0.735690364,9);		// Kent 
		LAMaps.LA31 = new Array(51.50044463,-0.109300059,10);	// Greater London 
		LAMaps.LA32 = new Array(51.76536422,-1.321976786,9);	// Oxfordshire
		LAMaps.LA33 = new Array(51.24786706,-0.420568815,9);	// Surrey 
		LAMaps.LA34 = new Array(50.95529886,-0.49552717,9);		// West Sussex 
		LAMaps.LA35 = new Array(50.737107,-3.533478,7);			// England - South West
		LAMaps.LA36 = new Array(51.10167671,-2.942152112,9);	// Somerset 
		LAMaps.LA37 = new Array(51.82546093,-2.126893194,9);	// Gloucestershire
		LAMaps.LA38 = new Array(51.29706216,-1.94462543,9);		// Wiltshire
		LAMaps.LA39 = new Array(50.80541958,-2.322622673,9);	// Dorest
		LAMaps.LA40 = new Array(50.76039875,-3.810693069,9);	// Devon 
		LAMaps.LA41 = new Array(50.40551475,-4.827449022,8);	// Cornwall 
		LAMaps.LA42 = new Array(52.522906,-1.793518,8);			// England - West Midlands
		LAMaps.LA43 = new Array(52.10344475,-2.744991818,9);	// Herefordshire
		LAMaps.LA44 = new Array(52.63487734,-2.734081771,9);	// Shropshire
		LAMaps.LA45 = new Array(52.85142969,-2.032264198,9);	// Staffordshire
		LAMaps.LA46 = new Array(52.30020093,-1.559727234,9);	// Warwickshire
		LAMaps.LA47 = new Array(52.48834445,-1.874240898,9);	// West Midlands 
		LAMaps.LA48 = new Array(52.22351272,-2.16905697,9);		// Worcestershire
		LAMaps.LA49 = new Array(53.848711,-1.087646,8);			// England - Yorkshire &amp; Humber
		LAMaps.LA50 = new Array(53.48446455,-1.331436631,9);	// South Yorkshire 
		LAMaps.LA51 = new Array(53.74309439,-1.688882638,9);	// West Yorkshire 
		LAMaps.LA52 = new Array(54.19064532,-1.418773546,9);	// North Yorkshire 
		LAMaps.LA53 = new Array(53.86768979,-0.482066073,9);	// East Yorkshire &amp; Humberside 
		LAMaps.LA54 = new Array(54.622978,-6.663208,8);			// N. Ireland
		// LAMaps.LA55 = new Array(58.031372, -3.020117,6);		// Scotland
		LAMaps.LA55 = new Array(56.824933,-4.801025,7);			// Scotland
		LAMaps.LA56 = new Array(52.47635,-3.82616,8);			// Wales
		LAMaps.LA57 = new Array(52.789476,-2.537842,7);			// England
	}
	
	/**
	 * Load and show the national summary panel for the current overlay
	 * @param {String} iframeName ID/name of an existing <iframe>
	 * @param {String} file filename to load
	 */
	function nationalSummary(iframeName,file) {
		$(".notes-data").hide();
		$("#" + iframeName).show();
		frames[iframeName].location.href = file;
	}
	
	/**
	 * Pan and zoom in to a particular district
	 * @param {Float} lat destination latitude
	 * @param {Float} lng destination longitude
	 * @param {Int} distZoom zoom level
	 */
	function panToLocation(lat, lng, distZoom) {
		map.setCenter(new GLatLng(parseFloat(lat), parseFloat(lng)), parseInt(distZoom, 10));
	}
	
	/**
	 * Callback function to center map on a point
	 * @param {GLatLng} destination
	 */
	function setCenterToPoint(point) { 
		map.setCenter(point, 10);
	}
	
	/**
	 * Load a new overlay
	 * @param {String} name overlay title
	 * @param {String} url directory of map tiles
	 */
	function setCurrentIndicator(name, url) {
		currentIndicatorName = name;
		currentIndicatorURL = url;
		loadContent();
	}
	
	/**
	 * Remove the existing overlay and load the new one
	 * @param {String} baseName name of the new overlay or "" to just remove
	 */
	function setDataSource(baseName) {
		var old_pmapType = map.getMapTypes()[0];
		if (baseName !== "") {
			if (map.getZoom() > maxZoomLevels[baseName]) {
				map.setZoom(maxZoomLevels[baseName]);
			}
			normalTileLayers[1].baseName = baseName;
			normalTileLayers[1].mapBounds = mapBounds;
			var p_customMap = new GMapType(
				normalTileLayers,
				new GMercatorProjection(18),
				p_buttonText,
				{
					errorMessage: "Data not available"
				}
			);
			p_customMap.getMaximumResolution = function(latlng){ return maxZoomLevels[baseName]; };
			p_customMap.getMinimumResolution = function(latlng){ return minZoomLevel; };
			map.addMapType(p_customMap);
		}
		map.removeMapType(old_pmapType); // fires update of zoom controls, so needs to go last
	}
	
	/**
	 * If there's a real value in the postcode box, do something with it
	 * (#postcode[alt] contains the default text, "Town / place")
	 */
	function submitPostcode() {
		var postcode = $("#postcode").attr("value");
		var alt = $("#postcode").attr("alt");
		if (postcode != alt) {
			usePointFromPostcode(postcode, setCenterToPoint );
		}
	}
	
	/**
	 * Use Google Local search for postcode lookup
	 * @param {String} postcode
	 * @callbackFunction {Function} (probably setCenterToPoint())
	 */
	function usePointFromPostcode(postcode, callbackFunction) {
		var localSearch = new GlocalSearch();
		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);
				callbackFunction(point);
			} else {
				alert("Postcode not found!");
			}
		});
		localSearch.execute(postcode + ", UK");
	}
	
	/**
	 * Load the content for an overlay from text file and build the sidebar
	 * @param {String} file directory and stub of the filename (no extension)
	 */
	function writeLegend(file) {
		if (file === "" || file === "tiles/" || file === "legend") {
			return;
		} else {
			var process_legend = function(legend_file) {
				$("#sidebar-content").html(legend_file);
				changeLegendOpacity('scale_colour');
				handleResize();
				$(".notes-data").wrapInner('<div class="wrapper"></div>').append('<a class="close">close</a>').addClass("panel").appendTo("#content");
				$(".notes-data a.close").click(function() {
					$(".notes-data").hide();
				});
				$("#sidebars-wrapper").animate({ left: "-310px" }, 500);
			};
			var filename = "key/" + file + ".txt";
			GDownloadUrl(filename, process_legend);
		}
	}
	
	/**
	 * Load the national summary content from a HTML file
	 * @param {String} file directory and stub of the filename (no extension)
	 */
	function writeSummary(file) {
		if (file === "" || file === "tiles/" || file === "legend") {
			return;
		} else {
			var processSummary = function(summary_file) {
				$("#nationalSummaryContent").children().not("a.close").remove();
				var summary_body = summary_file.split(/<\/?body[^>]*>/)[1];
				$(summary_body).prependTo("#nationalSummaryContent");
			};
			var filename = 'key/' + currentIndicatorURL + '_summary.html';
			GDownloadUrl(filename, processSummary);
		}
	}
	
	/*
	 * jQuery helper function: sort out hints in text inputs
	 */
	$.fn.fieldHint = function() {
		this.each(function() {
			$(this).focus(function(){
				if($(this).val() == $(this).attr("alt")) {
					$(this).val("");
				}
			}).blur(function() {
				if($(this).val() == $(this).attr("alt") || !$(this).val().length) {
					$(this).val($(this).attr("alt"));
				}
			}).blur();
		});
		return this;
	};
	
	/* Stuff to do on page load */
	
	$(function() {
		init();
	});
}

// Create an instance of the HNM object
var hnm = new HNM();
