/**
 * @author peter.goulborn
 * Requires OpenLayers, Prototype, atExtendOpenLayers.js
 *
 * $Revision: 147 $
 * $Author: Matt.walker $
 *
 * 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.
			 * -----
			 */
			options = options || [];
			this.ismurl = ismurl;
			this.dataurl = dataurl;
			this.imageRatio= 1.4;
			this.getMapService = new Astun.JS.GetData.JSONRequester(
				this.ismurl,
				'callback',
	    		{
	    			'params': {
						'Type': 'jsonp'
					}
				}
			);
			this.getDataService = new Astun.JS.GetData.JSONRequester(
				this.dataurl,
				'callback',
	    		{
	    			'params': {
						'Type': 'jsonp'
					}
				}
			);

			if( !Astun.appType ) Astun.appType = '';

			this.mapElement = $( elementId );
			this.locationZoom = window.astun.settings.locationZoomWidth || 500; // 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( { 'numDigits': 0 } );
			this.installedControls.keys = new OpenLayers.Control.KeyboardDefaults( );
			this.installedControls.attrib = new OpenLayers.Control.Attribution( );
			this.installedControls.pzb = Astun.appType == 'lite' ? new OpenLayers.Control.PanZoom( ) : 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.openlayers.updateSize( );
			}

			/* Event listeners */
			Event.observe( this.mapElement, 'astun:mapResize', mapResize.bindAsEventListener( this ) );



		},
		'addControl': function( controlName, control ) {
			this.openlayers.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.openlayers.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;
			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.openlayers = new OpenLayers.Map(
				this.mapElement,
				mapOptions
			 );
			this.map = this.openlayers; //backwards compatibility
			this.openlayers.wrapper = this; // this is needed but ideally shouldn't be necessary...


			for( var c = 0; c < this.openlayers.controls.length; c++ ) {
				this.openlayers.controls[ c ].activate( );
			}

			var defaultLayerOptions =  {
				'projection': this.openlayers.projection,
				'units': this.openlayers.units,
				'maxExtent': this.openlayers.maxExtent
			}
			while( resScaleProperties.length ) {
				var property = resScaleProperties.pop( );
				var value = this.openlayers[ 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': this.imageRatio,
						'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.mapSource = dataMapSource;

				this.openlayers.addLayer( this.ismDataLayer );
				this.openlayers.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;
				var rasterLayerName = definition.name;
				var rasterType = ( definition.type ) ? definition.type : 'WMS';

				if( this.rasterLayer ) {
					var oldRasterLayer = this.rasterLayer;
					var oldResolution = this.map.getResolution();
				}

				var rasterOptions = Object.extend( {}, defaultLayerOptions );
				var wmsOptions = {
					'layers': rasterLayerName
				}
				if( definition.options ) {
					rasterOptions = Object.extend( rasterOptions, definition.options );
					wmsOptions.FORMAT = definition.options.format;
				}
				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;
				if( definition.tilecaches && definition.tilecaches.length) {
					rasterType = 'TileCache';
				}


				switch( rasterType ) {
					case 'WMS':
						this.rasterLayer = new OpenLayers.Layer.WMS(
							'iShareMaps Raster Layer',
							definition.uri,
							wmsOptions,
							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.rasterLayer.events.register('loadend', this.rasterLayer, function(evt) {
					jQuery('#' + this.map.wrapper.mapElement.id ).trigger('reasterLayerLoaded');
					if(!astun) {window.astun = {};}
					astun.isReasterLayerLoaded = true;
				})
				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.openlayers.maxResolution, 'maxExtent': this.openlayers.maxExtent} );
			this.openlayers.markerLayer = this.markerLayer; //map.markerLayer is deprecated please use markerlayer instead.
			this.openlayers.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.draw( bounds.toArray() );
				}
			}
			if( !options.deferDraw ) {
				this.mapElement.fire( 'astun:loadSetting', {setting: 'mapView', loadFunction: setView.bind( this )} );
			}


			this.openlayers.events.register( "moveend", this, function( ) {
				// Create iSharemaps 'view' from centre point plus image width and resolution
				var en = this.openlayers.getCenter( );
				var imageSize = this.ismDataLayer.getImageSize( );
				var view = {
					'easting': en.lon,
					'northing': en.lat,
					'scale': this.openlayers.getScale(),
					'extent': this.openlayers.getExtent().toArray(),
					'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 setPosition = function( evt ) {
				/**
				 * Function: setPosition
				 * Event wrapper for this.draw
				 *
				 * 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 ) {
					this.currentPosition = new OpenLayers.LonLat( evt.memo.x, evt.memo.y );
					this.draw({
						'easting': evt.memo.x,
						'northing': evt.memo.y,
						'zoom': this.locationZoom
					} );
				}
            }

            var setAddress = function( evt ) {
				/**
				 * Function: setAddress
				 * Event wrapper for this.showLocation and setPosition
				 *
				 * 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.currentLocation = evt.memo;
					this.showLocation( en );
					Astun.JS.Common.setCookie('astun:currentLocation', Object.toJSON( evt.memo ), 100 ,(astun.intranetApp) ? '' : '/', '');
					setPosition.bindAsEventListener( this )( evt );
				}
            }

			Event.observe( this.mapElement, 'astun:mapMove', mapMove.bindAsEventListener( this ) );
			Event.observe( this.mapElement, 'astun:setAddress', setAddress.bindAsEventListener( this ) );
			//Change the cursor during the ajax calls
			//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 );
				$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('moveMap', this, function( evt, location ) {
					evt.data.draw( location );
				})
				.bind('setAddress', this, function( evt, address ) {
					evt.data.setAddress( address );
				})
				.bind('searchISM', this, function( evt, layer, field, value ) {
					evt.data.searchISMLayer( layer, field, value,  function( response, type, queryOptions ) {
						evt.data.showResults( response, type, queryOptions );
					} );
					//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('resizeMapElement', this, function( evt,mapWidth){
						var centre = evt.data.map.getCenter(); //lonlat of pre-resize map

						if(mapWidth && mapWidth!='') $eventElement.width(mapWidth);
						if( centre ) {
							var view = {easting: centre.lon, northing: centre.lat};
							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.openlayers.markerLayer.addMarker( marker );
			//set marker layer to be top one
			this.openlayers.markerLayer.setZIndex( this.openlayers.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.openlayers.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.openlayers.popups.length > 0 ) {
				while( this.openlayers.popups.length )
				{
					var popup = this.openlayers.popups.pop( );
					this.openlayers.removePopup( popup );
					popup.destroy( );
					popup = null;
				}
			}
		},
		'getPopup': function(popupId) {
		/**
		* Function: getPopup
		* Takes the id and returns the popup with that id (Added by aamir.afridi - 23 July 2010)
		*
		* Parameters:
		* popupId - {string} The Id of the poup required
		* Returns: popupArr- {array} Array of all the popups (Openlayers.Poup) found
		*/
			if( this.openlayers.popups.length > 0 )
			{
				popupArr = new Array();
				for(i=0;i<this.openlayers.popups.length;i++)
				{
					var popup = this.openlayers.popups[i];
					if(jQuery(popup.div).attr('rel')==popupId || jQuery(popup.div).attr('rev')==popupId)
					{
						popupArr.push(popup);
					}
				}
			}
			return popupArr;
		},
		'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} co-ordinates with old iShareMaps 'zoom': { easting, northing, zoom }
		*		- {Object} co-ordinates with scale: { easting, northing, scale }
		*		- [Array] array of bounding box co-ordinates: [ minX, minY, maxX, maxY ]
		* Returns:
		*/

			if( view && ( view.easting || view.northing ) ) {
					// recentre map on provided co-ordinates
				var newCentre = OpenLayers.Util.extend( this.openlayers.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.scale ) {
					view.scale = view.scale - 0.0;
					var resolution =  view.scale * this.openlayers.getResolution() / this.openlayers.getScale();
				}
				else if( view.zoom && view.zoom != imageSize.w * currentResolution ) {
					var resolution = view.zoom / imageSize.w;
				}
				else {
					var resolution = currentResolution;
				}
				this.openlayers.setCenter(
					newCentre,
					this.openlayers.getZoomForResolution( resolution ),
					false,
					true
				);
			}
			else if( view && Astun.JS.Common.isArray( view ) && view.length === 4) {
				var newBounds = OpenLayers.Bounds.fromArray( view );
				this.openlayers.zoomToExtent( newBounds );
			}
			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.mapName,
				'RequestType': 'GeoJSON',
				'ServiceAction': '',
				'ActiveTool': '',
				'ActiveLayer': '',
				'mapid': -1,
				'axuid': new Date( ).valueOf( )
			};
			var parameters = Object.extend( defaultParams, params );
			var thisPointer = this ;
			var queryOptions = {'params': parameters, 'url':this.getMapService.uri, 'isFirstResult':true}

			this.mapElement.fire( 'astun:dataLoadBegin', {} );

			this.getMapService.request(
				parameters,
				function( json ) {
					if( json[0] && json[0].type && json[0].type == 'FeatureCollection' ) {
						callbackFunction( json, thisPointer, queryOptions );
					}
					else if( json.error ) {
						callbackFunction( { 'unexpectedResponse': json.error.response, 'error': json.error }, thisPointer ) ;
					}
					else {
						callbackFunction( { 'unexpectedResponse': 'Unknown response', 'response': json }, thisPointer ) ;
					}
					this.mapElement.fire( 'astun:dataLoadComplete', {} );
				}.bind( this )
			 );

			/*
			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
			 */
		},

		'getResultsPerLayer' : function(serverErrorLayers,isFirstTime,numOfLayers,filteredArr,parameters,thisPointer,queryOptions,callbackFunction)
		{
		/**
		* Function: getResultsPerLayer
		* Returns collection of underlying map features for each layer one by one
		*
		* Parameters:
		*
		* Returns:
		*/
			//Check if search is not cancelled
			var atTimer=0;

			//If layers has no item than just return
			if(filteredArr.length==0 || astun.searchCancelled)
			{
				//debug('Search finished');
				//Clear the timer
				clearTimeout(atTimer);
				//Check if the totall number of layers and the number of layers for which the no results found are equal
				if(numOfLayers==astun.numResultsFoundForLayers)
				{
					callbackFunction({ 'unexpectedResponse': 'No results found for any layer.', 'error': true }, thisPointer, queryOptions );
				}
				this.mapElement.fire( 'astun:dataLoadComplete', {} );
				//Remove the progress div from the loader
				jQuery('#atMapLoader').find('#atLoaderProgress, #atCnlSrch').remove();
				//Change Searching msg back to Loading...
				jQuery('#atMapLoader #atLoaderText').html('Loading...');
				return;
			}
			//Also add a query option to tell the result set that these are the results for the first layer
			queryOptions.isFirstResult = (isFirstTime); //Will return true on first time but will be false soon the first time a layer get result - see it is getting false in below callback function
			//Change the 'Layers' parameter of the object and just this one layer
			parameters.Layers = filteredArr[0];
			//Add the progress div to loader so user can see the progress
			jQuery('#atMapLoader').find('#atLoaderProgress, #atCnlSrch').remove();
			//var progress = (parseInt(parseInt((numOfLayers-filteredArr.length)*100,10)/numOfLayers,10)); //Progress in percentage
			var progress = (numOfLayers+1-filteredArr.length)+' of '+(numOfLayers); //Progress in layers count => Searching layer 1 of 5
			//Change loading msg to Searching...
			jQuery('#atMapLoader #atLoaderText').html('Searching');
			jQuery('#atMapLoader').append(
				'<div id="atLoaderProgress" style="font-size:11px">layer '+ progress +'</div>',
				jQuery('<a href="#" id="atCnlSrch">Cancel search</a>')
					.css({'font-size':11, 'display':'block','padding-top':3,'text-decoration':'underline','color':'#CD0A0A'})
					.click(function(){
						astun.searchCancelled = true;
						//debug('Cancel button clicked');
						astun.mapWrapper.getResultsPerLayer(serverErrorLayers,isFirstTime,numOfLayers,filteredArr,parameters,thisPointer,queryOptions,callbackFunction);
						return false;
					})
			);


			//Check the progress after 60 secs and if the progress is the same than just issue a timeout for this layer
			atTimer = setTimeout(function()
			{
				//Return if search is already cancelled
				if(astun.searchCancelled) return;
				//Add the layername to array
				serverErrorLayers.push(parameters.Layers);
				//Once we get the request, call itself after removing the Searched Layer from the array
				filteredArr.shift();
				//Call itself again
				astun.mapWrapper.getResultsPerLayer(serverErrorLayers,isFirstTime,numOfLayers,filteredArr,parameters,thisPointer,queryOptions,callbackFunction);
			}
			,60000);

			//Make a request (jsonp) to get the results
			this.getMapService.request(
				parameters,
				function(json) {
					//Return if search is already cancelled
					if(astun.searchCancelled) return;
					if(typeof json.error=='undefined')
					{
						/*Check if the layer name is already in the list of serverErrorLayers array which means that
						  result is already sent (too late sorry)*/
						if(jQuery.inArray(json[0].properties.layername, serverErrorLayers)!=-1)
						{
							//debug('Late response found for: '+ json[0].properties.layer)
							return;
						}
					}

					//First clear timer because we got the results and the request is not set timeout
					clearTimeout(atTimer);

					//Check the response for validity
					if( json[0] && json[0].type && json[0].type == 'FeatureCollection' )
					{
						callbackFunction( json, thisPointer, queryOptions);
						isFirstTime = false;//Results found so make it false for the next loop
					}
					else
					{
						astun.numResultsFoundForLayers++;
					}

					//Once we get the request, call itself after removing the Searched Layer from the array
					filteredArr.shift();
					//Call itself again
					astun.mapWrapper.getResultsPerLayer(serverErrorLayers,isFirstTime,numOfLayers,filteredArr,parameters,thisPointer,queryOptions,callbackFunction);
				}.bind(this)
			);

		},

		'getMapMultiInfo': function( coordinates, pxTolerance, queryType , callbackFunction )
		{
		/**
		* Function: getMapMultiInfo
		* Returns collection of underlying map features on mouse-click and mouse hoverPause
		*
		* Parameters:
		* coordinates - {object } / { string } OpenLayers.LonLat object OR string with all the corrdinates for a shape
		* 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:
		*/

			//Filter the results. For details look at http://intranet.astun.local/docs/development:layers_catalog_specification#info_click_issue
			//Create the cookie name
			var $eventElement = jQuery('#'+this.mapElement.id),
				mapSource = $eventElement.data('currentlyActiveMapSource').replace(/[\s\/]+/g, '_') || '',
				layersResultsCookieName = 'rLayers_'+mapSource,
				rLayers = false,
				qLayers = this.ismDataLayer.params.layers.toArray(),
				filteredArr = new Array();

			//Now load the cookie value
			this.mapElement.fire('astun:loadSetting',
			{
				setting: layersResultsCookieName,
				loadFunction:function(layers)
				{
					//Check if layers is null that means cookie is not there so create the cookie and add the layers to it
					rLayers = (typeof layers=='undefined' || layers=='undefined' || layers==null) ? false : layers;
				}
			});

			/*
			  Now we have saved all those layer's names in cookie which user don't want the results to be displayed.
			  Now loop through the query layers and check each layer against the results Layer array. If found, remove it
			*/
			if(rLayers)
			{
				qLayers.each(function(i)
				{
					if(jQuery.inArray(i, rLayers.split(','))==-1)
					{
						filteredArr.push(i);
					}
				});
			}
			else
			{
				filteredArr = qLayers;
			}

			//Do nothing if there is no layers
			if(filteredArr.length==0) return;

			//Start loader after the above IF condition
			this.mapElement.fire( 'astun:dataLoadBegin', {} );

			switch( queryType )
				{ // switch
					case 'shapeInfo' :
					{
						//shapeInfo
							var parameters =
							{
									'MapSource': this.mapSource.mapName,
									'RequestType': 'GeoJSON',
									'ServiceAction': 'GetMultiInfoFromShape',
									'ActiveTool': 'MultiInfo',
									'ActiveLayer': '', //can be left blank?
									//'Layers': 'faults',
									'Layers': filteredArr, //always use current layers
									'mapid': -1,
									'axuid': new Date( ).valueOf( ),
									'Shape':coordinates
							};
							var thisPointer = this ;

							var queryOptions = { 'params': parameters, 'url':this.getMapService.uri }

							//Check if we need results per layer instead of getting all layers together
							if(astun.intranetApp){
									//Start with zero and increment if no results found. At the end if the number of this is equal to number of layers than no results found for any layer so show an alert
									astun.numResultsFoundForLayers = 0;
									astun.searchCancelled=false;
									isFirstTime = true; //To check if this is the first time the function is called in the loop
									serverErrorLayers = [] //List of layers for which server will timeout or return with error
									astun.mapWrapper.getResultsPerLayer(serverErrorLayers,isFirstTime,filteredArr.length,filteredArr,parameters,thisPointer,queryOptions,callbackFunction);

							}
							else	{
								this.getMapService.request(
									parameters,
									function( json ) {
										if( json[0] && json[0].type && json[0].type == 'FeatureCollection' ) {
											callbackFunction( json, thisPointer, queryOptions);
										}
										else if( json.error ) {
											callbackFunction( { 'unexpectedResponse': json.error.response, 'error': json .error }, thisPointer ) ;
										}
										else {
											callbackFunction( { 'unexpectedResponse': 'Unknown response', 'response': json }, thisPointer ) ;

										}
										this.mapElement.fire( 'astun:dataLoadComplete', {} );
									}.bind( this )
								);
							}

							break;
					}
					case 'info':
						{ //info

							var parameters =
							{
									//'MapSource': 'Elmbridge/Faults',
									'MapSource': this.mapSource.mapName,
									'RequestType': 'GeoJSON',
									'ServiceAction': 'GetMultiInfoFromPoint',
									'ActiveTool': 'MultiInfo',
									'ActiveLayer': '', //can be left blank?
									//'Layers': 'faults',
									'Layers': filteredArr, //always use current layers
									'Easting':	coordinates.lon,
									'Northing':	coordinates.lat,
									'mapid': -1,
									'axuid': new Date().valueOf(),
									//'tolerance': this.openlayers.getResolution( ) * 24
									'tolerance': this.openlayers.getResolution( ) * pxTolerance
							};
							var thisPointer = this,
								queryOptions = { 'params': parameters, 'url':this.getMapService.uri };

							//Check if we need results per layer instead of getting all layers together
							if(astun.intranetApp) {
									//Start with zero and increment if no results found. At the end if the number of this is equal to number of layers than no results found for any layer so show an alert
									astun.numResultsFoundForLayers = 0;
									astun.searchCancelled=false;
									isFirstTime = true; //To check if this is the first time the function is called in the loop
									serverErrorLayers = [] //List of layers for which server will timeout or return with error
									astun.mapWrapper.getResultsPerLayer(serverErrorLayers,isFirstTime,filteredArr.length,filteredArr,parameters,thisPointer,queryOptions,callbackFunction);
							}
							else {
								this.getMapService.request(
									parameters,
									function( json ) {
										if( json[0] && json[0].type && json[0].type == 'FeatureCollection' ) {
											callbackFunction( json, thisPointer, queryOptions);
										}
										else if( json.error ) {
											callbackFunction( { 'unexpectedResponse': json.error.response, 'error': json .error }, thisPointer ) ;
										}
										else {
											callbackFunction( { 'unexpectedResponse': 'Unknown response', 'response': json }, thisPointer ) ;

										}
										this.mapElement.fire( 'astun:dataLoadComplete', {} );
									}.bind( this )
								);
							}

							break;
							/*
							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: parameters,
									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;
			var parameters =
				{
					'SearchType' : 'findMyNearest',
					'ServiceAction': 'ShowMyClosest',
					'ActiveTool': 'MultiInfo',
					'ActiveLayer': layername,
					'Distance':  distance ,
					'MaxResults': maxResults,
					'Easting':	location.lon,
					'Northing':	location.lat
				};
			this.getGeoJSONData(
				parameters,
			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.openlayers.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 previous marker and create new one
			if (this.locationMarker) {
			    this.deleteMarker(this.locationMarker);
			}
			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.locationMarker = 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, queryOptions )
		{//ShowFeatures
			if(response.unexpectedResponse) {
				var displayname = '';
				var htmlResponse = '' + response.unexpectedResponse;
			}
			else{
				var featureCollectionArray = response.slice();
				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;
				this.query.popups.length = 0; //Make it empty
				OpenLayers.Popup.FramedCloud.prototype.fixedRelativePosition = true;
				OpenLayers.Popup.FramedCloud.prototype.relativePosition = "tr";
				html += featureCol.properties.htmlHeader;
				var features = featureCol.features.slice();
				while ( features.length )
				{
					var feature = 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 ){},
						astun.settings.baseURL+'css/jQuery/'+ astun.settings.themeName + '/images/popup-bg.png'
					);
					this.query.popups.push(featurePopup);
					var temp = jQuery("<div>"+feature.properties.html+"<div>");
					var mapPinNum = "<p><strong>Map pin:</strong>" + feature.id + "</p>";
					temp.find('.infoResult').append(mapPinNum)
					html += temp.html(); //We want results HTML to be in ascending order of distance

				}
				html += featureCol.properties.htmlFooter;

				this.openlayers.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.openlayers.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, 'json': response, 'queryOptions':queryOptions} );

		} ,
		//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.openlayers.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.openlayers.zoomToExtent( boundingBox,false );
		},
		'searchISMLayer': function( layer, field, value, callbackFunction ) {
			var parameters =
				{
					'ServiceAction': 'SearchLayer',
					'ActiveTool': 'MultiInfo',
					'ActiveLayer': layer,
					'SearchLayer': layer,
					'SearchField': field,
					'SearchValue': value,
					'MapSource': this.mapSource.mapName
				};
			var thisPointer = this ;
			var queryOptions = {'params': parameters, 'url':this.getMapService.uri, 'isFirstResult':true}

			this.getGeoJSONData(
				parameters,
				function( response, mapWrapper ) {
					if( response.unexpectedResponse ) {
						alert('No results found.');// for layer search.\n\nSearchLayer: ' + layer + '\nSearchField: ' + field + '\nSearchValue: ' + value );
					}
					else {
						callbackFunction( response, 'searchISM', queryOptions );
					}
				}
			 );
		}
	});
})();

