// Load library code
google.load("maps", "2");
google.load("jquery", "1.2.6");

// Variables set in the HTML
var wcil_location;
var mapInitLat;
var mapInitLng; 
var mapInitZoom;
var cloudMadeMapURL;
var cloudMadeAPIKey;
var cloudMadeStyle;
var searchStation;
var results;
var searchStation;
var searchParams;

// Other variables
var map;
var selectedResult = -1;

// Initialisation
window.onload = function()
{
	// Open some links and forms in new windows
	$("#journey-planner form, a[rel*=external]").each(function()
	{
		this.target = "_blank";
	});
	
    // Set up station auto-complete
	var oDS = new YAHOO.util.FunctionDataSource(function(q)
	{
		var query = q.toLowerCase();
		var matches = [];

		for(var i = 0; i < stations.length; i++)
		{
			if(stations[i].toLowerCase().indexOf(query) > -1)
				matches[matches.length] = stations[i];
		}

		return matches;
	});

    var oAC = new YAHOO.widget.AutoComplete("search-station-input", "station-autocomplete-options", oDS);
	//oAC.forceSelection = true; // Causes bad input to be cleared onblur, which causes issues when onblur is triggered by clicking submit
	oAC.allowBrowserAutocomplete = false;
    oAC.prehighlightClassName = "yui-ac-prehighlight";
    oAC.useShadow = true;

	// For IE, use an iFrame to display the results
	// Gets around the autocomplete dropdown appearing behind other form elements
	if($("body").hasClass("ie"))
		oAC.useIFrame = true;

	// Check the browser is Google Maps compatible
	if(!GBrowserIsCompatible()){
		return;
	}

	// Instantiate map extensions object
	var mapExtensions = new WhereCanILiveMapExtensions();
	
	// Set default map types
	var mapOptions = {mapTypes:[
		mapExtensions.cloudMadeMapType()
	]};
	
	// Create the Google map
	map = new GMap2(document.getElementById("map"), mapOptions);
	map.setCenter(new GLatLng(mapInitLat, mapInitLng), mapInitZoom);
	map.enableScrollWheelZoom();
       
	// Add controls to the map
	$("#controls").append($("<button>zoom +</button>").attr("title", "Zoom in").click(function() {map.zoomIn();}));
	$("#controls").append($("<button>zoom -</button>").attr("title", "Zoom out").click(function() {map.zoomOut();}));

	if(searchStation)
	{
		// Mark the station the user searched for
		var searchIcon = new GIcon(G_DEFAULT_ICON, "http://maps.google.com/mapfiles/arrow.png");
		searchIcon.iconSize = new GSize(39, 34);
		searchIcon.printImage = "http://maps.google.com/mapfiles/arrow.png";
		searchIcon.shadow = "http://maps.google.com/mapfiles/arrowshadow.png";
		searchIcon.printShadow = "http://maps.google.com/mapfiles/arrowshadow.png";
		searchIcon.mozPrintImage = "http://maps.google.com/mapfiles/arrowff.gif";
		searchIcon.transparent = "http://maps.google.com/mapfiles/arrowtransparent.png";
		
		/*
		 * Detect if the search station is one of the results
		 * May want to use this at some point to modify the marker in some way
		if(results[0].title == searchStation.title && results[0].lat == searchStation.lat && results[0].lng == searchStation.lng)
		{
		}
		 */
		
		var searchLocation = new GLatLng(searchStation.lat, searchStation.lng);
		map.addOverlay(new GMarker(searchLocation, {icon:searchIcon, title:strings.searchMarkerPrefix+" "+searchStation.title}));
		map.setCenter(offsetLocation(searchLocation), mapInitZoom);
	}

	// Display the results
	if(results)
	{
		// Add the results to the map
		for(var i = 0, result; result = results[i]; i++)
			addMarkerForResult(i);
		
		// Add interactivity to the results table
		$("#results tbody tr").each(function(index)
		{
			var $tr = $(this);
			$("th", this).click(function()
			{
				displayResult(index);
				map.panTo(offsetLocation(results[index].marker.getLatLng()));
			});
			$("th", this).mouseover(function(){
				hoverResult(index);
			});
		});
	}
	
	// Set up the tabs on the right hand side
	$("#results-tabs li.details a").click(function()
	{
		toggleDetailsTab();
//		if(typeof(this.blur) == "function") this.blur();
		return false;
	});
	
	$("#results-tabs li.results a").click(function()
	{
		toggleResultsTab();
//		if(typeof(this.blur) == "function") this.blur();
		return false;
	});
	
	// Initialise the content details
	$("#summary").html("<big>"+strings.noResultMessage+"</big>");
	$("#details .tab-group").hide();
	
	// Initialise the details tabs
	$("div.tab-group").each(function()
	{
		var $tabGroup = $(this);
		
		$("div.tab", this).hide();
		$("div.tab:first", this).show();
		$("ul.tabs li:first", this).addClass("selected");
		
		$("ul.tabs li", this).each(function(index)
		{
			$(this).click(function()
			{
				$("div.tab", $tabGroup).hide();
				$("div.tab:eq("+index+")", $tabGroup).show();
				
				$("ul.tabs li", $tabGroup).removeClass("selected");
				$(this).addClass("selected");
				
				var link = $("a", this).get(0);
//				if(typeof(link.blur) == "function") link.blur();
				
				return false;
			})
		});
	})
	
	// Set up the custom scroll bars
	$(window).resize(function()
	{
		var sHeight = $("#search").height();
		var rHeight = $("#map-container").height();
		$("#results-container").css("height", (rHeight-sHeight)+"px");
		$("#results-container .inner").css("padding-bottom", "1em");
	}).trigger("resize");
	
	
	// Filter the contents of the price select based on the listing type
	var priceOptionsHTML = {};
	$("optgroup[id^=search-maxprice-group]").each(function()
	{
		var val = $(this).attr("id").replace(/^search-maxprice-group-/, "");
		priceOptionsHTML[val] = $(this).html();
	});
	$("#search-ltype-input")
		.change(function()
		{
			var type = $(this).attr("value"); // "buy" or "rent"
			$("#search-maxprice-input").html(priceOptionsHTML[type]);
		})
		.trigger("change");
		
	// Add dynamic "about us" functionality
	$("#introduction h3").before(
		$("<a></a>")
			.append("<abbr title=\"" + strings.closeButtonTitle + "\">&times;</abbr>")
			.addClass("close")
	);
	
	$("#aboutus-close, #introduction a.close").click(function() 
	{ 
		$("#introduction-fullpage").fadeOut("slow"); 
		return false;
	});
	
	$("#aboutus-open").click(function()
	{
		$("#introduction-fullpage").fadeIn();
		return false;
	});
	
	// Replace Google logo with text
	$("div.gmnoprint:first")
		.css("bottom", "-100px")
		.css("left", "-100px");

	// Animate footer link groups
	$("#footer div.link-group").hover(
		function() { $(this).addClass("show-link-group"); },
		function() { $(this).removeClass("show-link-group"); }
	);
}


/**
 * Offsets a GLatLng position intended to be the new map centre to take the parts of the map partially obscured search form and results form into account.
 * Should be used in conjunction with map.setCentre() and map.panTo()
 * @param GLatLng loc is the point that should be central on the map
 * @return GLatLng is the transformed position that should be set as the centre of the map in order to make the original point the centre of the viewable area
 **/
function offsetLocation(loc)
{
	var type = map.getCurrentMapType();
	if(!type)
		return loc;
	
	var projection = type.getProjection();
	var zoom = map.getZoom();
	
	var pixelLocation = projection.fromLatLngToPixel(loc, zoom);
	pixelLocation.x += ($("#results").width() / 2);
	pixelLocation.y -= ($("#search").height() / 2);
	return projection.fromPixelToLatLng(pixelLocation, zoom);
}


/**
 * Class to add Open Street Map and Tube Map overlays to Google Maps
 **/
function WhereCanILiveMapExtensions()
{
}

WhereCanILiveMapExtensions.prototype = {
	
	_contructor:WhereCanILiveMapExtensions,
	
	/**
	 * Returns a GTileLayer object configured to display the tube map overlay from Nestoria's servers
	 * @return GTileLayer
	 **/
	tubeMapTileLayer:function()
	{
		// Set up tile layer options
		var layerConfig = {
			name: strings.tubeMapTitle,
			minZoom: 1,
			maxZoom: 17
		};

		// Create the copyright collection
		var copyright = new GCopyright(2, new GLatLngBounds(new GLatLng(-90,-180), new GLatLng(90,180)), layerConfig.minZoom, strings.tubeMapCopyright);
		var copyrightCollection = new GCopyrightCollection(layerConfig.name);
		copyrightCollection.addCopyright(copyright);

		// Create the tile layer
		var tLayer = new GTileLayer(copyrightCollection, layerConfig.minZoom, layerConfig.maxZoom);

		// Tile layer isPng function
		tLayer.isPng = function() 
		{ 
			return true;
		};

		// tile layer getTileUrl function
		tLayer.getTileUrl = function(tile,zoom)
		{
			tile_url_base = "http://timg.nestoria.co.uk/map_tiles";
			if (zoom < strings[wcil_location].tubeMinZoom || zoom > strings[wcil_location].tubeMaxZoom )
				return tile_url_base + '/404.png';

			return [
					tile_url_base,
					strings[wcil_location].tubeOverlayName,
					zoom,
					tile.x,
					strings[wcil_location].tubeOverlayName + '_' + zoom + '_' + tile.x + '_' + tile.y + '.png'
				].join("/");
		};

		return tLayer;
	},
	
	/**
	 * Returns a GTileLayer object configured to display Open Street Map
	 * @return GTileLayer
	 **/
	openStreetMapTileLayer:function()
	{
		// Set up tile layer options
		var layerConfig = {
			name: strings.openStreetMapTitle,
			minZoom: 1,
			maxZoom: 18
		};

		// Create the copyright collection
	    var copyright = new GCopyright(1, new GLatLngBounds(new GLatLng(-90,-180), new GLatLng(90,180)), 0, strings.openStreetMapCopyright);
	    var copyrightCollection = new GCopyrightCollection(layerConfig.name);
	    copyrightCollection.addCopyright(copyright);

		// Create the tile layer
	    var tLayer = new GTileLayer(copyrightCollection, layerConfig.minZoom, layerConfig.maxZoom);

		// Tile layer isPng function
	    tLayer.isPng = function() 
		{
	        return true;
	    };

		// Tile layer getTileUrl function
	    tLayer.getTileUrl = function (a, b) 
		{
	        return "http://tile.openstreetmap.org/"+b+"/"+a.x+"/"+a.y+".png";
	    };

		// Tile layer getOpacity function
	    tLayer.getOpacity = function() 
		{
	        return 1.0;
	    };
	
		return tLayer;
	},
	
	/**
	 * Returns a GTileLayer object configured to display Cloud Made's Open Street Map tiles
	 * @return GTileLayer
	 **/
	cloudMadeMapTileLayer:function()
	{
		// Set up tile layer options
		var layerConfig = {
			name: strings.cloudMadeMapTitle,
			minZoom: 1,
			maxZoom: 18
		};

		// Create the copyright collection
	    var copyrightCollection = new GCopyrightCollection();
	
		// Add "powered by Google Maps" and "OpenStreetMap"
		copyrightCollection.addCopyright(new GCopyright(1, new GLatLngBounds(new GLatLng(-90,-180), new GLatLng(90,180)), 0, strings.poweredByGoogle));
		copyrightCollection.addCopyright(new GCopyright(2, new GLatLngBounds(new GLatLng(-90,-180), new GLatLng(90,180)), 0, strings.cloudMadeMapCopyright));

		// Create the tile layer
	    var tLayer = new GTileLayer(copyrightCollection, layerConfig.minZoom, layerConfig.maxZoom);

		// Tile layer isPng function
	    tLayer.isPng = function() 
		{
	        return true;
	    };

		// Tile layer getTileUrl function
	    tLayer.getTileUrl = function (a, b) 
		{
			var now = new Date();
			return cloudMadeMapURL + "/" + cloudMadeAPIKey + "/" + cloudMadeStyle + "/256/"+b+"/"+a.x+"/"+a.y+".png?"+now.getTime();
	    };

		// Tile layer getOpacity function
	    tLayer.getOpacity = function() 
		{
	        return 1.0;
	    };
	
		return tLayer;
	},
	
	/**
	 * Returns a GMapType containing a single GMapTileLayer configured to display Open Street Map
	 * @return GMapType
	 **/
	openStreetMapType:function()
	{
	    var newType = new GMapType(
			[this.openStreetMapTileLayer(), this.tubeMapTileLayer()], 
			new GMercatorProjection(19), 
			strings.openStreetMapTitle, 
			{
	        	errorMessage: strings.openStreetMapError
	    	}
		);

		return newType;
	},
	
	/**
	 * Returns a GMapType containing a single GMapTileLayer configured to display Cloud Made's version of Open Street Map
	 * @return GMapType
	 **/
	cloudMadeMapType:function()
	{
	    var newType = new GMapType(
			[this.cloudMadeMapTileLayer(), this.tubeMapTileLayer()], 
			new GMercatorProjection(19), 
			strings.cloudMadeMapTitle, 
			{
	        	errorMessage: strings.cloudMadeMapError
	    	}
		);

		return newType;
	},
	
	/**
	 * Adds a GMapTileLayer configurd to display the tube map overlay to an existing GMapType.
	 * Note that GMapTypes are immutable, so a copy is made of the existing GMapType which contains the additional layer.
	 * @param GMapType bastType is the map type to start from, e.g. G_NORMAL_MAP
	 * @param string name is the name to assign to the returned GMapType. Optional, if ommitted baseType's name will be retained.
	 * @return GMapType
	 **/
	addTubeLinesToMapType:function(baseType, name)
	{
		// Create a tile layer for the tube map
		var tubeMapTileLayer = this.tubeMapTileLayer();
		
		// Add the tube map layer to the base map type's layers
		var layers = baseType.getTileLayers();
		layers.push(tubeMapTileLayer);
		
		// Work out the name to use
		if(typeof(name) == "undefined")
			name = baseType.getName();

		// Create a new map type based on the old map type
		var newType = new GMapType(
			layers,
			baseType.getProjection(),
			name,
			{ 
				maxResolution: baseType.getMaximumResolution(),
				minResolution: baseType.getMinimumResolution(),
				errorMessage: baseType.getErrorMessage()
			}
		);
		
		return newType;
	}
};



function iconForTime(time, selected)
{
	var file = "0";
	if(time > 67.5)
		file = "plus";
	else if(time > 52.5)
		file = "60";
	else if(time > 37.5)
		file = "45";
	else if(time > 22.5)
		file = "30";
	else if(time > 7.5)
		file = "15";
	
	var baseURL = $("base").attr("href");
	var imageURL = baseURL + "images/markers/" + strings[wcil_location].mapMarkerPath + (selected ? "selected" : "normal") + "/" + file + ".png";
	
	var icon = new GIcon(G_DEFAULT_ICON);
	icon.image = imageURL;
	icon.iconSize = selected ? new GSize(40, 42) : new GSize(25, 27);
	icon.iconAnchor = selected ? new GPoint(20, 38) : new GPoint(10, 19);
	icon.shadow = null;
	icon.shadowSize = new GSize(0,0);
	return icon;
}

function replaceMarkerIconForResult(index, newIcon)
{
	var newMarker = new GMarker(results[index].marker.getLatLng(), {
		icon: newIcon,
		title: results[index].marker.getTitle()
	});
	
	map.removeOverlay(results[index].marker);
	results[index].marker = newMarker;
	map.addOverlay(results[index].marker);
	GEvent.addListener(results[index].marker, "mouseover", function(marker)
	{
		displayResult(index);
		showDetailsTab();
	});
}

function addMarkerForResult(index)
{
	var resultIcon = iconForTime(results[index].time, false);
	
	results[index].marker = new GMarker(new GLatLng(results[index].lat, results[index].lng), {
		icon:resultIcon, 
		title:strings.resultMarkerPrefix+" "+results[index].title+" "+strings.resultMarkerSuffix+" ("+formatTime(results[index].time)+")"
	});
	map.addOverlay(results[index].marker);
	GEvent.addListener(results[index].marker, "mouseover", function(marker)
	{
		displayResult(index);
		showDetailsTab();
	});
}

function toggleDetailsTab()
{
	if($("#details").hasClass("hidden"))
		showDetailsTab();
	else
		hideDetailsTab();
}

function showDetailsTab()
{
	$("#details")
		.animate({right:"3px"}, "fast")
		.removeClass("hidden");
	$("#results-tabs li.details a").animate({marginLeft:"-30em", left:"-3px"}, "fast");
	
	hideResultsTab();
}

function hideDetailsTab()
{
	$("#details")
		.animate({right:"-30em"}, "fast")
		.addClass("hidden");
	$("#results-tabs li.details a").animate({marginLeft:"0", left:"0"}, "fast");
}

function toggleResultsTab()
{
	if($("#results").hasClass("hidden"))
		showResultsTab();
	else
		hideResultsTab();
}

function showResultsTab()
{
	$("#results")
		.animate({right:"3px"}, "fast")
		.removeClass("hidden");
	$("#results-tabs li.results a").animate({marginLeft:"-30em", left:"-3px"}, "fast");
	hideDetailsTab();
}

function hideResultsTab()
{
	$("#results")
		.animate({right:"-30em"}, "fast")
		.addClass("hidden");
	$("#results-tabs li.results a").animate({marginLeft:"0", left:"0"}, "fast");
}

function hoverResult(index)
{	
	// Don't bother if we're just reselecting the same item all again
	if(selectedResult == index)
		return;
	
	// Highlight te item in the results tabls
	$("#results tbody tr").removeClass("hovered");
	$("#results tbody tr:eq("+index+")").addClass("hovered");
}

function displayResult(index)
{	
	// Don't bother if we're just reselecting the same item all again
	if(selectedResult == index)
		return;
	
	// Highlight te item in the results tabls
	if(selectedResult >= 0)
	{
		$("#results tbody tr.selected").removeClass("selected");
		replaceMarkerIconForResult(selectedResult, iconForTime(results[selectedResult].time, false));
	}
	$("#results tbody tr:eq("+index+")").addClass("selected");

	// Update the marker's icon
	replaceMarkerIconForResult(index, iconForTime(results[index].time, true));
	selectedResult = index;
	
	// Populate the "Live Near" tab
	var price_formatted = results[index].price;
	if ( price_formatted.length > 1)
		price_formatted = strings.currencyPrefix+results[index].price+strings.currencySuffix;
	else
		price_formatted = 'n/a';
	
	var detailsHTML = 	
		"<h2>"+strings.resultTitlePrefix+" <span class=\"station\">"+results[index].title+"</span> "+strings.resultTitleSuffix+"</h2>" +
		"<dl>" +
		"<dt>"+strings.resultDetailTimeTitle+"</dt><dd class=\"times\"><ul>" + 
		"<li>"+formatTime(results[index].time)+" "+strings.byTube+"</li>";
		
	if(results[index].walkTime > -1)
		detailsHTML += "<li>"+formatTime(results[index].walkTime)+" "+strings.walking+"</li>";
	if(results[index].cycleTime > -1)
		detailsHTML += "<li>"+formatTime(results[index].cycleTime)+" "+strings.cycling+"</li>";
		
	detailsHTML +=
		"</ul></dd>" +
		"<dt>"+strings.resultDetailPriceTitle+"</dt><dd>"+price_formatted+"</dd>" +
		"</dl>";
	
	$("#details #summary").html(detailsHTML);
	
	// Set up the Google Earth and TFL links
	$("#details a.google-earth").attr("href", strings.googleMapsURL+"/maps?q="+results[index].lat+","+results[index].lng+"&output=kml");
	//$("#details a.station").attr("href", "http://www.google.com/search?q=site%3A"+strings.stationSite+"+\""+escape(results[index].title)+"\"&btnI=1");
	
	// Populate the TFL search form
	$("#jp-origin-input").attr("value", results[index].title);
	
	// Load the house price widget
	if(typeof(NESTORIA) != "object" || !NESTORIA.widgets)
	{
		// Loading for the first time
		
		NESTORIA_config = {
			widgets: "nContent",
			container: "house-price-widget",
			dataSrc: strings.housePriceDataSourceDomain + escape(results[index].nestoriaurl) + strings.housePriceDataSourcePath + searchParams.ltype,
			mode: "avgprice",
			view: 3
		};
		
		$("#details").append("<script type=\"text/javascript\" src=\"" + strings.housePriceDataSourceDomain + "static/js/2/widget.js\"></script>");
	}
	else
	{
		// Updating the already-loaded widget for a different location
		
		var url = strings.housePriceDataWidgetDomain + escape(results[index].nestoriaurl) + "/property/" + searchParams.ltype + "?mode=avgprice&amp;view=3&amp;callback=nContent";
		$("#details").append("<script type=\"text/javascript\" src=\""+url+"\"></script>");		
	}
	
	// Load the property listings
	var baseURL = $("base").attr("href");
	$("#listings-widget")
		.html("<p>"+strings.loadingMessage+"</p>")
		.load(baseURL+"listings_widget.php5", {
			"lat" : results[index].lat,
			"lng" : results[index].lng,
			"bedrooms" : searchParams.bedrooms,
			"maxprice" : searchParams.maxprice,
			"ltype" : searchParams.ltype
		});
		
	// Make sure the details are visible
	$("#details .tab-group").show();
}

function formatTime(minutes)
{
	var hours = 0;
	if(minutes > 60)
	{
		hours = Math.floor(minutes / 60);
		minutes -= hours * 60;
	}
	
	var timeString = "";
	if(hours > 0)
		timeString += hours + (hours == 1 ? strings.hoursSuffixSingular : strings.hoursSuffixPlural) + " ";
	
	timeString += minutes + (minutes == 1 ? strings.minsSuffixSingular : strings.minsSuffixPlural);
	
	return timeString;
}
