/**
 * Photomap - methods for displaying markers on the map when the viewport changes
 */


// Photomap JS class
// #!# Rename to iconlayer or similar
var photomap =
{
	// A layer containing the markers
	markerLayer: null,
	
	
	// Make this object easier to identify in Firebug
	toString: function()
	{
		var string = 'Photomap()';
		return string;
	},
	
	
	
	/**
	 * Sets up the Map to define the Photomap
	 * @param float longitude, latitude are the map centre, and the place where the marker initially appears if locationKnown
	 * @param int zoom
	 * @param int id Id of the initially selected feature.
	 * @param filterSring is expected to be URLencoded already
	 */
	initialize: function(datasource, apiKey, csNS, gmlNS, iconDictionary, selectedId, filterString, enableBubbles)
	{
// #!# Seemingly not used?
		// Defines namespaces
		this.csNS  = csNS;
		this.gmlNS = gmlNS;
		
		// Parameters used in the XML calls to get the data
		this.datasource = datasource;
		this.filterString = filterString;
		this.apiKey  = apiKey;
		
		// An initially-selected point
		this.selectedId = selectedId;
		
		// Set up a marker layer based on a supplied directory, and add the assigned marker layer to the main map object
		this.markerLayer = this.setupMarkerLayer('Photomap', iconDictionary);
		/*global CS */
		CS.map.addLayer(this.markerLayer);
		
		// Create a handle onto the layer to govern what should happen to it when items are selected or unselected
		/*global OpenLayers */
		if(enableBubbles == '1') {
			this.selectControl = new OpenLayers.Control.SelectFeature(this.markerLayer);
			this.markerLayer.events.on({
				'featureselected': this.onFeatureSelect,
				'featureunselected': this.onFeatureUnselect
			});
		} else {
			this.selectControl = new OpenLayers.Control.SelectFeature(this.markerLayer, {onSelect: this.itinerarySelect});
		}
		CS.map.addControl(this.selectControl);
		this.selectControl.activate();
		
		// Add the select control to the CS namespace so that it can be reactivated
		CS.selectControl = this.selectControl;
		
		// This object is used to read the GML objects directly and translates to the map projection.
		this.formatGML =  new OpenLayers.Format.GML({internalProjection: CS.map.getProjectionObject(), externalProjection: new OpenLayers.Projection("EPSG:4326")});
		
		// Assign actions that should be called when changes to the map are made
		CS.map.events.on({"moveend": this.getPoints, "zoomend": this.getPoints, scope: this});
		
		// On initial loading, get the markers
		this.getPoints();
	},
	
	
	// Function to get the markers
	getPoints: function()
	{
		// Determine if the point needs to be brought into view.
		var bounds = CS.map.getExtent().transform(CS.map.getProjectionObject(), CS.map.displayProjection);
		
		// Assign the current centre point
		var center = bounds.getCenterLonLat();
		
		// Determine the URL from which the data can be obtained
		var url = CS.baseUrl +
			this.datasource +
			'?key=' + this.apiKey +
			'&useDom=1' +
			'&latitude=' + CS.r7(center.lat) + '&longitude=' +  CS.r7(center.lon) + '&zoom=' + CS.map.getZoom() +
			'&w=' +  CS.r7(bounds.left) + '&s=' +  CS.r7(bounds.bottom) + '&e=' +  CS.r7(bounds.right) + '&n=' +  CS.r7(bounds.top) +
			'&' + this.filterString;
		
		// Put the URL into the debug tag
		CS.debugtagmsg('<a href="' + url + '" target="_blank">' + url + '</a>');
		
		// Use this AJAX reader call to retrieve the data, defining the success function which will handle the result; this call is defined in /openlayers/lib/OpenLayers/Ajax.js
		OpenLayers.loadURL(url, null, this, this.succeed);
	},
	
	
	// Function called on successful AJAX request
	succeed: function(request)
	{
		// Assign the features in the GML to a variable
		var features = this.formatGML.read(request.responseText);
		
		// Assign that there is no initially-selected feature
		var initiallySelectedFeature = false;
		
// #!# Not sure what is happening here - please document!
		// 
		for(var f = 0; f < features.length; f++) {
			features[f].feature = features[f].attributes.feature;
			
			// Maintain selection: If there is an initially selected feature, and it matches the current feature in the loop, set it the selected item which will be added below
			if(this.selectedId && features[f].attributes.id == this.selectedId) {
				initiallySelectedFeature = features[f];
			}
		}
		
		// Remove whatever features are currently there
		this.markerLayer.removeFeatures(this.markerLayer.features);
		
		// Insert the new features
		this.markerLayer.addFeatures(features);
		
		// Assign the initial selection, which may have come from the default or the cached item just above
		if(initiallySelectedFeature) {
			this.selectControl.select(initiallySelectedFeature);
		}
		
		// Count of the displayed markers, if there is a tag for this the HTML
		var msgEl = document.getElementById('message');
		if(msgEl) {
			msgEl.innerHTML = (features.length ? 'Showing ' + features.length + ' ' + (features.length == 1 ? 'marker' : 'markers') + '.' : 'No markers to display, try zooming out or panning to a different area.');
		}
		
		// If there is a tag for this, create a list of the markers as text, e.g. '12,21,25,57'
		this.currentMarkersStringCount(features);
	
	},
	
	
	// Function to create a string of the current points and a count
	// #!# This function could probably be made simpler by someone who understands DHTML better!
	currentMarkersStringCount: function(features)
	{
		var editablemarkertextEl = document.getElementById('editablemarkertext');
		if(editablemarkertextEl) {
			
			// Assign the containers for the list and for a count
			var editablemarkerlinkEl = document.getElementById('editablemarkerlink');
			var editablemarkercountEl = document.getElementById('editablemarkercount');
			
			// If there are no features; fade out the link; otherwise, add the list of IDs
			if(features.length === 0) {
				editablemarkerlinkEl.className = 'actions editablemarkerlinkdisabled';
				editablemarkercountEl.innerHTML = '';
			} else {
				editablemarkerlinkEl.className = 'actions';
				editablemarkercountEl.innerHTML = features.length + ' ';
				
				// Create an array to store the list, and add each ID in
				var idList = [];
				for(var featureNumber = 0; featureNumber < features.length; featureNumber++) {
					idList[featureNumber] = features[featureNumber].attributes.id;
				}
				
				// Compile the list as a set of comma-separated values, and slash-terminate
				var idListString = idList.join(',') + '/';
				
				// Add the list to the end of the tag's original location, e.g. /path/to/foo/ becomes /path/to/foo/12,21,25,57/
				// #!# Use of rel here is a dirty way of ensuring the original base ref is maintained as it is rewritten - perhaps there is a better way
				var originalHref = editablemarkerlinkEl.rel;
				editablemarkerlinkEl.href = originalHref + idListString;
			}
		}
	},
	
	
	/**
	 * Setus up a style lookup table based on...
	 * @link /openlayers/examples/styles-unique.html
	 */
	setupMarkerLayer: function(layerName, iconDictionary)
	{
		// Create a styleMap with a custom default symbolizer
		var styleMap = new OpenLayers.StyleMap({
			// Set the z-indexes of both graphics to make sure the background graphics stay in the background (shadows on top of markers looks odd; let's not do that).
// #!# Not clear why the background has a higher zIndex?
			graphicZIndex:           100,
			backgroundGraphicZIndex: 110,
			// If these are not supplied openlayers/.../Canvas.js works out some defaults based on pointRadius.
			graphicWidth:  36,
			graphicHeight: 30
		});
		
		// Add rules from the above lookup table, with the keys mapped to the 'type' property of the features, for the 'default' intent
		styleMap.addUniqueValueRules('default', 'feature', iconDictionary);
		
		// Finally, assign the marker layer
		var markerLayer = new OpenLayers.Layer.Vector(layerName, {styleMap: styleMap, rendererOptions: {yOrdering: false}});
		
		// Return the marker layer
		return markerLayer;
	},
	
	
	// Function to handle what happens when a feature is clicked on; see:
	// - http://docs.openlayers.org/library/overlays.html#displaying-popups - which contains the code example
	// - http://openlayers.org/dev/examples/popups.html - which seems like a broken exaple
	// - http://osgeo-org.1803224.n2.nabble.com/control-SelectFeature-td4930933.html
	// - http://trac.openlayers.org/changeset/7616?format=diff&new=7616
	onFeatureSelect: function(evt)
	{
		// Obtain a reference to the feature
		var feature = evt.feature;
		
		// Obtain the popup contents from the HTML (defined globally)
		/*global popupHtml */
		var popupContents = popupHtml(feature);
		
		// Create a popup (bubble window); #!# A bit hacky: this is done in the CS scope so that it can easily be destroyed
		CS.popup = new OpenLayers.Popup.FramedCloud(
			'featurePopup',
			feature.geometry.getBounds().getCenterLonLat(),
			new OpenLayers.Size(100,100),	// #!# Doesn't seem to work as documented
			popupContents,
			null,
			true,
			this.onPopupClose	/* This doesn't actually exist, but seems to work anyway */
		);
		feature.popup = CS.popup;
		CS.popup.feature = feature;
		
		// Add the popup to the main map object in exclusive mode; http://dev.openlayers.org/docs/files/OpenLayers/Map-js.html#OpenLayers.Map.addPopup
		CS.map.addPopup(CS.popup, true);
	},
	
	
	// Function to handle what happens when a feature is unselected
	onFeatureUnselect: function(evt)
	{
		// Obtain a reference to the feature
		var feature = evt.feature;
		
		// Unselect the popup
		if (feature.popup) {
			CS.popup.feature = null;
			CS.map.removePopup(feature.popup);
			feature.popup.destroy();
			feature.popup = null;
		}
	},
	
	
	// Function to handle what happens when a popup is closed
	onPopupClose: function(evt)
	{
		// 'this' is the popup
		this.selectControl.unselect(this.feature);
	},
	
	
	// Function to handle itinerary clicking within a popup
	itinerarySelect: function(feature)
	{
		/*global itinerary */
		itinerary.requestNearestPoint(feature.attributes.longitude, feature.attributes.latitude, 'Click', false);
		
	}
};

