/** 
* This is the jquery plugin that handels google maps and its api
*
* @projectDescription 	CakeDC Jquery Map Plugin
* @version	0.1 
*/
var globale = '';
function testmap(mapId){
			globale = mapId;
		}
(function ($) {
/**
* This is just the function that handles the creation of the map object,
* checks compatability and delegates to the Map constructor
* 
* @param {Object} [options] the options for creating and displaying the map
* @return {jQuery}	the calling jQuery object
* @type {Function}
*/
	$.fn.renderMap = function(options) {
		// make sure the runtime can run this code
		try {
			__checkCompatibility(this);
		} catch (e) {
			alert(e.message);
			return false;
		}
		
		/* this is used to prevent mem leaks */
		$(window).bind('unload', GUnload);
		
		return this.each(function() {
			$(this).data('map', new $.Map(this, options));
		});
	};
/* --------------------------- MAP CLASS -------------------------------- */	
/**
* Create new instance of Map
*
* @classDescription	This class is the container for the map instance
* @param {HTMLElement} elt the element in which the map will be attached
* @param {Object} [options] the options for creating and displaying the map
* @return {Map}	returns a new Map.
* @type {Object}
* @constructor	
*/
	$.Map = function(elt, options) {
		var me = this; // this is just used to help scoping (closure)
		this.markerList = {};
		this.listenerList = {};
		this.polylineList = {};
		
		/* get event options and put them in the events member var */
		var opts = $.extend(this.defaults, options);
		var setEvents = {};
		$.each(opts, function(name, value) {
			if($(me.events).index(name) != -1 && typeof value == 'function') {
				setEvents[name] = value;
				delete opts[name];
			}
		});
		
		/* process options that are passed to the GMap constructor */
		var mapOptions = {};
		if(opts.size instanceof Array && opts.size.length == 2) {
			mapOptions.size = new GSize(opts.size[0], opts.size[1]);
		}
		
		/* create the map instance */
		var mapInstance = new GMap2(elt, mapOptions);
		
		this.getMap = function () { return mapInstance; };
		
		/* if the marker manager is used then init it */ 
		var markerManager = null;
		if(options.useMarkerManager && typeof(MarkerManager) == 'function') {
			markerManager = new MarkerManager(mapInstance);
		} else {
			markerManager = mapInstance;
			// this is to make the marker interface for the map be the same as the markerManager class
			markerManager.clearMarkers = function () { mapInstance.clearOverlays(); }
		}
	/**
	 * Call this after the map is resized,
	 * this will allow the rendering of the 
	 * map to adjust
	 * 
	 * @return {Map} Returns the calling map object
	 * @type {Function}
	 * @method
	 */
		this.resizeMap = function () {
			mapInstance.checkResize();
			return this;
		}
	
	/**
	* Gets directions from one location to another and
	* plots them on the map and if specified puts the step
	* by step directions in a div
	*
	* @param {String} from an address or lat, lng point to start from
	* @param {String} to an address or lat, lng point to end
	* @param {Object} [options] an optional options object
	* @return {Map} Returns the calling map
	* @type {Function}
	* @method
	*/
		this.getDirections = function (from, to, options) {
			if(options === undefined) {
				options = {};
			}
			var panel = null;
			if (options.dirPanel) {
				panel = options.dirPanel;
			}
			var GDir = new GDirections(mapInstance, $(panel).get(0));
			GDir.loadFromWaypoints([from, to]);
			return this;
		}
	/**
	 * Gets lat and lng from an address, processes using callback
	 * 
	 * @param {String} address
	 * @param {Function} callback
	 * @return void
	 * @type {Function}
	 * @method
	 */
		this.geocode = function (address, callback) {
			var geo = new GClientGeocoder();
			geo.getLatLng(address, function (point) {
				var coords = null;
				if (point !== null) {
					coords = [point.lat(), point.lng()];
				}
				callback(coords);
			});
		};
		
	/**
	* Adds a marker to the calling map object.
	*
	* @param {String} id a unique id for the new marker
	* @param {jQuery, Marker, Object} marker jQuery object, Marker object, or a generic object (lat, lng, and optional txt)
	* @param {Object} [options] optional options object
	* @return {Map}	Returns the calling Map object.
	* @type {Function}
	* @method
	*/
		this.addMarker = function (id, marker) {
			if (!marker) { return null; }
			
			var mark = null;
			if(marker instanceof $.Map.Marker) {
				mark = marker;				
			} else if(marker instanceof jQuery) {
				if (opts.processDiv) {
					mark = new $.Map.Marker(options.processDiv((marker.innerHtml)));					
				} else {
					mark = new $.Map.Marker.makeFromDOM(marker);
				}
			} else if (typeof marker == 'object' && marker.lat && marker.lng) {
				mark = new $.Map.Marker(marker);
			} else if (marker instanceof String) {
				mark = new $.Map.Marker.makeFromAddress(marker);
			}
			this.markerList[id] = mark;
			var markerOpts = {};
			if (options.useMarkerManager) {
				markerOpts = marker.getOptions();
			}
			markerManager.addOverlay(mark.getInstance(), markerOpts);
			if (markerManager.refresh) {
				markerManager.refresh();
			}
			if (marker.trigger !== undefined && marker.trigger === true) {
				mark.triggerEvent('click');
			}
			return this;
		};
		
	/**
	* Removes a marker from the calling map
	*
	* @param {String} id the unique id of the marker to be removed
	* @return {Map}	returns the calling Map object.
	* @type {Function}
	* @method
	*/
		this.removeMarker = function (id) {
			if(!id) return false;
			markerManager.removeOverlay(this.markerList[id]); 
			delete(this.markerList[id]);
			return this;
		};
	/**
	* Clears the map of markers
	* 
	* @return {Map} the calling map object
	* @type {Function}
	* @method
	*/ 		
		this.clearMarkers = function () {
			markerManager.clearMarkers();
			this.markerList = {};
			return this;
		};
		
	/**
	* Sets the center of the map, if pan is true then the transition will be smooth
	*
	* @param {Float} lat Latitude
	* @param {Float} lng Longitude
	* @param {Boolean} [pan] if the map should pan gracefully or just go to the location
	* @return {Map}	returns the calling Map object.
	* @type {Function}
	* @method
	*/
		this.setCenter = function (lat, lng, pan) {
			var point = new GLatLng(lat, lng);
			if(pan) {
				mapInstance.panTo(point);
			} else {
				mapInstance.setCenter(point);
			}
			return this;
		};
		
	/**
	* Sets the zoom level for the calling map
	*
	* @param {Integer} zoom new zoom level
	* @return {Map}	returns the calling Map object.
	* @type {Function}
	* @method
	*/
		this.setZoom = function(zoom) {
			mapInstance.setZoom(zoom);
			return this;
		};
		
	/**
	* Adds an object that implements the GControl interface to the map
	* (see http://code.google.com/apis/maps/documentation/reference.html#GControl)
	* 
	* TODO: verify that the class is an implementation of GControl before calling eval
	*
	* @param {String} controlName the name of the control class
	* @return {Map}	returns the calling Map object.
	* @type {Function}
	* @method
	*/
		this.addControl = function (controlName) {
			eval("mapInstance.addControl(new " + controlName + "());");
			return this;
		};
		
	/**
	* Binds a map event to the calling map
	* (see the section on events for possible event choices at
	*  http://code.google.com/apis/maps/documentation/reference.html#GMap2)
	* 
	* @param {String} event a valid map event name
	* @param {Function} callback the function to call on event trigger
	* @return {Map}	returns the calling Map object.
	* @exception {Error} throws a general error exception if the event is not valid or the call back is not a function
	* @type {Function}
	* @see $.Map.prototype.events
	* @method
	*/
		this.bindEvent = function (event, callback) {
			if($(me.events).index(event) == -1 || typeof callback != 'function') {
				throw Error("Invalid map event or callback function");
			}
			me.listenerList[event] = GEvent.addListener(mapInstance, event, callback);
			return this;
		};
	/**
	* Triggers an event on the calling map class
	*
	* @param {String} event the name of the event to trigger
	* @type {Function}
	* @method
	*/
		this.triggerEvent = function (event) {
			GEvent.trigger(mapInstance, event);
		};
	/**
	* Unbinds a map event to the calling map
	* 
	* @param {String} event a valid map event name
	* @return {Map}	returns the calling Map object.
	* @type {Function}
	* @see $.Map.prototype.events
	* @method
	*/
		this.unbindEvent = function (event) {
			if(typeof me.listenerList[event] == 'object') {
				GEvent.removeListener(this.listenerList[event]);
			}
			return this;
		};
	/**
	* Zooms to the minimal bounding box to fit all the maps markers
	*
	* @return {Map} returns the calling Map object
	* @type {Function}
	* @method
	*/
		this.zoomToFitMarkers = function () {
			var bounds = new GLatLngBounds();
			for (var i in this.markerList) {
				bounds.extend(this.markerList[i].getInstance().getLatLng());
			}
			var zoomLevel = mapInstance.getBoundsZoomLevel(bounds);
			var centerPoint = bounds.getCenter();
			this.setCenter(centerPoint.lat(), centerPoint.lng());
			this.setZoom(zoomLevel);
			return this;
		};
			
		/* Set the map to inital start location and zoom */
		if(opts.startLat && opts.startLng) { this.setCenter(opts.startLat, opts.startLng); }
		if(opts.zoom) { this.setZoom(opts.zoom); }
		
		/* add the controls to the map */
       	for (var i = 0; i < opts.controls.length; i++) {
       		this.addControl(opts.controls[i]);
       	}
       	
       	/* Add the markers into the map */
       	if (typeof opts.markerList == 'string') {
       		// markerList is an jquery selector for a set of divs
       		$(opts.markerList).each(function() {
       			me.addMarker($(this).attr('id'), $(this));	
       		});
       	} else if (typeof opts.markerList == "object") {
       		// markerList is a list of objects formatted to pass to the addMarker function
       		for (var id in opts.markerList) {
       			this.addMarker(id, opts.markerList[id]);
       		}
       	}
       	
       	/* set event handlers if set in options */
       	$.each(setEvents, me.bindEvent);
	};
	
	
/* --------------------------- POLYLINE CLASS -------------------------------- */
/**
* Create new instance of Polyline
* draws a polyline on the calling map, if geodesic:true is passed as an option the
* polyline will curve with the earth
*
* @classDescription	Represents a polyline on the map
* @param {Map} map the map object that the polyline belongs to
* @param {String} id a unique id or name for the polyline (for later identification)
* @param {Array} points the array of [lat, lng] pairs 
* @param {Object} [options] the optional options object
* @return {Polyline}	returns a new Polyline class instance.
* @type {Object}
* @constructor	
*/
	$.Map.Polyline = function (map, id, points, options) {
		var mapInstance = map.getMap();
		var opts = $.extend(this.defaults, options);
		
		var coords = [];
		for (var i = 0; i < points.length; i++) {
			coords.push(new GLatLng(points[i][0], points[i][1]));
		}
		var polyline = new GPolyline(coords, opts.color, opts.weight, opts.opacity, opts);
		
	/**
	* toggles a polyline to hidden and visible
	* 
	* @return {Polyline} the calling polyline
	* @type {Function}
	* @method
	*/
		this.toggle = function () {
			if (polyline.isHidden()) {
				polyline.show()
			} else {
				polyline.hide();
			}
			return this;
		};
		
	/**
	* removes the line from the map
	* 
	* @type {Function}
	* @method
	*/
		this.removeLine = function () {
			mapInstance.removeOverlay(polyline);
		};
		
	/**
	* adds a vertex to the polyline
	* 
	* @param {Array} coord the [lat, lng] pair to add to the polyline
	* @param {Integer} index the index to add it to the polyline
	* @return {Polyline} the calling polyline object
	* @type {Function}
	* @method
	*/
		this.addVertex = function (coord, index) {
			polyline.insertVertex(index, coord);
			return this;
		};
	/**
	* remove a vertex from the polyline by index
	* 
	* @param {Integer} index the index of the vertex to remove
	* @return {Polyline} the calling polyline object
	* @type {Function}
	* @method
	*/
		this.removeVertex = function (index) {
			polyline.removeVertex(index);
			return this;
		};
		
	/**
	* gets the length of the polyline in meters
	* 
	* @return {Float} the length
	* @type {Function}
	* @method
	*/
		this.getLength = function () {
			return polyline.getLength();
		};
		
	/**
	* focus the map on the polyline so that the bounding box is almost as big as the 
	* map bounding box
	*
	* @return {Polyline} the calling polyline object
	* @type {Function} 
	* @method
	*/	
		this.focus = function () {
			var lineBounds = polyline.getBounds();
			var lineCenter = lineBounds.getCenter();
			var lineZoom = mapInstance.getBoundsZoomLevel(lineBounds);
		
			map.setCenter(lineCenter.lat(), lineCenter.lng());
			map.setZoom(lineZoom);
			
			return this;
		};

		map.polylineList[id] = this;
		mapInstance.addOverlay(polyline);
		return this;
	};
	
/* --------------------------- MARKER CLASS -------------------------------- */
/**
* Create new instance of Marker
*
* @classDescription	Represents a marker on the map
* @param {Object} [options] the options for creating and displaying the marker
* @return {Map}	returns a new Marker.
* @type {Object}
* @constructor	
*/
	$.Map.Marker = function(options){
		var opts = $.extend(this.defaults, options);
		var markerOptions = {};
		if (opts.title) {
			markerOptions.title = options.title;
		}
		
		if (opts.icon) {
			// Create the custom icon instance
			var customIcon = new GIcon(G_DEFAULT_ICON);
			if (typeof opts.icon == 'string') {
				customIcon.image = '/img/YouFootMap/mark-venue2.png';
				customIcon.iconSize = new GSize(32, 32);
				customIcon.shadowSize = new GSize(51, 32);
				customIcon.iconAnchor = new GPoint(16, 32);
				customIcon.infoWindowAnchor = new GPoint(16, 2);
			}
			else 
				if (typeof opts.icon == 'object') {
					customIcon.image = '/img/YouFootMap/mark-venue2.png';
					customIcon.iconSize = new GSize(opts.icon.width, opts.icon.height);
					customIcon.shadowSize = new GSize(Math.floor(opts.icon.width * 1.6), opts.icon.height);
					customIcon.iconAnchor = new GPoint(opts.icon.width / 2, opts.icon.height)
					customIcon.infoWindowAnchor = new GPoint(opts.icon.width / 2, Math.floor(opts.icon.height / 12));
				}
			
			markerOptions.icon = customIcon;
		}
		
		
		if($.url.segment(1) == 'add' || $.url.segment(1) == 'add_leg' || $.url.segment(1) == 'edit'){
			markerOptions.draggable = true;
		}
		
		var instance = new GMarker(new GLatLng(opts.lat, opts.lng), markerOptions);
		/**
	 * Get Longitude of the calling marker
	 *
	 * @return {Float} the longitude of the marker
	 * @type {Function}
	 * @method
	 */
		this.getLng = function(){
			return instance.getLatLng().lng();
		};
		
		/**
	 * Get Latitude of the calling marker
	 *
	 * @return {Float} the latitude of the marker
	 * @type {Function}
	 * @method
	 */
		this.getLat = function(){
			return instance.getLatLng().lat();
		};
		
		/**
	 * Get the title of the calling marker
	 *
	 * @return {String} the title
	 * @type {Function}
	 * @method
	 */
		this.getTitle = function(){
			return instance.getTitle();
		};
		/**
	 * Gets read only copy of the value of the key passed in the options object which is passed to the constructor
	 *
	 * @todo this function should return a copy of the value to prevent modification
	 * @param {String} [key] the key to index into the options array
	 * @return {Mixed} the value assoced with the key
	 * @type {Function}
	 * @method
	 */
		this.getOptions = function(key){
			if (key !== undefined) {
				return opts[key];
			}
			return opts;
		}
		
		/**
	 * Return the instance of the GMarker class
	 *
	 * @return {GMarker} the instance of the GMarker class
	 * @type {Function}
	 * @method
	 */
		this.getInstance = function(){
			return instance;
		}
		
		/**
	 * Triggers an event on the calling marker class
	 *
	 * @param {String} event the name of the event to trigger
	 * @type {Function}
	 * @method
	 */
		this.triggerEvent = function(event){
			GEvent.trigger(instance, event);
		}
		/* create the popup window for the marker */
		if (opts.txt) {
			var htmlTxt = opts.txt;
			GEvent.addListener(instance, opts.htmlOpenEvent, function(){
				instance.openInfoWindowHtml(htmlTxt);
			});
			if (opts.lnk) {
				opts.lnk.click(function(){
					instance.openInfoWindowHtml(htmlTxt);
					GEvent.trigger(instance, 'click');
				});
			}
			
		}
		GEvent.addListener(instance, 'dragend', function(){
			var amine = instance.getLatLng();
			
			//alert(amine.lat()+'--'+amine.lng());
			var geocoder = new GClientGeocoder();
			geocoder.getLocations(amine, function(response){
			
				if (!response || response.Status.code != 200) {
					alert("Status Code:" + response.Status.code);
				}
				else {
					place = response.Placemark[0];
					point = new GLatLng(place.Point.coordinates[1], place.Point.coordinates[0]);
					
					/*instance.openInfoWindowHtml('<b>orig latlng:</b>' + response.name + '<br/>' +
				 '<b>latlng:</b>' +
				 place.Point.coordinates[1] +
				 "," +
				 place.Point.coordinates[0] +
				 '<br>' +
				 '<b>Status Code:</b>' +
				 response.Status.code +
				 '<br>' +
				 '<b>Status Request:</b>' +
				 response.Status.request +
				 '<br>' +
				 '<b>Address:</b>' +
				 place.address +
				 '<br>' +
				 '<b>Accuracy:</b>' +
				 place.AddressDetails.Accuracy +
				 '<br>' +
				 '<b>Pays:</b>' +
				 place.AddressDetails.Country.CountryName +
				 '<br>' +
				 '<b>Region:</b>' +
				 place.AddressDetails.Country.AdministrativeArea.AdministrativeAreaName +
				 '<br>' +
				 '<b>2:</b>' +
				 place.AddressDetails.Country.AdministrativeArea.SubAdministrativeArea.Locality.PostalCode.PostalCodeNumber +
				 '<br>' +
				 '<b>3:</b>' +
				 place.AddressDetails.Country.AdministrativeArea.SubAdministrativeArea.Locality.LocalityName)*/
				
					var admin = '.AdministrativeArea';
					var subAdmin = '.SubAdministrativeArea';
					var loc = '.Locality';
					var depLoc = '.DependentLocality';
					var locName = '.LocalityName';
					var depLocName = '.DependentLocalityName';
					var zipcode = '.PostalCode';
					var zipcodeNumber = '.PostalCodeNumber';
				
					if ((eval("place.AddressDetails.Country"+admin)) == undefined) {
						var admin = '';
					}
					
					if ((eval("place.AddressDetails.Country"+admin+subAdmin)) == undefined) {
						var subAdmin = '';
					}
					
					if ((eval("place.AddressDetails.Country"+admin+subAdmin+loc)) == undefined) {
						var loc = '';
					}
					
					if ((eval("place.AddressDetails.Country"+admin+subAdmin+loc+depLoc)) == undefined) {
						var depLoc = '';
					}
					if ((eval("place.AddressDetails.Country"+admin+subAdmin+loc+depLoc+depLocName)) == undefined) {
						var depLocName = '';
					}
					if ((eval("place.AddressDetails.Country"+admin+subAdmin+loc+locName)) == undefined) {
						var LocName = '';
					}
					if ((eval("place.AddressDetails.Country"+admin+subAdmin+loc+depLoc+zipcode)) == undefined) {
						var zipcode = '';
					}
					if ((eval("place.AddressDetails.Country"+admin+subAdmin+loc+depLoc+zipcode+zipcodeNumber)) == undefined) {
						var zipcodeNumber = '';
					}
					/*var mouk = "place.AddressDetails.Country"+admin+subAdmin+loc+locName;
					alert(eval(mouk));
					alert(JSON.stringify(place));*/
					
					//Map on team add page (HeadQuarter)
					var postalcode;
					if(eval("place.AddressDetails.Country"+admin+subAdmin+loc+depLoc+zipcode+zipcodeNumber)=='[object Object]'){
						 postalcode = '';
					}else{
						 postalcode = eval("place.AddressDetails.Country"+admin+subAdmin+loc+depLoc+zipcode+zipcodeNumber); 
					}
					$("#VenueCountry_0"+globale).val(place.AddressDetails.Country.CountryName);
					if(globale =='0'){
						$("#HQPostalCode").empty();
						$("#HQCity").empty();
						$("#HQAddress").val('');
						
					$("#HQLat").val(amine.lat());
					$("#HQLong").val(amine.lng());	
					$("#HQPostalCode").val(postalcode);
					$("#HQCity").val(eval("place.AddressDetails.Country"+admin+subAdmin+loc+locName));
					$("#TeamCountry").val(place.AddressDetails.Country.CountryName);
					$("#HQAddress").val(eval("place.AddressDetails.Country"+admin+subAdmin+loc+depLoc+".Thoroughfare.ThoroughfareName"));
										
					}else{
						//Map on venue add page
						$("#VenuePostalCode"+globale).empty();
						$("#VenueCity"+globale).empty();
						$("#VenueAddress"+globale).val('');
						$("#VenueLat"+globale).empty();
						$("#VenueLong"+globale).empty();
						if(globale == ''){							
							$("#VenueLat").val(amine.lat());
							$("#VenueLong").val(amine.lng());
						}else{
							$("#VenueLat"+globale).val(amine.lat());
							$("#VenueLong"+globale).val(amine.lng());
						}
						//alert(amine.lng());
						$("#VenuePostalCode"+globale).val(postalcode);
						$("#VenueCity"+globale).val(eval("place.AddressDetails.Country"+admin+subAdmin+loc+locName));
						$("#VenueCountry"+globale).val(place.AddressDetails.Country.CountryName);
						$("#VenueAddress"+globale).val(eval("place.AddressDetails.Country"+admin+subAdmin+loc+depLoc+".Thoroughfare.ThoroughfareName"));
					}
					globale = '';
				}
			});
			return this;
		});
	}
	
/**
* Secondary constructor that creates a new instance of marker from the DOM elem
*
* @param {HTMLElement} elem the dom element that contains the info for creating the marker
* @return {Map}	returns a new Marker.
* @type {Function}
* @constructor	
*/
	$.Map.Marker.makeFromDOM = function (elem) {
		if ($('span.title', elem)) {
			var title = $('span.title', elem).html();	
		} else {
			var title = $("h4", elem).html();
		}
		var txtElem = '<h4>'+$("h4", elem).html()+'</h4>';
		if ($("span.description", elem).html()) {
			txtElem += '<span class="description">' + $("span.description", elem).html() + '</span>';
		}
		var latElem = $("abbr.latitude", elem)[0];
		var lngElem = $("abbr.longitude", elem)[0];

		if (latElem && lngElem) {
			var options = {
				lng: parseFloat($(lngElem).attr("title")),
				lat: parseFloat($(latElem).attr("title")),
				title: title,
				txt: txtElem,
				lnk: $("a.marker", elem)
			};
			
			var maxZoom = $("abbr.maxZoom", elem);
			if (maxZoom) {
				options.maxZoom = maxZoom[0];
			}
			var minZoom = $("abbr.minZoom", elem);
			if (minZoom) {
				options.minZoom = minZoom[0];
			}
			return new $.Map.Marker(options);
		} 
		
		return null;
	};

	/* private helper funtions */
	var __checkCompatibility = function(el) {
		if (!window.GBrowserIsCompatible || !GBrowserIsCompatible()) {
			throw Error('Browser is not compatible');
		}
	};
/* --------------------------- DEFAULTS -------------------------------- */
	$.Map.prototype.defaults = {
		lng: null,
		lat: null,
		txt: null,
		htmlOpenEvent: "click",
		draggable: false,
		minZoom: 0,
		maxZoom: null,
		icon: "/img/map_icons/default.png",
		lnk: null
	};
	
	$.Map.prototype.defaults = {
		startLng: 15,
		startLat: 50,
		zoom: 4,
		markerList: null,
		controls: ["GLargeMapControl", "GMapTypeControl"],
		size: null,
		useMarkerManager: false
	};
	
	$.Map.prototype.events = [
		'addmaptype',
		'removemaptype',		
		'click',
		'dblclick',
		'singlerightclick',
		'movestart',
		'move',
		'moveend',
		'zoomend',
		'maptypechanged',
		'infowindowopen',
		'infowindowbeforeclose',
		'infowindowclose',
		'addoverlay',
		'removeoverlay',
		'clearoverlays',
		'mouseover',
		'mouseout',
		'mousemove',
		'dragstart',
		'drag',
		'dragend',
		'load'
	];
	
	$.Map.Polyline.prototype.defaults = {
		'color': '#000000',
		'geodesic': false,
		'clickable': true,
		'weight': 1,
		'opacity': 0.5
	};
})(jQuery);
