jQuery(function($)
{
var DIRECTIONS_LINK_TEMPLATE = "http://maps.google.co.uk/maps?saddr={0}&daddr={1}";
var DEFAULT_CENTER = new google.maps.LatLng(55.823085, -4.482305);
var DEFAULT_ZOOM = 5;
var CLOSE_ZOOM = 15;

$.Class.extend("DefibFinder", {},
{
	init: function(options)
	{
		this._feedUrl = options['feedUrl'];
		this._locationsListElement = options['locationsListElement'];
		this._locationsListSize = options['locationsListSize'];
		this._resultsNotFoundElement = options['resultsNotFoundElement'];
		this._locationInfoTemplate = options['locationInfoTemplate'];
		this._locationListItemTemplate = options['locationListItemTemplate'];
		
		this._markerIcon = options['markerIcon'];
		this._clusterIcon = options['clusterIcon'];
		this._tagIcons = options['tagIcons'];
		
		this._region = options['region'];
		
		this._locationMarkers = {};
		this._clusterMarkers = [];
		this._searchMarker = null;
		this._searchAddress = "";
		this._markerToShow = null;

		this._geocoder = new google.maps.Geocoder();
		this._directionsService = new google.maps.DirectionsService();
		
		this._infoWindow = new google.maps.InfoWindow();
		
		this._map = new google.maps.Map(options['mapElement'],
		{
			zoom: DEFAULT_ZOOM,
			center: DEFAULT_CENTER,
			mapTypeId: google.maps.MapTypeId.ROADMAP	
		});
		google.maps.event.addListener(this._map, "idle", this.callback("refreshMarkers"));
	},
	
	search: function(address)
	{
		this._searchAddress = address;
		
		var params = {
			address: address,
			region: this._region
		};

		var self = this;
		this._geocoder.geocode(params, function(results, status)
		{
			if (status == google.maps.GeocoderStatus.OK)
			{
				self.setSearchMarker(results[0].geometry.location, true);
			}
			else
			{
				alert("Address search was unsuccessful. Status: " + status);
				// TODO
			}
		});
	},
	
	setSearchMarker: function(position, zoomTo)
	{
		if (this._searchMarker)
		{
			this._searchMarker.setMap(null);
		}
		
		this._searchMarker = new google.maps.Marker({
			map: this._map,
			position: position
		});
		
		if (zoomTo)
		{
			this._map.setCenter(position);
			this._map.setZoom(CLOSE_ZOOM);
		}
		
		this.refreshSearchResults();
	},
	
	refreshMarkers: function()
	{
		var bounds = this._map.getBounds();
		var northEast = bounds.getNorthEast();
		var southWest = bounds.getSouthWest();

		var params = {
			minLongitude: southWest.lng(),
			maxLongitude: northEast.lng(),
			minLatitude: southWest.lat(),
			maxLatitude: northEast.lat()
		};
		
		if (this._searchMarker)
		{
			params['searchLongitude'] = this._searchMarker.position.lng();
			params['searchLatitude'] = this._searchMarker.position.lat();
		}

		$.getJSON(this._feedUrl, params, this.callback("_updateMarkers"));
	},
	
	refreshSearchResults: function()
	{
		if (this._searchMarker)
		{
			var params = {
				searchLongitude: this._searchMarker.position.lng(),
				searchLatitude: this._searchMarker.position.lat(),
				maxResults: this._locationsListSize
			};
			
			$.getJSON(this._feedUrl, params, this.callback("_updateSearchResults"));
		}
		else
		{
			$(this._locationsListElement).hide();
			$(this._resultsNotFoundElement).hide();
		}
	},
	
	_updateMarkers: function(locationsResponse)
	{
		var newLocationIds = {};
		
		// Create new location markers
		var locations = locationsResponse.locations;
		if (locations)
		{
			for (var i = 0; i < locations.length; i++)
			{
				var location = locations[i];
			
				if (typeof this._locationMarkers[location.id] == "undefined")
				{
					// New location
					this._createLocationMarker(location);
				}
			
				newLocationIds[location.id] = true;
			};
		}
		
		// Remove old location markers
		var self = this;
		$.each(this._locationMarkers, function(id, marker)
		{
			if (typeof newLocationIds[id] == "undefined")
			{
				// Location no longer exists (or not visible)
				marker.setMap(null);
				google.maps.event.clearListeners(marker, "click");
				
				delete(self._locationMarkers[id]);
			}
		});
		
		// Remove old cluster markers
		var marker;
		while (typeof (marker = this._clusterMarkers.pop()) != "undefined")
		{
			marker.setMap(null);
		}
		
		// Create new cluster markers
		var clusters = locationsResponse.clusters;
		if (clusters)
		{
			for (var i = 0; i < clusters.length; i++)
			{
				this._createClusterMarker(clusters[i]);
			}
		}
		
		// Check for a marker to show
		if (this._markerToShow && typeof this._locationMarkers[this._markerToShow] !== "undefined")
		{
			google.maps.event.trigger(this._locationMarkers[this._markerToShow], "click");
			this._markerToShow = null;
		}
	},
	
	_updateSearchResults: function(locationsResponse)
	{
		$(this._locationsListElement).empty();

		var locations = locationsResponse.locations;
		if (locations)
		{
			$(this._locationsListElement).show();
			$(this._resultsNotFoundElement).hide();

			for (var i = 0; i < locations.length; i++)
			{
				var listItem = this._createInfoContent(this._locationListItemTemplate, locations[i]);
				listItem.appendTo(this._locationsListElement);
				
				var self = this;
				listItem.bind("click", { location: locations[i] }, function(e)
				{
					self._showMarker(e.data.location);
				});
			};
		}
		else
		{
			$(this._locationsListElement).hide();
			$(this._resultsNotFoundElement).show();
		}
	},
	
	_showMarker: function(location)
	{
		if (typeof this._locationMarkers[location.id] !== "undefined")
		{
			google.maps.event.trigger(this._locationMarkers[location.id], "click");
		}
		else
		{
			this._markerToShow = location.id;

			this._map.panTo(new google.maps.LatLng(location.latitude, location.longitude));
			this._map.setZoom(CLOSE_ZOOM);
		}
	},
	
	_createLocationMarker: function(location)
	{
		var icon = this._markerIcon;
		if (this._tagIcons && location.tags)
		{
			for (var i = 0; i < location.tags.length; i++)
			{
				var tag = location.tags[i];
				if (typeof this._tagIcons[tag] != "undefined")
				{
					icon = this._tagIcons[tag];
					break;
				}
			}
		}
		
		var marker = new google.maps.Marker({
			position: new google.maps.LatLng(location.latitude, location.longitude),
			map: this._map,
			title: location.name,
			icon: icon
		});
		
		var self = this;
		google.maps.event.addListener(marker, "click", function()
		{
			self._infoWindow.content = self._createInfoContent(self._locationInfoTemplate, location)[0];
			self._infoWindow.open(self._map, marker);
		});

		this._locationMarkers[location.id] = marker;
	},
	
	_createClusterMarker: function(cluster)
	{
		var marker = new google.maps.Marker({
			position: new google.maps.LatLng(cluster.latitude, cluster.longitude),
			map: this._map,
			icon: this._clusterIcon
		});
		
		this._clusterMarkers.push(marker);
	},
	
	_createInfoContent: function(template, location)
	{
		var element = $(template).clone();
		element.removeAttr("id");
		element.show();

		location.distance = (location.distance ? Math.round(location.distance*10)/10 : "");
		
		for (key in location)
		{
			$("." + key, element).text(location[key]);
		}
		
		var directionsLink =
			String.format(DIRECTIONS_LINK_TEMPLATE, encodeURIComponent(this._searchAddress), encodeURIComponent(location.address));
		$(".directionsLink", element).attr("href", directionsLink);
		
		element.find("*").andSelf().trigger("render");
		
		return element;
	}
});

});
