/*
Sky.GoogleMaps
copyright by MySign AG (http://www.mysign.ch), Dominik Richner, Christian Marbet
dependencies: Prototype 1.6.1
           script.aculo.us 1.8.1 effects
		   Sky.PrototypeExtensions
*/

// creating namespace
var Sky;
if ( !Sky ) Sky = {};

Sky.GoogleMaps = Class.create();

Sky.GoogleMaps._requirementsChecked = false;
Sky.GoogleMaps._REQUIRED_PROTOTYPE = '1.6.1';
Sky.GoogleMaps._REQUIRED_SCRIPTACULOUS_EFFECTS = '1.8.1';

Sky.GoogleMaps._checkRequirements = function ()
{
	if( !Sky.GoogleMaps._requirementsChecked )
	{
		// checking Sky.PrototypeExtensions
		if( Object.isUndefined( Sky.PrototypeExtensions ) )
		{
			throw( "Sky.GoogleMaps requires the Sky.PrototypeExtensions JavaScript framework" );
		}
		// checking Prototype
		if( !Sky.PrototypeExtensions.isPrototypeLoaded( Sky.GoogleMaps._REQUIRED_PROTOTYPE ) )
		{
			throw( "Sky.GoogleMaps requires the Prototype JavaScript framework >= " + Sky.GoogleMaps._REQUIRED_PROTOTYPE );
		}
		// checking script.aculo.us effects
		if( !Sky.PrototypeExtensions.isScriptaculousEffectsLoaded( Sky.GoogleMaps._REQUIRED_SCRIPTACULOUS_EFFECTS ) )
		{
			throw( "Sky.GoogleMaps requires the script.aculo.us effects JavaScript framework >= " + Sky.GoogleMaps._REQUIRED_SCRIPTACULOUS_EFFECTS );
		}
		
		Sky.GoogleMaps._requirementsChecked = true;
	}
}

Sky.GoogleMaps.prototype =
{
	initialize: function( id, opts )
	{
		// checking requirements
		Sky.GoogleMaps._checkRequirements();
		
		this.id = id;
		this.webroot = skyGoogleMapsWebRoot;
		this.language = skyGoogleMapsLanguagePath;
		
		this.options = {
			notSupportedBrowserCode: null,
			
			defaultLat: 0,
			defaultLng: 0,
			defaultZoomLevel: 15,
			mapControls: [
				false, // GSmallMapControl: Erstellt ein Bedienelement mit Schaltflächen zum Schwenken in vier Richtungen sowie zum Heran- und Herauszoomen.
				true, // GLargeMapControl: Erstellt ein Bedienelement mit Schaltflächen zum Schwenken in vier Richtungen sowie zum Heran- und Herauszoomen, ausserdem einen Zoom-Schieberegler.
				false, // GSmallZoomControl: Erstellt ein Bedienelement mit Schaltflächen zum Heran- und Herauszoomen.
				false, // GScaleControl: Erstellt ein Bedienelement, das den Kartenmassstab anzeigt.
				false, // GMapTypeControl: Erstellt ein Standardkartentyp-Bedienelement zum Auswählen und Umschalten zwischen unterstützten Kartentypen mittels Schaltflächen.
				true, // GMenuMapTypeControl: Erstellt ein Dropdown-Kartentyp-Bedienelement zum Umschalten zwischen unterstützten Kartentypen.
				false, // GHierarchicalMapTypeControl: Erstellt ein verschachteltes Kartentyp-Bedienelement zum Auswählen und Umschalten zwischen unterstützten Kartentypen mittels Schaltflächen und verschachtelter Kontrollkästchen.
				true // GOverviewMapControl: Erstellt eine zusammenklappbare Übersichtskarte in der Ecke der Hauptkarte als Positions- und Navigationshilfe (durch Ziehen). Das GOverviewMapControl erstellt eine Übersichtskarte mit einem ein Pixel breiten schwarzen Rahmen. Hinweis: Im Gegensatz zu anderen Bedienelementen können Sie dieses Bedienelement lediglich in der unteren rechten Ecke der Karte platzieren (G_ANCHOR_BOTTOM_RIGHT). 
			],
			useKeyboardHandler: true,
			enableScrollWheelZoom: true,
			//no use in v3 enableGoogleBar: false, 
			//no use in v3 copyright: null, // can contain html code
			
			showElementsAfterLoad: true,
			searchObj: null, // Form#serialize( { hash: true } )
			callbackAfterSearch: null,
			callbackDragStart: null,
			callbackAfterDragEnd: null,
			callbackMarkerAdded: null, // this method is called after adding a marker (params: marker, element)
			callbackPolylineAdded: null, // this method is called after adding a polyline (params: polyline, element)
			callbackPolygonAdded: null, // this method is called after adding a polygon (params: polygon, element)
			callbackMarkerMouseOver: null,
			callbackMarkerMouseOut: null,
			callbackPolylineMouseOver: null,
			callbackPolylineMouseOut: null,
			callbackPolygonMouseOver: null,
			callbackPolygonMouseOut: null,
			gPolygonOptions: { 'clickable': false }, // defaultoptions for GPolygon. can be empty {} or null
			
			markLocationAfterLoad: false, // Marked given Location (markLocationSearchString) after load of googlemaps
			markLocationSearchString: null, // this location will be marked after load if markLocationAfterLoad is true
			markLocationInfoWindowText: null, // this text will be shown after load in infowindow if markLocationAfterLoad is true
			maxOpenedInfoWindows: 1,
			forAdmin : false
		};
		Object.extend( this.options, opts || {} );
		
		this.googleMapMarkerManagerOptions = {
				"maxZoom": 0,
				"trackMarkers" : false
		};
		
		this.openInfoWindows = [];
		
		// load GoogleMaps
		this._initMap();
	},
	
	/**
	 * This Method show elements on map that are found with the given search object.
	 * 
	 * @param searchObj Form#serialize( { hash: true } )
	 */
	showElements: function ( searchObj )
	{
		new Ajax.Request( this.webroot + 'myinterfaces/' + this.language + '/sky/geo/elements.json', {
			method: "get",
			parameters: searchObj,
			onComplete: this._showElementsOnComplete.bind( this )
		} );
	},
	
	showElement: function ( element )
	{
		switch ( element.type )
		{
			case 1:
				this._showPoint( element );
				break;
			case 2:
				this._showPolyline( element );
				break;
			case 3:
				this._showPolygon( element );
				break;
			default:
				throw( 'Unknown element-type. Currently only 1-3 allowed.' );
				break;
		}
	},
	
	removeAllElements: function ()
	{
		if ( this.polylines != null )
		{
			this.polylines.each( function ( polyline ) {
				polyline.setMap( null );
			} );
		}
		if ( this.polygones != null )
		{
			this.polygones.each( function ( polygone ) {
				polygone.setMap( null );
			} );
		}
		if ( this.markers != null )
		{
			this.markers.each( function ( marker ) {
				marker.setMap( null );
			} );
		}
		this.googleMapMarkerManager.clearMarkers();
	},
	
	markLocation: function ( locationString, locationInfoWindowText )
	{
		if ( locationInfoWindowText == null )
		{
			locationInfoWindowText = locationString;
		}
		
		if ( locationString != null && locationString.length > 0 )
		{
			var geocoder = new google.maps.Geocoder();
			geocoder.geocode( { 'address': locationString}, function( results,  status) {
				if (status == google.maps.GeocoderStatus.OK)
				{				
					this.googleMap.setCenter( results[0].geometry.location );
					var marker = new google.maps.Marker({
			              map: this.googleMap, 
			              position: results[0].geometry.location
			          });
					this.markers.push(marker);
					this.openInfoWindow( marker, '<b>' + locationInfoWindowText + '</b>' );
				}
				else
				{
			          alert("Geocode was not successful for the following reason: " + status);
		        }

			}.bind( this ) );
		}
	},
	
	openInfoWindow: function ( marker, content )
	{
		var infoWindowPosition = marker.infoPosition;
		
		var alreadyOpen = false;
		for ( var i = 0; i < this.openInfoWindows.length; i++ )
		{
			if( this.openInfoWindows[i].getPosition().equals( infoWindowPosition ) )
			{
				alreadyOpen = true;
				break;
			}
		}
		
		if ( !alreadyOpen )
		{
			if ( this.openInfoWindows.length >= this.options.maxOpenedInfoWindows )
			{
				var tmpWindows = [];
				
				for ( var i = 0; i < this.openInfoWindows.length; i++ )
				{
					if ( this.openInfoWindows.length - i < this.options.maxOpenedInfoWindows )
					{
						tmpWindows.push( this.openInfoWindows[i] );
					}
					else
					{
						this.openInfoWindows[i].close();
					}
				}
				
				this.openInfoWindows = tmpWindows;
			}
			
			var infoWindow = new google.maps.InfoWindow();
			infoWindow.setContent( content );
			infoWindow.setPosition( infoWindowPosition );
			
	    	google.maps.event.addListener( infoWindow, 'closeclick', function () {
	    		var tmpOpenInfowindows = [];
	    		for ( var i = 0; i < this.openInfoWindows.length; i++ )
	    		{
	    			if( !this.openInfoWindows[i].getPosition().equals( infoWindowPosition ) )
	    			{
	    				tmpOpenInfowindows.push( this.openInfoWindows[i] );
	    			}
	    		}
	    		this.openInfoWindows = tmpOpenInfowindows;
	    	}.bind( this ));
	    	
	    	this.openInfoWindows.push( infoWindow );
			
			infoWindow.open( this.googleMap );
		}
	},
	
	_showElementsOnComplete: function ( response )
	{
		if ( response.status == 200 )
		{
			if ( !response.responseText.isJSON () )
			{
				throw( 'Response is not a JSON-Obj.' );
				return false;
			}
			
			this.removeAllElements();
			// the request was successful - extract the response data
			var data = response.responseText.evalJSON ();
			
			if ( data.elements && data.elements != null && data.elements.length > 0 )
			{
				for ( var i = 0; i < data.elements.length; i++ )
				{
					var element = data.elements[i];
					this.showElement( element );
				}
				
				if ( data.elements.length == 1 && data.elements[0].points.length > 0 )
				{
					this.googleMap.setCenter( new google.maps.LatLng( data.elements[0].points[0].lat, data.elements[0].points[0].lng ) );
				}
			}
			
			if ( this.options.callbackAfterSearch )
			{
				this.options.callbackAfterSearch( data.elements );
			}
		}
		else
		{
			throw( "Can't get elements." );
		}
	},
	
	/**
	 * Internal method to init GoogleMaps after loading
	 */
	_initMap: function ()
	{
		// check if google maps support the current Browser
//		if ( !GBrowserIsCompatible() )
//		{
//			if ( this.options.notSupportedBrowserCode != null )
//			{
//				$(this.id).update( this.options.notSupportedBrowserCode );
//			}
//			return;
//		}
		var mapOpts = {
				// enable scroll wheel zoom on map
				scrollwheel : this.options.enableScrollWheelZoom,
				keyboardShortcuts : this.options.useKeyboardHandler,
				mapTypeId: google.maps.MapTypeId.ROADMAP,
				scaleControl : this.options.mapControls[3],
				mapTypeControl : this.options.mapControls[5],
				mapTypeControlOptions : this.options.mapControls[5] ? {mapTypeIds : [google.maps.MapTypeId.HYBRID, google.maps.MapTypeId.ROADMAP, google.maps.MapTypeId.SATELLITE, google.maps.MapTypeId.TERRAIN], position : google.maps.ControlPosition.TOP_RIGHT, style : google.maps.MapTypeControlStyle.DROPDOWN_MENU } : {}
			};
		// init map
		this.googleMap = new google.maps.Map( $(this.id), mapOpts );
		this.googleMap.setCenter( new google.maps.LatLng( this.options.defaultLat, this.options.defaultLng ) );
		this.googleMap.setZoom( this.options.defaultZoomLevel );
		
		// add map controls
//		if ( this.options.mapControls[0] )
//		{
//			this.googleMap.addControl( new GSmallMapControl() );
//		}
//		if ( this.options.mapControls[1] )
//		{
//			this.googleMap.addControl( new GLargeMapControl() );
//		}
//		if ( this.options.mapControls[2] )
//		{
//			this.googleMap.addControl( new GSmallZoomControl() );
//		}
//		if ( this.options.mapControls[4] )
//		{
//			this.googleMap.addControl( new GMapTypeControl() );
//		}
//		if ( this.options.mapControls[5] )
//		{
//			this.googleMap.addControl( new GMenuMapTypeControl() );
//		}
//		if ( this.options.mapControls[6] )
//		{
//			this.googleMap.addControl( new GHierarchicalMapTypeControl() );
//		}
//		if ( this.options.mapControls[7] )
//		{
//			this.googleMap.addControl( new GOverviewMapControl() );
//		}
		
		// add map keyboard handler
//		if ( this.options.useKeyboardHandler )
//		{
//			new GKeyboardHandler( this.googleMap );
//		}
		
		// to define the min zoom level on map
//		G_NORMAL_MAP.getMinimumResolution = this._getMinZoomLevel.bind( this );
//		
//		G_SATELLITE_MAP.getMinimumResolution = this._getMinZoomLevel.bind( this );
//		
//		G_HYBRID_MAP.getMinimumResolution = this._getMinZoomLevel.bind( this );
//		
		// init GIcon's
		this.GICONS = new Hash();
		this.polygones = new Array();
		this.polylines = new Array();
		this.markers = new Array();
		for ( var i = 0; i < skyGoogleMapsIconSet.length; i++ )
		{
			var iconData = skyGoogleMapsIconSet[i];
			var icon = {
				"iconId": iconData.iconId,
				"gIcons": [
				]
			};
			
			var iconImgs = iconData.imgs;
			for ( var x = 0; x < iconImgs.length; x++ )
			{
				var iconImgData = iconImgs[x];
				
				var img = {
					"gIcon": new google.maps.MarkerImage(),
					"gShaddow": new google.maps.MarkerImage(),
					"minZoomLevel": iconImgData.level,
					"maxZoomLevel": iconImgData.maxZoomLevel
				};
				
				if ( this.googleMapMarkerManagerOptions.maxZoom < img.maxZoomLevel )
				{
					this.googleMapMarkerManagerOptions.maxZoom = img.maxZoomLevel;
				}
				
				img.gIcon.url = iconImgData.iconImg;
				img.gIcon.size = new google.maps.Size( iconImgData.iconImgWidth, iconImgData.iconImgHeight );
				img.gIcon.anchor = new google.maps.Point( iconImgData.infoWindowAnchorX, iconImgData.infoWindowAnchorY );
				
				img.gShaddow.url = iconImgData.shadowImg;
				img.gShaddow.size = new google.maps.Size( iconImgData.shadowImgWidth, iconImgData.shadowImgHeight );
				img.gShaddow.anchor = new google.maps.Point( iconImgData.infoWindowAnchorX, iconImgData.infoWindowAnchorY );
				
				icon.gIcons.push( img );
			}
			
			this.GICONS.set( iconData.iconId, icon );
		}
		
		// init MarkerManager
		this.googleMapMarkerManager = new MarkerManager( this.googleMap, this.googleMapMarkerManagerOptions );
		
		google.maps.event.addListener(this.googleMapMarkerManager.projectionHelper_, 'ready', function () {
			// load elements and show on map
			if ( this.options.showElementsAfterLoad )
			{
				this.showElements( this.options.searchObj );
			}
			
			// mark location on the map
			if ( this.options.markLocationAfterLoad )
			{
				this.markLocation( this.options.markLocationSearchString, this.options.markLocationInfoWindowText );
			}
		 }.bind( this ) );
	},
	
	_getMinZoomLevel: function ()
	{
		return skyGoogleMapsMinZoomLevel;
	},
	
	_showPoint: function ( element )
	{
		if ( element.points.length > 0 )
		{
			var point = new google.maps.LatLng( element.points[0].lat, element.points[0].lng );
			var icon = this.GICONS.get( element.geoiconid );
			
			if ( icon != null )
			{
				for ( var i = 0; i < icon.gIcons.length; i++ )
				{
					var gIconData = icon.gIcons[i];
					var markerOpts = {
						"position": point,
						"icon": gIconData.gIcon,
						"shadow" : gIconData.gShaddow,
						"clickable": true, 
						"draggable" : this.options.forAdmin
					};
					var marker = new google.maps.Marker( markerOpts );
					marker.infoPosition = point;
					
					if ( this.options.forAdmin )
					{
						if ( this.options.callbackAfterDragEnd != null )
						{
							google.maps.event.addListener( marker, "dragend", this.options.callbackAfterDragEnd );
						}
					}
					
					if ( this.options.callbackMarkerMouseOver != null )
					{
						google.maps.event.addListener( marker, "mouseover", this.options.callbackMarkerMouseOver );
					}
					if ( this.options.callbackMarkerMouseOut != null )
					{
						google.maps.event.addListener( marker, "mouseout", this.options.callbackMarkerMouseOut );
					}
					
					this.googleMapMarkerManager.addMarker( marker, gIconData.minZoomLevel, gIconData.maxZoomLevel );
					
					if ( this.options.callbackMarkerAdded != null )
					{
						this.options.callbackMarkerAdded( marker, element );
					}
				}
			}
		}
	},
	
	_showPolyline: function ( element )
	{
		if ( element.design != null && element.points.length > 1 )
		{
			var gLatLngs = [];
			for ( var i = 0; i < element.points.length; i++ )
			{
				var point = element.points[i];
				gLatLngs.push( new google.maps.LatLng( point.lat, point.lng ) );
			}
			var polyopts = {
					"path" : gLatLngs,
					"strokeColor" : "#" + element.design.color,
					"strokeWeight" : element.design.thickness,
					"strokeOpacity" : element.design.opacity
			};
			var polyline = new google.maps.Polyline( polyopts );
			polyline.infoPosition = gLatLngs[0];
			element.polyline = polyline;
			
			polyline.setMap( this.googleMap );
			//this.googleMap.addOverlay( polyline );
			this.polylines.push( polyline );
			if ( this.options.forAdmin )
			{
				for( var i = 0; i < gLatLngs.length; i++ )
				{
					var title = "" + (i+1);
					var markerOpts = {
							"position": gLatLngs[i],
							"clickable": true, 
							"draggable" : true,
							"title" : title
						};
						var marker = new google.maps.Marker( markerOpts );
						
						if ( this.options.callbackDragStart != null )
						{
							google.maps.event.addListener( marker, "dragstart", this.options.callbackDragStart );
						}
						
						if ( this.options.callbackAfterDragEnd != null )
						{
							google.maps.event.addListener( marker, "dragend", this.options.callbackAfterDragEnd );
						}
						
						if ( this.options.callbackMarkerMouseOver != null )
						{
							google.maps.event.addListener( marker, "mouseover", this.options.callbackMarkerMouseOver );
						}
						if ( this.options.callbackMarkerMouseOut != null )
						{
							google.maps.event.addListener( marker, "mouseout", this.options.callbackMarkerMouseOut );
						}
						
						marker.setMap( this.googleMap );
						this.markers.push(marker);
				}
			}
			
			if ( this.options.callbackPolylineMouseOver != null )
			{
				google.maps.event.addListener( polyline, "mouseover", this.options.callbackPolylineMouseOver );
			}
			if ( this.options.callbackPolylineMouseOut != null )
			{
				google.maps.event.addListener( polyline, "mouseout", this.options.callbackPolylineMouseOut );
			}
			
			if ( this.options.callbackPolylineAdded != null )
			{
				this.options.callbackPolylineAdded( polyline, element );
			}
		}
	},
	
	_showPolygon: function ( element )
	{
		if ( element.design != null && element.points.length > 1 )
		{
			var gLatLngs = [];
			for ( var i = 0; i < element.points.length; i++ )
			{
				var point = element.points[i];
				gLatLngs.push( new google.maps.LatLng( point.lat, point.lng ) );
			}
			gLatLngs.push( gLatLngs[0] );
			
			var design = element.design;
			
			var polyopts = {
					"path" : gLatLngs,
					"clickable": true, 
					"strokeColor" : "#" + design.color,
					"strokeWeight" : design.thickness,
					"strokeOpacity" : design.opacity,
					"fillColor" : "#" + design.fillcolor,
					"fillOpacity" : design.fillopacity
			};
			Object.extend( this.options.gPolygonOptions, polyopts || {} );
			
			var polygon = new google.maps.Polygon( this.options.gPolygonOptions );
			polygon.infoPosition = gLatLngs[0];
			element.polygon = polygon;
			
			if ( this.options.callbackPolygonMouseOver != null )
			{
				google.maps.event.addListener( polygon, "mouseover", this.options.callbackPolygonMouseOver );
			}
			if ( this.options.callbackPolygonMouseOut != null )
			{
				google.maps.event.addListener( polygon, "mouseout", this.options.callbackPolygonMouseOut );
			}
			polygon.setMap( this.googleMap );
			
			this.polygones.push( polygon );
			if ( this.options.forAdmin )
			{
				for( var i = 0; i < gLatLngs.length; i++ )
				{
					var title = "" + (i+1);
					var markerOpts = {
							"position": gLatLngs[i],
							"clickable": true, 
							"draggable" : true,
							"title" : title
						};
						var marker = new google.maps.Marker( markerOpts );
						
						if ( this.options.callbackDragStart != null )
						{
							google.maps.event.addListener( marker, "dragstart", this.options.callbackDragStart );
						}
						if ( this.options.callbackMarkerMouseOver != null )
						{
							google.maps.event.addListener( marker, "mouseover", this.options.callbackMarkerMouseOver );
						}
						if ( this.options.callbackMarkerMouseOut != null )
						{
							google.maps.event.addListener( marker, "mouseout", this.options.callbackMarkerMouseOut );
						}
						
						if ( this.options.callbackAfterDragEnd != null )
						{
							google.maps.event.addListener( marker, "dragend", this.options.callbackAfterDragEnd );
						}
						if ( this.options.callBackAfterMouseOver != null )
						{
							google.maps.event.addListener( marker, "dragend", this.options.callbackAfterDragEnd );
						}
						marker.setMap( this.googleMap );
						this.markers.push(marker);
				}
			}
			
			
			if ( this.options.callbackPolygonAdded != null )
			{
				this.options.callbackPolygonAdded( polygon, element );
			}
		}
	}
};
