/**
 * A Google maps API wrapper with buildin Geocoding.
 * The Map depends upon Prototype, Caching and AjaxRequest.
 *
 * @version 1.0.0
 * @author Gijs van de Nieuwegiessen
 */

/* Custom Icoon -> Hans: niet goed deze code; nog verder uitwerken....
var icon = new GIcon();
icon.image = 'C:\Data\VisuaIinterdev\Vrijheid\images\icon10.png';
icon.shadow = 'C:\Data\VisuaIinterdev\Vrijheid\images\icon10s.png';
// Dimensies van 'image' (hoogte x breedte)
icon.iconSize = new GSize(25, 25);
// Dimensies van shadow afbeelding
icon.shadowSize = new GSize(39.0, 25.0);
// Positie van icoontje
icon.iconAnchor = new GPoint(16.0, 16.0);
// Positie waar de infowindow (ballon) zich moet bevinden
icon.infoWindowAnchor = new GPoint(16.0, 16.0);

// Einde Custom Icoon -> Hans: niet goed deze code; nog verder uitwerken....
*/
 
var Map = Class.create(
{
	// Private properties
	_container: null,
	_gmap: null,
	_mapMarkers: [],
	_bounds: null,
	_options: {},
	_activeWindow: null,
	_defaultOptions: 
	{
		/**
		 * @var string  Width of the Google Map
		 */
		width: '500px',
		
		/**
		 * @var string  Height of the Google Map
		 */
		height: '500px',
		
		/**
		 * @var string Style of geocoding (client or service)
		 */
		geocodeStyle: 'client',

		/**
		 * @var string Geocoding service endpoint (if style is service)
		 */
		geocodeServiceUri: _AJAX_DEFAULT_ENDPOINT,
		
		/**
		 * @var string Geocoding service name (if style is service)
		 */
		geocodeServiceName: 'GeoService',
		
		/**
		 * @var string Geocoding service method (if style is service)
		 */
		geocodeServiceMethod: 'GeoCode',
		
		/**
		 * @var GLatLng Initial Lat Lng position of the GMap
		 */
		initLatLng:  new GLatLng(52.132633,5.291266),
		
		/**
		 * @var int Initial zoom level of the GMap
		 */
		initZoom: 6,
		
		/**
		 * @var array of GMapControls used by the GMap
		 */
		mapControls:  [new GSmallMapControl(), new GMapTypeControl()],
		
		/**
		 * @var GIcon The Marker icon
		 */
		markerIcon: new GIcon(G_DEFAULT_ICON),
//		markerIcon: new GIcon(icon),
		
		
		/**
		 * @var bool Indicates if the marker is clickable
		 */
		markerClickable: true,
		
		/**
		 * @var bool Show the index number along with the Name
		 */
		markerShowIndexInName: false,
		
		/**
		 * @var bool Show a button to zoom in on Marker
		 */
		markerShowZoomButton: true,
		
		/**
		 * @var bool Show a button that browses to a Directions page
		 */
		markerShowDirectionsButton: true,
		
		/**
		 * @var int Marker zoomed in level
		 */
		markerZoomInLevel: 16,
		
		/**
		 * @var int Zoom level that is used when 
		 * centering the Map to show all Markers
		 */
		markerBoundsZoomLevel: undefined,
		
		/**
		 * @var string Url of directions
		 */
		markerDirectionsUrl: "http://maps.google.nl/maps?saddr=&daddr={0}",
		
		/**
		 * @var Marker mouse over event handler 
		 */
		markerOnMouseClick: null,
		
		/**
		 * @var Marker mouse over event handler 
		 */
		markerOnMouseOver: null,
		
		/**
		 *  @var Marker mouse out event handler 
		 */
		markerOnMouseOut: null,
		
		/**
		 *
		 **/
		markerOpenWindowOnLoad: false,
		
		/**
		 * @var Display an info window when clicking on a Marker
		 */
		enableInfoWindow: true
	},
	
	/**
	 * Constructor
	 * @param string Id of the HTML container
	 * @param array of Options
	 * @usage new Map(htmlId[,options]);
	 */
	initialize: function(containerId, options)
	{
		// Setting up original options with default options
		Object.extend(this._options, this._defaultOptions);
		Object.extend(this._options, options);
		
		if (!GBrowserIsCompatible()) 
		{
			throw 'This Browser is not compatible with the Google Maps api';
			return;
		}
		
		if (!$(containerId)) 
		{
			throw 'The maps container with the id: ' + containerId + ' does not exist! Make sure the element exists or that this code is executed after the element is loaded! (body onload)';
		}
		
		this._bounds = new GLatLngBounds();
		this._container = $(containerId);

		this.SetWidth(this._options.width);
		this.SetHeight(this._options.height);
		
		this.bindMapEvents();
		this.preventMemoryLeaks();
		
		return this;
	},
	
	/**
	 * Display the Map
	 */
	Show: function(center)
	{
		this._gmap = new GMap2(this._container);
		this._gmap.enableDoubleClickZoom();
		this._gmap.setCenter(this._options.initLatLng, this._options.initZoom);
		
		this.addMapControls();
		
		this.Refresh(center);
	},
	
	Refresh: function(center)
	{
		var setCenter = (center==undefined) ? true : center;
		
		this.placeMapMarkers();
		
		if(setCenter)
		{
			Log('Move map towards center of added markers');
			
			this.setCenter();
		}
	},
	
	/**
	 * Add a collection of Map markers
	 * @param array MapMarker
	 */
	AddMapMarkers: function(markers)
	{
		for (var i = 0; i < markers.length; i++) 
		{
			var marker = markers[i];
			this.AddMapMarker(marker);
		}
	},
	
	/**
	 * Add a Map marker
	 * @param MapMarker marker
	 */
	AddMapMarker: function(marker)
	{
		Log('Adding a new marker');
		
		this._mapMarkers[this._mapMarkers.length] = marker;
	},
	
	/**
	 * Add a GOverlay to the Map
	 */
	AddOverlay: function(gobj)
	{
		this._gmap.addOverlay(gobj);
	},
	
	/**
	 * Remove a GOverlay from the Map
	 */
	RemoveOverlay: function(gobj)
	{
		this._gmap.removeOverlay(gobj);
	},
	
	/**
	 * Resize the width of the map
	 * @param width string
	 */
	SetWidth: function(width)
	{
		this._options.width = width;
		this._container.setStyle({'width': width});	
	},
	
	/**
	 * Resize the height of the map
	 * @param height string
	 */
	SetHeight: function(height)
	{
		
		this._options.height = height;
		this._container.setStyle({'height': height});	
	},
	
	/**
	 * @return latlng
	 */
	GetNorthEast: function()
	{
		var bounds = this._gmap.getBounds();
	    return bounds.getNorthEast();
	},
	
	/**
	 * @return latlng
	 */
	GetSouthWest: function()
	{
		var bounds = this._gmap.getBounds();
	    return bounds.getSouthWest();
	},
	
	/**
	 * Center map to display all markers.
	 */
	setCenter: function()
	{
		if(this._bounds.isEmpty())
		{
			Log('setCenter: No bounds to use to calculate center..');
			
			return;
		}
		
		var zoomLevel = this._options.markerBoundsZoomLevel;
		
		if( !zoomLevel )
		{
			Log('setCenter: no zoomlevel, get bounds zoomlevel');
			
			zoomLevel = this._gmap.getBoundsZoomLevel(this._bounds);
		}
		
		
		this._gmap.setZoom(zoomLevel);
		this._gmap.setCenter(this._bounds.getCenter());
	},
	
	/**
	 * Add GMap controls, if any...
	 */
	addMapControls: function()
	{
		for (var i = 0; i < this._options.mapControls.length; i++) 
		{
			this._gmap.addControl(this._options.mapControls[i]);
		}
	},
	
	/**
	 * Create all markers on the Map
	 */
	placeMapMarkers: function()
	{
		for (var i = 0; i < this._mapMarkers.length; i++) 
		{
			var marker = this._mapMarkers[i];
			
			// Huh?
			if(marker == null) continue;
			
			if (marker.Lat && marker.Lng) 
			{
				this.placeMapMarker(marker);
			}
			else 
			{
				// Search address and add Marker
				this.geocode(marker);
			}
		}
	},
	
	/**
	 * Place a marker on the Map
	 * @param MapMarker marker
	 */
	placeMapMarker: function(marker)
	{
		var point = new GLatLng(marker.Lat, marker.Lng);
		this._bounds.extend(point);
		
		var scope = this;
		var gmarker = new GMarker(point, {icon: this._options.markerIcon, clickable: this._options.markerClickable});
		
		if(this._options.enableInfoWindow)
		{
			GEvent.addListener(gmarker, "click", function(e)
			{
				this.openInfoWindowHtml(scope.createWindowHTML(marker));
			});
		}
		else 
		{
			GEvent.addListener(gmarker, "click", function(latlng)
			{
				if( !scope._options.markerOnMouseClick )
					return;
				
				scope._options.markerOnMouseClick(gmarker, marker, latlng);
			});
		}
				
		GEvent.addListener(gmarker, "mouseover", function() 
		{
			if( !scope._options.markerOnMouseOver )
				return;
			
			scope._options.markerOnMouseOver(gmarker, marker);
       	});  
		
		GEvent.addListener(gmarker, "mouseout", function() 
		{
			if( !scope._options.markerOnMouseOut )
				return;
					
			scope._options.markerOnMouseOut(gmarker, marker);
		}); 
		
		// Add marker to map
		this.AddOverlay(gmarker);
		
		
		if(this._options.markerOpenWindowOnLoad)
		{
			gmarker.openInfoWindowHtml(scope.createWindowHTML(marker));
		}
	},
	
	/**
	 * Try to Geocode a MapMarker that has no Lat/Lng
	 * @param MapMarker marker
	 */
	geocode: function(marker)
	{
		if (marker.Street.toLowerCase() == "postbus") 
		{
			// No way we can encode a PO box
			Log('geocode: pobox, cannot be decoded');
			return;
		}
		
		var scope = this;
		
		switch (this._options.geocodeStyle)
		{
			case 'service':
			
				Log('geocode: using custom geocode service');
				
				// JSON service geocoding
				AjaxRequest(this._options.geocodeServiceUri, this._options.geocodeServiceName, this._options.geocodeServiceMethod, 
				{
					index: marker.Index,
					id: marker.Id,
					street: marker.Street,
					housenumber: marker.Housenumber,
					postalcode: marker.Postalcode,
					city: marker.City,
					description: marker.Description
				}, 
				function(response)
				{
					var result = response.responseJSON;
					
					if (!result) 
					{
						throw 'AjaxRequest not receive a result ' + response.responseText;
						return;
					}
					
					if (!marker) 
					{
						throw 'AjaxRequest no marker.. ';
						return;
					}
					
					marker.Lng = result['lng'];
					marker.Lat = result['lat'];
					
					scope.placeMapMarker(marker);
					scope.setCenter();
				});
				break;
				
			default:
				
				Log('geocode: using client side (javascript) geocode service');
				
				// Clientside Geocoding
				var geocoder = new GClientGeocoder();
				var address = marker.Address();
				
				geocoder.getLocations(address, function(response) 
				{
					if (!response || response.Status.code != 200) {
				    	Log('geocode: GClientGeocoder error: ' + response.Status.code + ' no geocoded point for address: ' + address );
				    	return;
					}
					
			        var place = response.Placemark[0];
			        var point = new GLatLng(place.Point.coordinates[1], place.Point.coordinates[0]);
				    
					if ( !marker ) 
					{
						Log('geocode: GClientGeocoder no marker exists for the point of the address: ' + address );

						return;
					};

					marker.Lng = point.lng();
					marker.Lat = point.lat();

					scope.placeMapMarker(marker);
					scope.setCenter();
				  
				});
				break;
		}
	},
	
	/**
	 * Create the HTML shown inside a Info Window
	 * @param MapMarker marker
	 */
	createWindowHTML: function(marker)
	{
		var scope = this;
		
		if (!marker) 
		{
			return null;
		}
		
		var div = new Element('div', 
		{
			'class': 'map_window'
		});
		
		var name = marker.Name;
		
		if (this._options.markerShowIndexInName) 
		{
			// Show a name leading with the index number
			name = marker.Index + '. ' + name;
		}

		div.insert(new Element('h3').update(name));
		div.insert(new Element('p').insert(new Element('img', { src: 'downloads/foto/'+ marker.Postalcode + '_'+ marker.Housenumber + '.jpg', width: 150, height: 113 })));
		
				
		var ul = new Element('ul');
		div.insert(ul);


		if (marker.PhoneNumber) 
		{
			ul.insert(new Element('li').update("Tel. " + marker.PhoneNumber));
		}
		
		if (marker.Street) 
		{
			ul.insert(new Element('li').update(marker.Street + " " + marker.Housenumber + ", " + marker.Postalcode + " " + marker.City));
		}
		
/*		if (marker.Postalcode) 
		{
			ul.insert(new Element('li').update(marker.Postalcode + " " + marker.City));
		}
*/
		if (marker.Description) 
		{
		    ul.insert(new Element('li').update(marker.Description));
		}

		if (marker.Url) 
		{
			ul.insert(new Element('li').insert(new Element('a', 
			{
				href: (marker.Url.indexOf(':/') == -1) ? 'downloads/pdf/' + marker.Url : marker.Url,
				target: '_blank'
			}).update("Klik hier voor meer info")));
		}
		
		var span = new Element('span', 
		{
			'class': 'map_window_controls'
		});
		
		if (this._options.markerShowZoomButton) 
		{
			span.insert(new Element('a', 
			{
				href: "javascript: return;"
			}).update("Zoom").observe('click', function(event)
			{
				scope.zoomToMarker(marker);
			}));
		}
		
		if (this._options.markerShowDirectionsButton) 
		{
			var routeUri = this._options.markerDirectionsUrl.replace("{0}", marker.Address());
			span.insert(new Element('a', 
			{
				href: routeUri
			}).update("Route"));
		}
		
		if (marker.DetailsUrl) 
		{
			span.insert(new Element('a', 
			{
				href: marker.DetailsUrl
			}).update("Meer info"));
		}
		div.insert();

		div.insert(new Element('br'));
		
		div.insert(span);
		
		return div;
	},
	
	/**
	 * Zoom in on the Marker
	 * @param {Object} marker
	 */
	zoomToMarker: function(marker)
	{
		this._gmap.setCenter(new GLatLng(marker.Lat, marker.Lng), this._options.markerZoomInLevel);
	},
	
	
	bindMapEvents: function()
	{
		
	},
	
	/***
	 * Fix Internet Exploder memory leaks
	 */
	preventMemoryLeaks: function()
	{
		Event.observe(window, 'unload', function()
		{
			GUnload();
		});
	}
});

/**
 * Marker displayed on the Map
 */
var MapMarker = Class.create(
{
	Index: -1,
	Id: null,
	Name: null,
	PhoneNumber: null,
	Street: null,
	Housenumber: null,
	Postalcode: null,
	City: null,
	Url: null,
	DetailsUrl: null,
	Lat: null,
	Lng: null,
	Description: null,
	
	/**
	 * Constructor
	 * @param int index
	 * @param string id
	 * @param string name
	 * @param string phoneNumber
	 * @param string street
	 * @param string housenumber
	 * @param string postalcode
	 * @param string city
	 * @param string url
	 * @param string detailsUrl
	 * @param string lat
	 * @param string lng
	 * @param string description (extra door Hans)
	 */
	initialize: function(index, id, name, phoneNumber, street, housenumber, postalcode, city, url, detailsUrl, lat, lng, description)
	{
		this.Index = index;
		this.Id = id;
		this.Name = name;
		this.PhoneNumber = phoneNumber;
		this.Street = street;
		this.Housenumber = housenumber;
		this.Postalcode = postalcode;
		this.City = city;
		this.Url = url;
		this.DetailsUrl = detailsUrl;
		this.Lat = lat;
		this.Lng = lng;
		this.Description = description;
	},
	
	Address: function()
	{
		return this.Street + ' ' + this.Housenumber + ' ' + this.Postalcode + ' ' + this.City + ' Netherlands';
	}
});

/**
 * Custom controls
 */
var ZoeknedZoomControl = Class.create(new GControl(),{
	initialize: function(zoomMap) {
		
		if(zoomMap == undefined) return null;
		
		var ctrlContainer = new Element('div');
		
		var btnZoomin = new Element('img', {'src': '/Templates/V3/Public/images/btn_map_zoomin.png', 'class' : 'map_button'});
		GEvent.addDomListener(btnZoomin, 'click', function() {
			zoomMap.zoomIn();
		});
		
		var btnZoomout = new Element('img', {'src': '/Templates/V3/Public/images/btn_map_zoomout.png', 'class' : 'map_button'});
		GEvent.addDomListener(btnZoomout, 'click', function() {
			zoomMap.zoomOut();
		});
		
		ctrlContainer.insert(btnZoomin);
		ctrlContainer.insert(btnZoomout);
		
		zoomMap.getContainer().appendChild(ctrlContainer);
		
		return ctrlContainer;	
	},
	getDefaultPosition: function() {
		return new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(2, 2));
	}
});

var ZoeknedTypeControl = Class.create(new GControl(), {
	initialize: function(map) {
	
		if(map == undefined) return null;
		
		map.addMapType(G_NORMAL_MAP);
		map.addMapType(G_SATELLITE_MAP);
		map.addMapType(G_HYBRID_MAP);
		
		var container = new Element('div');
		
		var btnMapType = new Element('img', {'src': '/Templates/V3/Public/images/btn_map_maptype.png', 'class' : 'map_button'});
		
		GEvent.addDomListener(btnMapType, 'click', function() {
			var currentType = map.getCurrentMapType();
			var newType;
			
			switch(currentType)
			{
				case G_NORMAL_MAP:
					newType = G_SATELLITE_MAP;
					break;
				case G_SATELLITE_MAP:
					newType = G_HYBRID_MAP;
					break;
				case G_HYBRID_MAP:
					newType = G_NORMAL_MAP;
					break;
			}
			map.setMapType(newType);
		});
	
		container.insert(btnMapType);
		
		map.getContainer().appendChild(container);
		
		return container;	
	},
	getDefaultPosition: function() {
		return new GControlPosition(G_ANCHOR_TOP_RIGHT, new GSize(2, 2));
	}
});

/**
 * 
 */
var HtmlOverlay = Class.create(new GOverlay(), {
	
	_options: {},
	_defaultOptions: {
		map: null,
		point: null,
		box: null,
		position: null,
		width: null,
		height: null,
		ondraw: null,
		className: 'overlay'
	},
	initialize: function(options) {
		
		if(options.map == null) return;
		
		// Setting up original options with default options
		Object.extend(this._options, this._defaultOptions);
		Object.extend(this._options, options);
		
		this.ondraw = this._options.ondraw;
		this._options.box = new Element('div', {'class': this._options.className}).update(this.ondraw());
		this._options.map.getPane(G_MAP_MAP_PANE).appendChild(this._options.box);
	},
	remove: function() {
		this._options.box.parentNode.removeChild(this._options.box);
	},
	copy: function() {
		return new CustomOverlay(this._options);
	},
	redraw: function(force) {
	    if (force) {
	    	var pos = this._options.map.fromLatLngToDivPixel(this._options.position);
	    	var w = this._options.box.getWidth();
	    	var h = this._options.box.getHeight();
	    	
	    	this._options.box.setStyle({
	    			position: 'absolute',
	    			left: (pos.x - (w/2 + 4)) + 'px',
	    			top: (pos.y - (h)) + 'px'
	    	});
	    }
	},
	/**
	 * Overridden to provide custom HTML behavior
	 **/
	ondraw: function() {
	}
});

/** 
 * Clientside debug logging 
 * @param msg string
 */
function Log(msg)
{
	if (eval("typeof console == 'object'")) 
	{
		var id = (this.id) ? this.id : 'Onbekend';
		console.log(id + ' - ' + msg);
	}
};
