/**
 * @author peter.goulborn
 * Requires OpenLayers, Prototype, atExtendOpenLayers.js
 *
 * $Revision: 108 $
 * $Author: Peter.goulborn $ 
 * 
 * This is a class to provide an Openlayers map for an iSharemaps server.
 */

if( !Astun ) var Astun = {};
if( !Astun.iSharemaps ) Astun.iSharemaps = {};
if( !OpenLayers || !Prototype || !Astun.JS.Common ) throw new Error( 'Dependent library not found' );  // Needs to be nicer, possibly iSharemaps-wide library error?


( function( ) {

	// local properties & objects
	
	// expose OLMap class
	Astun.iSharemaps.OLMap = Class.create( {
		'initialize': function ( elementId , ismurl , options, dataurl ) {
			/*
			 * ARGUMENTS
			 * ---------
			 * elementId - { string } id of the map element
			 * ismurl - {string } iShareMaps map page URL string
			 * options: { object } optional settings for wrapper and all maps
			 *		created by it.
			 *	callbacks: { object } map method container object.
			 *		{ click, doubleclick, hoverPause, hoverMove }
			 * dataurl - { string } iShareMaps data web service URL 
			 * ---------
			 * 
			 * NOTES
			 * -----
			 * Assumes base layer remains the iSharemaps layer.
			 * -----
			 */
			this.ismurl = ismurl;
			this.dataurl = dataurl
			this.mapElement = $( elementId );
			this.locationZoom = 1000; // width of map in metres, hardcoded as no setting to govern this  
			
			this.currentLocation = {};
			this.query={};
			this.query.popups = $A( );
		    this.themeName = options.themeName || 'base';
			this.installedControls = {};
			this.installedControls.nav = new OpenLayers.Control.Navigation( );
			this.installedControls.navHistory = new OpenLayers.Control.NavigationHistory();
			this.installedControls.lonlat = new OpenLayers.Control.MousePosition( );	
			this.installedControls.keys = new OpenLayers.Control.KeyboardDefaults( );  
			this.installedControls.attrib = new OpenLayers.Control.Attribution( ); 
			this.installedControls.pzb = new OpenLayers.Control.PanZoomBar( ); 
			this.installedControls.scale = new OpenLayers.Control.ScaleLine( );
			this.timers = {};
			this.timers.CurrentWait = 0 ;
			this.date='';
			
			
			
			this.defaultMapControls = [ 
				this.installedControls.nav,
				this.installedControls.navHistory,
				this.installedControls.lonlat,	
				this.installedControls.keys, 
				this.installedControls.attrib,
				this.installedControls.pzb,
				this.installedControls.scale
			 ];
			
			if( options && options.callbacks ) {
				var clickCallbacks = 	Object.extend( {}, {
					'click': options.callbacks.click, 
					'dblclick': options.callbacks.doubleclick
				} );
				var hoverCallbacks = 	Object.extend( {}, {
					'pause': options.callbacks.hoverPause || function( ){}, 
					'move': options.callbacks.hoverMove || function( ){}
				} );
				this.installedControls.hover = new OpenLayers.Control.Hover( {'delay': astun.settings.popupDelay}, hoverCallbacks );
				this.installedControls.click = new OpenLayers.Control.Click( {'double': false, pixelTolerance: 1}, clickCallbacks ); 
				this.defaultMapControls.push( this.installedControls.hover );
				this.defaultMapControls.push( this.installedControls.click );
			}
			
			this.arThemePopup=[ ];
			
			var mapResize = function( evt ){
					this.map.updateSize( );
			}
			
			/* Event listeners */
			Event.observe( this.mapElement, 'astun:mapResize', mapResize.bindAsEventListener( this ) );
			
			
			
		},
		'addControl': function( controlName, control ) {
			this.map.addControl( control );
			this.installedControls[ controlName ] = control;
			this.installedControls[ controlName ].activate( );
		},
		'removeControl': function( controlName ) {
			var removed = false;
			var control = this.installedControls[ controlName ];
			if( !!control ) {
				removed = this.installedControls[ controlName ].deactivate( );
				this.map.removeControl( control );
			} 
			return removed;
			
		},
		'setControl': function( controlName, control ) {
			this.removeControl( controlName );
			return this.addControl( controlName,control );
		}
		,
		'loadMap': function( baseMapSource, dataMapSource, options ) {
			/* 
			 * baseMapSource: {Object} Javascript representation of a base 
			 *     map source
			 * dataMapSource: {Object} Javascript representation of a data
			 *     map source
			 * options: {Object} container for optional settings:
			 * 		initialView: {Object} ISM view( easting, northing, zoom}.
			 * 		initialViewType: {String} Whether supplied initial view or default
			 *			view is used instead of cookie view. ).
			 *		data {Object}: Optional object with 
			 *			{<OpenLayers.Layer.iShareMaps>} properties to tag onto
			 *			the layer.
			 */
			var resScaleProperties = $A( [ 'scales','resolutions','maxResolution','minScale' ] );
			if( !options ) {
				var options = {};
			}
			if( options.forceInitial ) {
				options.initialViewType = 'supplied';  //catch old way of doing this.
			}
			if( options.initialViewType !== 'default' && options.initialViewType !== 'supplied' ) {
				options.initialViewType = 'cookie';
			}
			this.mapSource = dataMapSource.mapName;
			var bounds = new OpenLayers.Bounds.fromArray( baseMapSource.bounds );	
			var resBounds =  new OpenLayers.Bounds.fromArray( dataMapSource.bounds );
			var mapOptions = {
				'restrictedExtent': resBounds,
				'projection':  new OpenLayers.Projection('EPSG:27700'),
				'displayProjection':  new OpenLayers.Projection('EPSG:27700'),
				'units': 'm',
				'maxExtent': bounds,
				'controls': this.defaultMapControls
			};
			
			if( baseMapSource.baseMapDefinition.scales && baseMapSource.baseMapDefinition.scales.length ) {
				mapOptions.scales = baseMapSource.baseMapDefinition.scales;
				mapOptions.numZoomLevels = baseMapSource.baseMapDefinition.scales.length;
			}
			else {
				mapOptions.maxresolution = 50; //units per pixel
				mapOptions.numZoomLevels = 9;
			}
			
			this.map = new OpenLayers.Map( 
				this.mapElement, 
				mapOptions
			 );
			this.map.wrapper = this; // this is needed but ideally shouldn't be necessary...
			
			
			for( var c = 0; c < this.map.controls.length; c++ ) {
				this.map.controls[ c ].activate( );
			}		
			
			var defaultLayerOptions =  {
				'projection': this.map.projection,
				'units': this.map.units,
				'maxExtent': this.map.maxExtent
			}
			while( resScaleProperties.length ) {		
				var property = resScaleProperties.pop( );	
				var value = this.map[ property ];
				if( value ) {
					defaultLayerOptions[ property ] = value;
				}
			}
			
			var setISMLayer = function( dataMapSource)	{
				if( this.ismDataLayer ) {
					var oldISMDataLayer = this.ismDataLayer;
				}
				
				var ismOptions = Object.extend( {},defaultLayerOptions );
				Object.extend( 
					ismOptions, 
					{
						'singleTile': true,
						'ratio': 1.4,
						'alpha':true,
						'maxExtent': new OpenLayers.Bounds.fromArray( dataMapSource.bounds )
					}
				);
				
				this.ismDataLayer = new OpenLayers.Layer.iShareMaps( 
					'iSharemaps Data Layer', 
					this.ismurl, 
					{
						'mapsource': dataMapSource.mapName,
						'transparent': true
					}, 
					( options.data ) ? Object.extend( ismOptions, options.data ) : ismOptions
				);
				
				this.map.addLayer( this.ismDataLayer );  
				this.map.setLayerIndex( this.ismDataLayer, 1 ); //always one above bottom
				this.setISMLayers( 
					( options.data && options.data.defaultLayers ) ? options.data.defaultLayers : [ ],
					( options.data && options.data.defaultFilter ) ? options.data.defaultFilter : null
				);
				if( !this.ismDataLayer.params.layers.length ) {
					this.ismDataLayer.visibility = false;
				}
				if( oldISMDataLayer ) {
					oldISMDataLayer.destroy();
					oldISMDataLayer = null;
					this.mapElement.fire( 'astun:mapLoaded', dataMapSource );  //map has reloaded...
				}
			
			
			}.bind( this )
			
		
			
			var setBaseMap = function( baseMapSource ) {
				var definition = baseMapSource.baseMapDefinition;
				if( this.rasterLayer ) {
					var oldRasterLayer = this.rasterLayer;
					var oldResolution = this.map.getResolution();
				}
				
				var rasterOptions = Object.extend( {},defaultLayerOptions )
				if( definition.options ) {
					rasterOptions = Object.extend( rasterOptions, definition.options );
				}
				Object.extend( 
					rasterOptions, 				 
					{
						'buffer': 1,
						'transitionEffect': 'resize',
						'scales': definition.scales,
						'attribution': definition.copyright,
						'maxExtent': new OpenLayers.Bounds.fromArray( baseMapSource.bounds )
					}
				);
				this.map.maxExtent = rasterOptions.maxExtent;
				var rasterLayerName = definition.name;			
				var rasterType = ( definition.type ) ? definition.type : 'WMS';
				if( definition.tilecaches && definition.tilecaches.length) {
					rasterType = 'TileCache';
				}
				switch( rasterType ) {
					case 'WMS':
						this.rasterLayer = new OpenLayers.Layer.WMS( 
							'iShareMaps Raster Layer',
							definition.uri,
							{
								'layers': rasterLayerName
							},
							rasterOptions
						);
						break;
					case 'TileCache':
						this.rasterLayer = new OpenLayers.Layer.TileCache( 
							'iShareMaps Raster Layer',
							definition.tilecaches,
							rasterLayerName,
							rasterOptions
						);
						break;
					default:
						throw( 'Invalid raster layer type specified' );
						break;
				}
				this.map.addLayer( this.rasterLayer );  
				this.map.setBaseLayer( this.rasterLayer );
				
				if( oldRasterLayer ) {
					oldRasterLayer.destroy();
					oldRasterLayer = null;
					this.map.zoomTo( this.map.getZoomForResolution( oldResolution ) );
				}
				
				
			}.bind( this )
			
			setBaseMap( baseMapSource );
			setISMLayer( dataMapSource );
			
			this.markerLayer = new OpenLayers.Layer.Markers( 'Markers', {'maxResolution': this.map.maxResolution, 'maxExtent': this.map.maxExtent} );
			this.map.markerLayer = this.markerLayer; //map.markerLayer is deprecated please use markerlayer instead.
			this.map.addLayer( this.markerLayer );
			
			
			
			
			var setView = function( mapView ){
				if( options.initialViewType === 'supplied' || !mapView ) {
					var view = options.initialView;
				}
				else if( options.initialViewType === 'cookie' ){
					var view = mapView.evalJSON( );
				}
				
				if( view ) {
					this.draw( view );
				}
				else {
					// initialViewType === 'default'
					this.map.zoomToExtent( bounds );
				}
			}
			if( !options.deferDraw ) {
				this.mapElement.fire( 'astun:loadSetting', {setting: 'mapView', loadFunction: setView.bind( this )} );
			}
			
			
			this.map.events.register( "moveend", this, function( ) {
				// Create iSharemaps 'view' from centre point plus image width and resolution
				var en = this.map.getCenter( );
				var imageSize = this.ismDataLayer.getImageSize( );
				var view = {
					'easting': en.lon,
					'northing': en.lat, 
					'zoom' : imageSize.w * this.ismDataLayer.getResolution( )
				};
				var json = Object.toJSON( view );
				this.mapElement.fire( 'astun:saveSetting', {setting: 'mapView', value: '' + json} );

				//Trigger an jQuery event that map is moved
				if (jQuery )
				{
					jQuery('#' + this.mapElement.id )
						.trigger('mapMoved',[view])
						.trigger('moveBackMap',true)
						.trigger('adjustPopUp');
				}
				
            } );
            
            // If location set, show position on map
			if (this.currentPosition) {
				this.showLocation(this.currentPosition);
			}
			
            
            /* Set up event listeners */
            var mapMove = function( evt ) {
				/**
				 * Function: mapMove 
				 * Event wrapper for this.draw
				 *
				 * Parameters:
				 * evt - {object} Prototype Event, memo property expected
				 *		to be { easting, northing[ , zoom ] } view object.
				 *
				 * Returns:
				 * nothing
				 */
				this.draw( evt.memo );
            }
            
            var setAddress = function( evt ) {
				/**
				 * Function: setAddress 
				 * Event wrapper for this.draw and this.showLocation
				 *
				 * Parameters:
				 * evt - {object} Prototype Event, memo property expected
				 *		to be { address, uid, x, y } address object.
				 *
				 * Returns:
				 * nothing
				 */
				if( evt.memo.x && evt.memo.y ) {
					var en = new OpenLayers.LonLat( evt.memo.x, evt.memo.y );
					this.showLocation( en );
					this.draw({ 
						'easting': evt.memo.x, 
						'northing': evt.memo.y, 
						'zoom': this.locationZoom
					} );
					this.mapElement.fire( 'astun:saveSetting', { 'setting': 'currentLocation', 'value': Object.toJSON( evt.memo ) } );
				}
            }
            
			Event.observe( this.mapElement, 'astun:mapMove', mapMove.bindAsEventListener( this ) );
			Event.observe( this.mapElement, 'astun:setAddress', setAddress.bindAsEventListener( this ) );
			//Change the cursor during the ajax calss
			//Event.observe( this.mapElement, 'astun:dataLoadBegin', function(){ jQuery('#atMap').css('cursor', 'wait'); } );
			//Event.observe( this.mapElement, 'astun:dataLoadComplete', function(){ jQuery('#atMap').css('cursor','auto') } );
			
			//Event binding for jQuery
			if( jQuery )
			{
				var $eventElement = jQuery('#' + this.mapElement.id );
				
				// style the sketch fancy
				var sketchSymbolizers = {
					"Point": {
						pointRadius: 4,
						graphicName: "square",
						fillColor: "white",
						fillOpacity: 1,
						strokeWidth: 1,
						strokeOpacity: 1,
						strokeColor: "#333333"
					},
					"Line": {
						strokeWidth: 3,
						strokeOpacity: 1,
						strokeColor: "#666666",
						strokeDashstyle: "dash"
					},
					"Polygon": {
						strokeWidth: 2,
						strokeOpacity: 1,
						strokeColor: "#666666",
						fillColor: "white",
						fillOpacity: 0.3
					}
				};
				var style = new OpenLayers.Style();
				style.addRules([ new OpenLayers.Rule({symbolizer: sketchSymbolizers}) ]);
				var styleMap = new OpenLayers.StyleMap({"default": style});
				
				function handleMeasurements(event) {
					var geometry = event.geometry;
					var units = event.units;
					var order = event.order;
					var measure = event.measure;
					var $element = jQuery('#atMapOutPut');
					var out = "";
					if(order == 1) {
						out += "Distance: " + measure.toFixed(3) + " " + units;
					} else {
						out += "Area: " + measure.toFixed(3) + " " + units + '<sup style="position:absolute;padding-left:2px">2</' + 'sup>';
					}
					$element.show().animate({right:0} ,800).find('.atQuickInfo').html(out);
					if(!$element.is(':animated')) $element.effect('highlight', {}, 1000);
				}
				
				
				
				$eventElement
				.bind('loaderDialogVisibility', this, function( evt, visibility ) {
					visibility ? jQuery('#atMapLoader').fadeIn('fast') : jQuery('#atMapLoader').fadeOut('fast');
				})
				.bind('mapSourceLoaded', this, function( evt, mapSource, type ) {
					if( type === 'raster' ) {
						setBaseMap( mapSource );
					} 
					else if( type === 'ism' ) {
						setISMLayer( mapSource );
					} 				
				})
				.bind('mapMove', this, function( evt, location ) {
					evt.data.draw(location);
				})
				.bind('searchISM', this, function( evt, layer, field, value ) {
					evt.data.searchISMLayer( layer, field, value,  function( response ) {
						evt.data.showResults( response );
					} );
					//Hide the loader
					$eventElement.trigger('loaderDialogVisibility', false);
				})
				.bind('clearResults', this, function( evt ) {
					evt.data.clearPopups();
					evt.data.mapElement.fire("astun:resultsCleared", {'type': '', 'displayName': evt.data.query.layer} );
					evt.data.query.layer = null;
				})
				.bind('showChangeViewDialog', this, function( evt ) {
					//Load the cookie settings and take the CURRENT northing, easting and zoom level and populate the textboxes			 
						evt.data.mapElement.fire('astun:loadSetting',{
							setting		: 'mapView',
							loadFunction: function(cords){
											var cords = (!!cords) ? cords.evalJSON() : false;
											currentNorthing = cords.northing || '';
											currentEasting = cords.easting || '';
											currentZoom = cords.zoom || '';
										  }
						});
					//If there is a dialog already appended remove it - so in following lines it will redraw
					if(jQuery('.atDialogAlert').length!=0) jQuery('.atDialogAlert, .ui-widget-overlay:last').remove();
					
					//Append an empty div at the end of the body which will be converted to dialog
					jQuery('<div></div>')
						.addClass('atDialogAlert')
						.attr('title','Change view')
						.html(jQuery('<h4></h4>')
									 .html('<i>Please provide following details:</i>')
									 .css({'padding':2, 'margin-bottom':5, 'margin-top':0})
							 )
						.append(
							//Easting Box
							jQuery('<label></label>')
								.attr('for','atChangeViewEasting')
								.css({ 'display':'block', 'width':75, 'float':'left', 'padding':2 })
								.html('Easting:'),
							jQuery("<input name='atChangeViewEasting' id='atChangeViewEasting' />")
								.val(currentEasting)
								.css({'padding':2, 'margin-bottom':4})
								.keydown(function(e) { if( e.stopPropagation ) { e.stopPropagation(); } else { e.cancelBubble = true; } })
								.addClass('ui-state-active ui-corner-all'),
							'<br />',
							//Northing box
							jQuery('<label></label>')
								.attr('for','atChangeViewNorthing')
								.css({ 'display':'block', 'width':75, 'float':'left', 'padding':2 })
								.html('Northing:'),
							jQuery("<input name='atChangeViewNorthing' id='atChangeViewNorthing' />")
								.val(currentNorthing)
								.css({'padding':2, 'margin-bottom':4})
								.keydown(function(e) { if( e.stopPropagation ) { e.stopPropagation(); } else { e.cancelBubble = true; } })
								.addClass('ui-state-active ui-corner-all'),
							'<br />',
							jQuery('<label></label>')
								.attr('for','atChangeViewZoom')
								.css({ 'display':'block', 'width':75, 'float':'left', 'padding':2 })
								.html('Map width'),
							jQuery("<input name='atChangeViewZoom' id='atChangeViewZoom' />")
								.val(currentZoom)
								.css({'padding':2, 'margin-bottom':4})
								.keydown(function(e) { if( e.stopPropagation ) { e.stopPropagation(); } else { e.cancelBubble = true; } })
								.addClass('ui-state-active ui-corner-all'),
							'<br />',
							jQuery('<label></label>').css({ 'display':'block', 'width':75, 'float':'left', 'padding':2 }),
							jQuery("<button>Cancel</button>")
									.addClass('ui-state-default ui-corner-all')
									.css('margin-right',4)
									.bind('mouseenter mouseleave',function(){ jQuery(this).toggleClass('ui-state-hover') })
									.keydown(function(e) { if( e.stopPropagation ) { e.stopPropagation(); } else { e.cancelBubble = true; } })
									.click(function(){
										jQuery('.atDialogAlert').dialog('close');
									}),
							jQuery("<button>Go</button>")
								.addClass('ui-state-default ui-corner-all')
								.bind('mouseenter mouseleave',function(){ jQuery(this).toggleClass('ui-state-hover') })
								.keydown(function(e) { if( e.stopPropagation ) { e.stopPropagation(); } else { e.cancelBubble = true; } })
								.click(function(){
									var n		= jQuery('#atChangeViewNorthing'),
										e		= jQuery('#atChangeViewEasting'),
										z		= jQuery('#atChangeViewZoom'),
										error	= false;
									//validation
									jQuery('.ui-dialog:last').find('input').each(function(){
										if(jQuery.trim(jQuery(this).val()).length==0)
										{
											jQuery(this).addClass('ui-state-error');
											error=true;
										}
										else jQuery(this).removeClass('ui-state-error')
										
										//OnBlur check for the errors again
										jQuery(this).blur(function(){ jQuery('.ui-dialog:last button:first').trigger('click') })
									})
									//If error found
									if(error) return false;
									//Location from the textboxes
										var loc = {
													"easting"	: jQuery.trim(e.val()),
													"northing"	: jQuery.trim(n.val()),
													"zoom"		: jQuery.trim(z.val())
												  }
									//Move the map
										$eventElement.trigger('mapMove', loc);
									//Close the dialog
										jQuery('.atDialogAlert').dialog('close');
								})
						 )
						.appendTo('body')
						.dialog({
								modal: true,
								resizable: false
						 });
							
						//Fix the font size and z-index
							jQuery('.ui-widget-overlay:last').css('z-index',10000000)
							jQuery('.ui-dialog:last').css({'font-size':12, 'z-index':10000000})
				})
				.bind('navigation',  function( evt,navType) {
					var navControl = astun.mapWrapper.installedControls.navHistory,
						p, n;
					/*Change the status of the navigation
					  Note: We put setTimout just to wait for mapMove event to finish its events.
					*/
					setTimeout(function(){
						//Make both true - active
							$eventElement.trigger('moveBackMap',true);
							$eventElement.trigger('moveForwardMap',true);
						if(navType=='back')
						{
							navControl.previousTrigger();
							if(!navControl.previousTrigger()) $eventElement.trigger('moveBackMap',false);
						}
						else
						{
							navControl.nextTrigger();
							if(!navControl.nextTrigger()) $eventElement.trigger('moveForwardMap',false);
						}
					},500)
				})
				.bind('mapControls', this, function( evt,dragType,status) {
					if(dragType == 'DragPan' && status)
					{
						//Default
						dragType = 'ZoomBox';
						status = false;
					}

					$eventElement.trigger('hideQuichInfoBox');
					
					switch(dragType)
					{
						case 'ZoomBox':
								if(!status)
								{
									astun.mapWrapper.setControl('drag', new OpenLayers.Control.ZoomBox());
								    astun.mapWrapper.setControl('shiftdrag', new OpenLayers.Control.DragPanExt({keyMask:OpenLayers.Handler.MOD_SHIFT}));
									$eventElement.trigger('zoomStatus',true);
									$eventElement.trigger('panStatus',false);
									$eventElement.trigger('distanceStatus',false);
									$eventElement.trigger('areaStatus',false);
									break;
								}
						case 'distance' :
								if(!status)
								{	
									var lineControl = new OpenLayers.Control.Measure
									(
										OpenLayers.Handler.Path, {
											persist: true,
											handlerOptions: {
												layerOptions: {styleMap: styleMap}
											}
										}
									)
									astun.mapWrapper.setControl('drag', lineControl);
									
									lineControl.events.on({
										"measurepartial": handleMeasurements
									});
									
									$eventElement.trigger('distanceStatus',true);
									$eventElement.trigger('areaStatus',false);
									$eventElement.trigger('zoomStatus',false);
									$eventElement.trigger('panStatus',false);
									break;
								}
						case 'area' :
								if(!status)
								{
									var areaControl = new OpenLayers.Control.Measure(
										OpenLayers.Handler.Polygon, {
											persist: true,
											handlerOptions: {
												layerOptions: {styleMap: styleMap}
											}
										}
									)
									astun.mapWrapper.setControl('drag', areaControl);
									
									areaControl.events.on({
										"measure": handleMeasurements
									});

									$eventElement.trigger('areaStatus',true);
									$eventElement.trigger('distanceStatus',false);
									$eventElement.trigger('zoomStatus',false);
									$eventElement.trigger('panStatus',false);
									break;
								}
						default:
								astun.mapWrapper.setControl('drag', new OpenLayers.Control.DragPanExt());
								astun.mapWrapper.setControl('shiftdrag', new OpenLayers.Control.ZoomBox({keyMask:OpenLayers.Handler.MOD_SHIFT}));
								astun.mapWrapper.setControl('click', astun.mapWrapper.installedControls.click);
								$eventElement.trigger('panStatus',true);
								$eventElement.trigger('zoomStatus',false);
								$eventElement.trigger('distanceStatus',false);
								$eventElement.trigger('areaStatus',false);
								break;
					}
				})
				.bind('resizeMapElement', this, function( evt,mapWidth) {
						var centre = evt.data.map.getCenter(); //lonlat of pre-resize map 
						var view = {easting: centre.lon, northing: centre.lat};
						if(mapWidth && mapWidth!='') $eventElement.width(mapWidth);
						evt.data.draw(view);
						//Also change the position of the loader
						$eventElement.trigger('repositionLoader');
				});
			}
			/* End event listener setup */
			
			
			
			
			
			
			this.ismDataLayer.events.register( "loadstart", this.ismDataLayer, function( ) {
                this.map.wrapper.mapElement.fire( "astun:layerStart", {layer: this} );
            } );
			this.ismDataLayer.events.register( "loadend", this.ismDataLayer, function( ) {
                this.map.wrapper.mapElement.fire( "astun:layerEnd", {layer: this} );
            } );
			this.mapElement.fire( 'astun:mapLoaded', dataMapSource );
			
			//Trigger jQuery event saying map is loaded
			if( jQuery )
			{
				jQuery('#' + this.mapElement.id ).trigger('mapLoaded',this);
			}
			
		},
		'setISMLayers': function( layers, filter ) {
			/*
			 * layers -> iSharemaps layer names array
			 */
			this.ismDataLayer.params.layers = $A( layers );
			
			if( filter ) {
				this.ismDataLayer.params.filterlayers = filter;
			}/*
			else if( this.ismDataLayer.params.filterlayers ) {
				delete this.ismDataLayer.params[ 'filterlayers' ];
			}*/
		},
		'getISMLayers': function( ) {
			/*
			 * layers -> iSharemaps layer names array
			 */
			return this.ismDataLayer.params.layers;
		},
		'removeISMLayer': function( layer ) {
			/*
			 * layer -> layer name string
			 */
			this.ismDataLayer.params.layers = this.ismDataLayer.params.layers.without( layer );
		},
		'addISMLayer': function( layer, filter ) {
			/*
			 * layer -> layer name string
			 */
			this.ismDataLayer.params.layers.push( layer );
			
				
		}, 
		'createMarker': function( position, icon ) {
		/**
		* Function: createMarker 
		* Draws a marker on the marker layer on the map 
		*
		* Parameters:
		* position - {object} OpenLayers.LonLat object
		* icon - OpenLayers.Icon object( defaults to default OpenLayers icon )	
		*
		* Returns:
		*( object} - OpenLayers.Marker object
		*/
			var marker = new OpenLayers.Marker( position, icon || OpenLayers.Marker.defaultIcon( ) );
			this.map.markerLayer.addMarker( marker );
			//set marker layer to be top one
			this.map.markerLayer.setZIndex( this.map.Z_INDEX_BASE[ 'Popup' ] - 1 ); 
			
			return marker;	
			 
		},
		'deleteMarker': function( marker ) {
		/**
		* Function: deleteMarker 
		* Removes a specific marker from the display and destroys the object 
		*
		* Parameters:
		* marker - {object} OpenLayers.Marker object
		*
		* Returns:
		*/
			if( marker ) {
				this.map.markerLayer.removeMarker( marker );
				marker.destroy( );
				marker = null;
			}
		},
		'clearPopups': function( ) {
		/**
		* Function: clearPopups 
		* Removes removes all popups from the display and destroy them 
		*
		* Parameters:
		*
		* Returns:
		*/
			if( this.map.popups.length > 0 ) {
				while( this.map.popups.length )
				{
					var popup = this.map.popups.pop( );
					this.map.removePopup( popup );
					popup.destroy( );
					popup = null;
				}
			}
		},
		'draw': function ( view )	{
		/**
		* Function: draw 
		* Draws or redraw the map.
		* Recenters if coordinates are specified,
		* rezooms if iSharemaps zoom level is specified, 
		* redraws iSharemaps layer, so any other changes to parameters take effect.
		*
		* Parameters:visib
		* view - {object ) iSharemaps view object: { easting, northing, zoom }
		* Returns:
		*/
		    
			if( !!view ) {
				var newCentre = OpenLayers.Util.extend( this.map.getCenter( ) || new OpenLayers.LonLat( ), {'lon': view.easting - 0.0, 'lat': view.northing - 0.0	} );
				var imageSize = this.ismDataLayer.getImageSize( );
				var currentResolution = this.ismDataLayer.getResolution( );
				if( view.zoom && view.zoom != imageSize.w * currentResolution ) {
					var resolution = view.zoom / imageSize.w;
				}
				else {
					var resolution = currentResolution;
				}
				this.map.setCenter( 
					newCentre, 
					this.map.getZoomForResolution( resolution ),
					false,
					true
				 );
			} 
			else 
			{
				window.olmap = this;
				
				if( this.timers.CurrentWait )
					clearTimeout( this.timers.CurrentWait );
			    
				this.timers.CurrentWait = setTimeout( "window.olmap.ismDataLayer.redraw( );",300 );
				/*
				window.olmap = this;
				 var timeOutVar = setTimeout( "window.olmap.ismDataLayer.redraw( )",500 );
				
				//this.ismDataLayer.redraw( );		
				*/		
			}
		
		},
		'getGeoJSONData' : function( params, callbackFunction ) {
		/**
		* Function: getGeoJSONData 
		* Makes a Ajax call with supplied params to return GeoJSON data
		*
		* Parameters:
		* params - {object ) params object as detailed below
		* callbackFunction - {object ) function object to be called on sucess
		*
		* Returns:
		*/
			var defaultParams = {
				'MapSource': this.mapSource,
				'RequestType': 'GeoJSON',
				'ServiceAction': '',
				'ActiveTool': '',
				'ActiveLayer': '',
				'mapid': -1,
				'axuid': new Date( ).valueOf( )
			};	
			var parameters = Object.extend( defaultParams, params );
			var thisPointer = this ;							
							
			this.mapElement.fire( 'astun:dataLoadBegin', {} );
			var successFunc = function( transport ) 
			{
				//check if return string contains a valid collection
				var strResponse = transport.responseText ; 
				
				if( strResponse.search( /FeatureCollection/ ) > 0 )
				{
					var results = transport.responseText.evalJSON( );							
					callbackFunction( results, thisPointer ) ; 
				}
				else
				{						
					callbackFunction( {'unexpectedResponse': transport.responseText}, thisPointer ) ; 
				}
				
				this.mapElement.fire( 'astun:dataLoadComplete', {} );
	
			};	 
			new Ajax.Request 
			( this.ismurl, 
				{
					method: 'get',
					parameters: parameters,
					onFailure: function( transport ) 
					{
						alert( "Error: Failed to get geometry data!" );
						//window.alert( "FAILURE: Could not access iShareMaps search page!" );	
					},
					onSuccess : successFunc.bindAsEventListener( this )			
				}
			 ); //Ajax request 
		},
		
		'getMapMultiInfo': function( en, pxTolerance, queryType , callbackFunction )
		{
		/**
		* Function: getMapMultiInfo 
		* Returns collection of underlying map features on mouse-click and mouse hoverPause
		*
		* Parameters:
		* en - {object ) OpenLayers.LonLat object
		* pxTolerance - {number} the pixel tolerance for selection of features, ignore those beyond
		* queryType - {string ) the nature of query which will determine features to be returned
		* callbackFunction - {object} Reference to the function to be called on success
		*
		* Returns:
		*/
			this.mapElement.fire( 'astun:dataLoadBegin', {} );
			switch( queryType ) 
				{ // switch
					case 'info':
						{ //info
							var params = 
							{
									//'MapSource': 'Elmbridge/Faults',
									'MapSource': this.mapSource,
									'RequestType': 'GeoJSON',
									'ServiceAction': 'GetMultiInfoFromPoint',
									'ActiveTool': 'MultiInfo',
									'ActiveLayer': '', //can be left blank?
									//'Layers': 'faults',
									'Layers': this.ismDataLayer.params.layers, //always use current layers
									'Easting':	en.lon,
									'Northing':	en.lat,
									'mapid': -1,
									'axuid': new Date( ).valueOf( ),
									//'tolerance': this.map.getResolution( ) * 24 
									'tolerance': this.map.getResolution( ) * pxTolerance 
							};
							var thisPointer = this ;
							var successFunc = function( transport ){
								try 
								{
									var strResponse = transport.responseText ; 
									if( strResponse.search( /FeatureCollection/ ) > 0 )
									{
										var results = transport.responseText.evalJSON( );
										callbackFunction( results, thisPointer ) ;
									}
									else
									{
										callbackFunction( {'unexpectedResponse': transport.responseText}, thisPointer ) ; 
									}
								}
								catch( parsingError ) 
								{
									if( transport.status != 0 ) { // i.e. if not fail because interrupted
										alert( "Error: Invalid iShareMaps search page!" );
									}
								}
								finally {
									this.mapElement.fire( 'astun:dataLoadComplete', {} );
								}
								
							}
							new Ajax.Request
							( this.ismDataLayer.url, 
								{
									method: 'get',
									parameters: params,
									onFailure: function( transport ) 
									{
										alert( "Error: Failed to get iShareMaps search page!" );
									},
									onSuccess : successFunc.bindAsEventListener( this )		
								}
							 ); //Ajax request for infoClick and showMapToolTip
							break ;
						} //info									
			
				} // switch			
		}, //getMapMultiInfo
		'findMyNearest': function( location, layername, maxResults, distance, callbackFunction ) {
			/*
			 * Finds nearest items on map as GeoJSON collection and calls supplied function with the results
			 * location {<OpenLayers.LonLat>} - point around which to call query
			 * layername - name defined in map source
			 * maxResults - most results to return
			 * distance - furthest results can be returned from
			 * callbackFunction - function to call on successful query, should take two arguments( response, pointer to this map wrapper )
			 */		
			this.currentPosition = location;			
			this.getGeoJSONData( {
				'ServiceAction': 'ShowMyClosest',
				'ActiveTool': 'MultiInfo',
				'ActiveLayer': layername,
				'Distance':  distance ,
				'MaxResults': maxResults,
				'Easting':	location.lon,
				'Northing':	location.lat
			},
			callbackFunction );
		
		}, //findMyNearest
		'findMy': function( location, layername, callbackFunction ) {
			/*
			 * Finds nearest items on map as GeoJSON collection and calls supplied function with the results
			 * location {<OpenLayers.LonLat>} - point around which to call query
			 * layername - name defined in map source
			 * maxResults - most results to return
			 * distance - furthest results can be returned from
			 * callbackFunction - function to call on successful query, should take two arguments( response, pointer to this map wrapper )
			 */		
			this.currentPosition = location;			
			this.getGeoJSONData( {
				'ServiceAction': 'ShowService',
				'ActiveTool': 'MultiInfo',
				'ActiveLayer': layername,
				'Easting':	location.lon,
				'Northing':	location.lat
			},
			callbackFunction );
		
		},

		'clearThemePopups': function( ) {
		/**
		* Function: clearThemePopups 
		* Removes removes all Theme Popups from the display and destroy them 
		*
		* Parameters:
		*
		* Returns:
		*/
			if( this.arThemePopup.length > 0 ) {
				while( this.arThemePopup.length )
				{
					var popup = this.arThemePopup.pop( );
					this.map.removePopup( popup );
					popup.destroy( );
					popup = null;
				}
			}
		},
		
		
		'showLocation': function( en )
		{
		/**
		* Function: showLocation 
		* Draws a marker to correspond to a specific position
		*
		* Parameters:
		* en - {object ) OpenLayers.LonLat object
		*
		* Returns:
		*/
			
			//delete any previous marker and create new one
			this.deleteMarkers( );
			var image = 'images/addressiconsmall.gif';
			var size = new OpenLayers.Size( 14,22 );
			var offset = new OpenLayers.Pixel( -( size.w/2 ), -size.h );
			var icon = new OpenLayers.Icon( image, size, offset );
			this.createMarker( en, icon );
			
			
		},
		

		'deleteMarkers': function( )
		{
		/**
		* Function: deleteMarkers 
		* Deletes all markers currently visible and destroys them from marker layer
		*
		* Parameters:
		*
		* Returns:
		*/
		
			if( this.markerLayer ){
				for( var i=0; i<this.markerLayer.markers.length; i++ )
				{
					this.deleteMarker( this.markerLayer.markers[ i ] );
				}
			}
		},
		'showResults' : function( response, type ) 
		{//ShowFeatures
			if(response.unexpectedResponse) {
				var displayname = '';
				var htmlResponse = '' + response.unexpectedResponse;
			}
			else{ 
				var featureCollectionArray = response;
				var featureCol = featureCollectionArray[0];	
				var html = '';
				this.clearPopups();	
				this.deleteMarker(this.currentLocation.marker);
				this.currentLocation.marker = null;
				var popupBounds = new OpenLayers.Bounds();
				popupBounds.extend(this.currentPosition);
				this.query.layer = featureCol.properties.layer;
				OpenLayers.Popup.FramedCloud.prototype.fixedRelativePosition = true;
				OpenLayers.Popup.FramedCloud.prototype.relativePosition = "tr"; 
				html += featureCol.properties.htmlHeader;
				while ( featureCol.features.length )
				{
					var feature = featureCol.features.shift(); // 
					var popupHTML = '<h1>'+feature.id+'</h1>' ;
					popupHTML += '<div class="atPopupFeatureInfo">';
					popupHTML += feature.properties.html;
					popupHTML += '</div>';
										
					var lonLat = new OpenLayers.LonLat(feature.geometry.coordinates[0][0], feature.geometry.coordinates[0][1]);
					
					popupBounds.extend(lonLat);
					// if third coordinate different then dealing with bounding box, extend to that too, put marker in centre
					if ((feature.geometry.coordinates[0][0] !== feature.geometry.coordinates[2][0]) || (feature.geometry.coordinates[0][1] !== feature.geometry.coordinates[2][1])) {
						var topRight = new OpenLayers.LonLat(feature.geometry.coordinates[2][0], feature.geometry.coordinates[2][1]);
						popupBounds.extend(topRight);
						var featureBounds = new OpenLayers.Bounds();
						featureBounds.extend(lonLat);
						featureBounds.extend(topRight);
						lonLat = featureBounds.getCenterLonLat();
					}
					
					var featurePopup = new OpenLayers.Popup.FramedMarker
					(
						null, 
						lonLat, 
						new OpenLayers.Size(20, 18), 
						popupHTML, 
						{size: new OpenLayers.Size(1, 1), pixel: new OpenLayers.Pixel(0, 0)}, 
						false,  // no close box
						function( evt ){},
						'css/jQuery/'+ this.themeName + '/images/popup-bg.png'
					);								
					this.query.popups.push(featurePopup);
					html += feature.properties.html; //We want results HTML to be in ascending order of distance
				}		
				html += featureCol.properties.htmlFooter;
					
				this.map.zoomToExtent(popupBounds.scale(1.4), true); //Add some padding and find zoom level as we will probably be using cached tiles
				
				
				var icon = new OpenLayers.Icon('images/zoomToIcon.gif', new OpenLayers.Size(34, 34), null, function(size) {
					return new OpenLayers.Pixel(-12, 2-size.h);
				});
				if( this.currentPosition ) {
					this.currentLocation.marker = this.createMarker(this.currentPosition, icon);
				}
				
				var numPopups = this.query.popups.length;
				for (var i = numPopups -1 ; i > -1; --i) {
					this.map.addPopup(this.query.popups[i]); // Reverse order to give nearest resutls highest z-index.
				}
				var displayName = this.query.layer;
				var htmlResponse = html;
			}
			
			this.mapElement.fire("astun:resultsReceived", {'type': type, 'displayName': displayName, 'html': htmlResponse} );
				
		} ,
		//sfeatureName: name of polygon feature on layer		
		'zoomToFeature': function( sFeatureName )
		{
		/**
		* Function: zoomToFeature 
		* Zoom-in on polygon with the specified name to occupy the best maximum map view region
		*
		* Parameters:
		* sFeatureName - {string} matching polygon.attributes.name
		*
		* Returns:
		*/
			if( !this.thematicLayer ) {
				this.thematicLayer = $A( this.map.layers ).find( function( layer ){
					return layer.CLASS_NAME == 'OpenLayers.Layer.Vector';
				} );
			}
			var oFeature = null;
			if( !this.thematicLayer )
				return ;

			for( var i=0; i< this.thematicLayer.features.length; i++ )
			{
				if( this.thematicLayer.features[ i ].attributes.name == sFeatureName )
				{
					oFeature = this.thematicLayer.features[ i ];
					break ;
				}
			}
			if( !oFeature )
				return

			var boundingBox = oFeature.geometry.getBounds( );
			this.map.zoomToExtent( boundingBox,false );
		},
		'searchISMLayer': function( layer, field, value, callbackFunction ) {
			this.getGeoJSONData( 
				{
					'ServiceAction': 'SearchLayer',
					'ActiveTool': 'MultiInfo',
					'ActiveLayer': layer,
					'SearchLayer': layer,
					'SearchField': field,
					'SearchValue': value,
					'MapSource': this.mapSource
				},
				function( response, mapWrapper ) {
					if( response.unexpectedResponse ) {
						alert('No results found for this layer');// for layer search.\n\nSearchLayer: ' + layer + '\nSearchField: ' + field + '\nSearchValue: ' + value );
					}
					else {
						callbackFunction( response, 'searchISM' );
					}
				}
			 );
			
		
		}
		
		
		
	} );	
	
} )( );


