/*
 
	Wrapper Class to display google Map

	Usage:
	Realy simple usage
	<script type="text/javascript" charset="utf-8">
		var m = new CfaMap(idContainer, [map_type], [lattitude], [longitude], [zoom])
	</script>
	
	With some point
	<script type="text/javascript" charset="utf-8">
		var m = new CfaMap(idContainer)
		m.onLoad = function() {
			this.addPoint({lat:45.595590652362915,lng:-76.5087890625})
		}
	</script>
	
	
	List of possible function:
		debug() : Display cord in the firebug console when the map is clicked
		setControls(string) : Choice between (null|small|zoom)
		resetControls() : Remove all the control
		setZoom(integer) : Set the zoom
		zoomIn() : Increase the zoom
		zoomOut() : Decrease the zoom
		addInfo(object) : Add an info window
		addPoint(object) : Add a point on the map
		addPoints(array) : Add multiple point(object)
		addLine(point, point) : Add a line between two points
		clear() : Clear the whole map
		setCenter(number, number, [number]) : Set the center of the map by lattitude, longitude and optionaly the zoom
		setType(string) : Set the type of the map (null|terrain|mixed)
		loadJSON(string) : Load an array of point(object) with the url
		
	Property:
		zoomFitAfterJSON : Boolean : Set the zoom of the map to fit the marker
*/

// JSON PARSER
if(!this.JSON){JSON=function(){function f(n){return n<10?'0'+n:n;}Date.prototype.toJSON=function(key){return this.getUTCFullYear()+'-'+f(this.getUTCMonth()+1)+'-'+f(this.getUTCDate())+'T'+f(this.getUTCHours())+':'+f(this.getUTCMinutes())+':'+f(this.getUTCSeconds())+'Z';};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(key){return this.valueOf();};var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapeable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'},rep;function quote(string){escapeable.lastIndex=0;return escapeable.test(string)?'"'+string.replace(escapeable,function(a){var c=meta[a];if(typeof c==='string'){return c;}return'\\u'+('0000'+(+(a.charCodeAt(0))).toString(16)).slice(-4);})+'"':'"'+string+'"';}function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==='object'&&typeof value.toJSON==='function'){value=value.toJSON(key);}if(typeof rep==='function'){value=rep.call(holder,key,value);}switch(typeof value){case'string':return quote(value);case'number':return isFinite(value)?String(value):'null';case'boolean':case'null':return String(value);case'object':if(!value){return'null';}gap+=indent;partial=[];if(typeof value.length==='number'&&!(value.propertyIsEnumerable('length'))){length=value.length;for(i=0;i<length;i+=1){partial[i]=str(i,value)||'null';}v=partial.length===0?'[]':gap?'[\n'+gap+partial.join(',\n'+gap)+'\n'+mind+']':'['+partial.join(',')+']';gap=mind;return v;}if(rep&&typeof rep==='object'){length=rep.length;for(i=0;i<length;i+=1){k=rep[i];if(typeof k==='string'){v=str(k,value);if(v){partial.push(quote(k)+(gap?': ':':')+v);}}}}else{for(k in value){if(Object.hasOwnProperty.call(value,k)){v=str(k,value);if(v){partial.push(quote(k)+(gap?': ':':')+v);}}}}v=partial.length===0?'{}':gap?'{\n'+gap+partial.join(',\n'+gap)+'\n'+mind+'}':'{'+partial.join(',')+'}';gap=mind;return v;}}return{stringify:function(value,replacer,space){var i;gap='';indent='';if(typeof space==='number'){for(i=0;i<space;i+=1){indent+=' ';}}else if(typeof space==='string'){indent=space;}rep=replacer;if(replacer&&typeof replacer!=='function'&&(typeof replacer!=='object'||typeof replacer.length!=='number')){throw new Error('JSON.stringify');}return str('',{'':value});},parse:function(text,reviver){var j;function walk(holder,key){var k,v,value=holder[key];if(value&&typeof value==='object'){for(k in value){if(Object.hasOwnProperty.call(value,k)){v=walk(value,k);if(v!==undefined){value[k]=v;}else{delete value[k];}}}}return reviver.call(holder,key,value);}cx.lastIndex=0;if(cx.test(text)){text=text.replace(cx,function(a){return'\\u'+('0000'+(+(a.charCodeAt(0))).toString(16)).slice(-4);});}if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,'@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,']').replace(/(?:^|:|,)(?:\s*\[)+/g,''))){j=eval('('+text+')');return typeof reviver==='function'?walk({'':j},''):j;}throw new SyntaxError('JSON.parse');}};}();}


function findCfaMapById(id) {
	var ret = "Error: CfaMap not found";
	$H(window).each(function(name, index) {		
		if(name.toString() == "m,[Object:CfaMap]") return name;
	});
	return ret;
}

var CfaMap = Class.create({
	
	// Initializer
	initialize: function(id, css_class, lat, lng, zoom) {
		//this.gmap_key = "ABQIAAAAAGT04m1sNQ2G0_9vg4B1lBQUH5oEfmZv1sFtFVBtPnTOGJ8lMxRXDm3WVOjxaJZpapPacEqMwSBhTg";
		this.gmap = {};
		this.id = id;
		this.css_class = (css_class != undefined || css_class != null) ? css_class : "medium" ;
		this.lat = (lat != undefined || lat != null) ? lat : 46.814394 ;
		this.lng = (lng != undefined || lng != null) ? lng : -71.218872 ;
		this.zoom = (zoom != undefined || zoom != null) ? zoom : 13 ;
		this.controls = [];
		this.mgr = {};
		this.assets_path = "/images/map/";
		this.cluster = {};
		this.clusterGroup = [];
		this.zoomFitAfterJSON = true;
		
		Event.observe(window, 'load', this.onWindowLoad.bindAsEventListener(this));
		Event.observe(window, 'unload', this.onWindowUnload.bindAsEventListener(this));	
	},	
	toString:function() { return "[Object:CfaMap]"; },
	getComputedStyle: function(elId, _style) {
		var computedStyle;
		if (typeof $(elId).currentStyle != 'undefined') {
			computedStyle = $(elId).currentStyle;
		} else { 
			computedStyle = document.defaultView.getComputedStyle($(elId), null); 
		}
		return computedStyle[_style];
	},
	
	// Display cord in the firebug console when you click on the map
	debug:function(mode) {
		if(mode == undefined) {
			GEvent.addListener(this.gmap, "click", function(overlay, gpoint) {
				if(gpoint != undefined) console.info(gpoint.lat()+", "+gpoint.lng());
			});
		} else {
			var content = '';
			content += '<h2>Soirée francophone</h2>';
			content += '<h3>Samedi le 22 août 2008</h3>';
			content += 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Phasellus vestibulum faucibus dui. Proin purus. Cras quis odio. Curabitur mattis rhoncus diam. Aenean libero.';
			content += '<a class="arrow" href="#">Plus d’informations</a>';
			switch(mode) {
				case "small":
					this.clear();
					this.setCenter(46.814394, -71.218872, 13);
					this.addInfo({lat:46.814394, lng:-71.218872, content:content});
					this.addPoint({lat:46.81116784634232,lng:-71.24328502819824,color:"orange"});
					this.addPoint({lat:46.81149884634232,lng:-71.24628602819824,color:"orange"});
					this.addPoint({lat:46.81045784634232,lng:-71.24828702819824,color:"orange"});
					this.addPoint({lat:46.81216784634232,lng:-71.24328502819824,color:"verte2"});
					var p1 = this.addPoint({lat:46.806445746132816,lng:-71.22530937194824, type:"avatar", src:"/images/map/luc.png", cluster:false});
					var p2 = this.addPoint({lat:46.8019807044634,lng:-71.2452220916748, cluster:false});
					this.addLine(p1, p2);
					this.addPoint({lat:46.80679823363894,lng:-71.20634078979492, content:content, color:"bleu"});
					this.addPoint({lat:46.82394983414019,lng:-71.25663757324219, content:"test", type:"avatar", size:"small", src:"/images/map/luc_small.png"});
				break;
				case "medium":
					this.clear();
					this.setCenter(47.12397234868853, -35.5078125, 2);
					this.loadJSON("/dataset_large.html");
				break;
				case "large":
					this.clear();
					this.setCenter(47.12397234868853, -35.5078125, 2);
					this.loadJSON("/dataset_large.html");
					this.loadJSON("/dataset_large_2.html");
					this.loadJSON("/dataset_large_3.html");
					this.loadJSON("/dataset_large_4.html");
				break;
			}
		}
	},
	
	// Event
	onLoad:function() {},
	onWindowLoad: function() {
		if(!$(this.id).hasClassName("map")) $(this.id).addClassName("map")
		if (GBrowserIsCompatible() && (this.getComputedStyle(this.id, "fontSize") == "11px")) {
			
			var css = '';
			css += '<div class="map_cadre_'+this.css_class+'">';
			css += '<img id="'+this.id+'_map_zoomin" class="map_zoomin" style="display:none;" src="/images/btn_emplacement_zoomin.gif" width="20" height="19" />';
			css += '<img id="'+this.id+'_map_zoomout" class="map_zoomout" style="display:none;" src="/images/btn_emplacement_zoomout.gif" width="20" height="19" />';			
			css += '<div class="map_'+this.css_class+'">';
			css += '<div id="'+this.id+'_map" class="map_'+this.css_class+'_asset"></div>';
			css += '</div></div>';
			$(this.id).insert(css);
			
			$(this.id+'_map_zoomin').observe('click', this.zoomIn.bindAsEventListener(this));
			$(this.id+'_map_zoomout').observe('click', this.zoomOut.bindAsEventListener(this));
			this.gmap = new GMap2($(this.id+'_map'));
			this.gmap.setCenter(new GLatLng(this.lat, this.lng), this.zoom);
			this.mgr = new MarkerManager(this.gmap);
			this.cluster = new ClusterMarker(this.gmap, {clusterMarkerIcon:"dynamic"});
			this.onLoad();
		}
	},
	onWindowUnload: function() {
		GUnload();
	},
	
	// Public Method
	// Icons
	generateIcon: function(opts) {
		opts = Object.extend({
			type:"icon",
			color:"bleu",
			size:"medium",
			src:this.assets_path+"marker_avatar_medium.png"
		},opts);
		var mark = new GIcon();
		switch(opts.type) {
			case "avatar":
				mark.image = opts.src;
				mark.infoWindowAnchor = new GPoint(10, 2);
				if(opts.size == "medium") {
					mark.shadow = this.assets_path+"marker_avatar_shadow.png";
					mark.shadowSize = new GSize(147, 78);
					mark.iconAnchor = new GPoint(18, 71);
					mark.iconSize = new GSize(66, 76);
					mark.infoWindowAnchor = new GPoint(50, 2);
				} else {
					if(mark.image == this.assets_path+"marker_avatar_medium.png") mark.image = this.assets_path+"marker_avatar_small.png";
					mark.iconAnchor = new GPoint(16, 16);
					mark.infoWindowAnchor = new GPoint(25, 0);
					mark.iconSize = new GSize(32, 32);
				}
				
			break;
			case "icon":
				mark.image = this.assets_path+"marker_"+opts.type+"_"+opts.size+"_"+opts.color+".png";
				mark.shadow = this.assets_path+"marker_icon_shadow.png";
				mark.shadowSize = new GSize(41, 33);
				if(opts.size == "medium") {
					mark.iconSize = new GSize(26, 34);
					mark.iconAnchor = new GPoint(8, 32);
					mark.infoWindowAnchor = new GPoint(15, 5);
				} else {
					mark.iconSize = new GSize(30, 20);
					mark.iconAnchor = new GPoint(11, 20);
				}
			break;
		}
		return mark;
	},
	
	// Controls
	setControls: function(type) {
		this.resetControls();
		switch(type) {
			case "zoom" :
				$(this.id+'_map_zoomin').show();
				$(this.id+'_map_zoomout').show();
			break;
			case "small" :
				this.controls.push(new GSmallZoomControl());
				this.gmap.addControl(this.controls[this.controls.length-1]);
				
				this.controls.push(new GMapTypeControl());
				this.gmap.addControl(this.controls[this.controls.length-1]);
			break;
			default :
				this.controls.push(new GLargeMapControl());
				this.gmap.addControl(this.controls[this.controls.length-1]);
				
				this.controls.push(new GMapTypeControl());
				this.gmap.addControl(this.controls[this.controls.length-1]);
			break;
		}		
				
	},	
	resetControls: function() {
		$(this.id+'_map_zoomin').hide();
		$(this.id+'_map_zoomout').hide();
		this.controls.each(function(name, index) {		
			this.gmap.removeControl(this.controls[index]);
		}, this);
		this.controls = [];
	},
	setZoom: function(lvl) {
		this.gmap.setZoom(lvl);
	},
	zoomIn: function() {
		this.gmap.zoomIn();
	},
	zoomOut: function() {
		this.gmap.zoomOut();
	},
	zoomFit: function () {
		this.cluster.fitMapToMarkers();
	},
	getZoom: function() {
		return this.gmap.getZoom();
	},
	
	
	// InfoBox and Point
	/**
	 * addInfo
	 * 
	 * lat:Number			Lattitude
	 * long:Number			Longitude
	 * content:String		Contenue de l'infobulle
	 * Les autres parametres sont donner comme parametre d'option a l'api google
	 * 			ex.: maxWidth est par defaut a 320
	 * 			Quand utiliser à partir d'un point: http://code.google.com/apis/maps/documentation/reference.html#GMarker
	 * 			Quand utiliser directement sur la carte: http://code.google.com/apis/maps/documentation/reference.html#GInfoWindowOptions
	 */
	addInfo: function(pointInfo) {
		pointInfo = Object.extend({
			maxWidth:320
		},pointInfo);
		if(pointInfo.content == undefined) {
			return lat.openInfoWindowHtml("<div class='map_infoWindow'></div>", pointInfo);
		} else {
			return this.gmap.openInfoWindowHtml(new GLatLng(pointInfo.lat, pointInfo.lng), "<div class='map_infoWindow'>"+pointInfo.content+"</div>", pointInfo);	
		}				
	},
	
	/**
	 * addPoint
	 * 
	 * lat:Number			Lattitude
	 * long:Number			Longitude
	 * [type]:String		Type de point [icon|avatar] 	:default=>icon
	 * [color]:String		Couleur de du point [blue|green|orange] (Applicable seulement pour le type icon)	:default=>blue
	 * [size]:String		Grosseur du point [medium|small]	:default=>medium
	 * [src]:String			Source de l'image de l'icone (applicable seulement pour le type avatar)
	 * [cluster]:Boolean	Si l'icone est gerer dans un groupe de cluster	:default=>true
	 * [content]:String		Contenue de l'infobulle
	 * [group]:String		Nom du groupe de cluster
	 * [zoom]:String		Sur qu'elle zomm le point doit s'afficher
	 * Les autres parametres sont donner comme parametre d'option a l'api google
	 * 			ex.: maxWidth est par defaut a 320
	 * 			Quand utiliser à partir d'un point: http://code.google.com/apis/maps/documentation/reference.html#GMarker
	 * 			Quand utiliser directement sur la carte: http://code.google.com/apis/maps/documentation/reference.html#GInfoWindowOptions
	 */
	addPoint: function(pointInfo) {
		var marker = new GMarker(new GLatLng(pointInfo.lat, pointInfo.lng), {icon:this.generateIcon(pointInfo)});
		if(pointInfo.content != undefined) {
			marker.rel = this;
			GEvent.addListener(marker, "click", function() {
				this.rel.addInfo(pointInfo);
			});
		}		
		if(pointInfo.zoom == undefined && pointInfo.cluster !== false) {
			if(pointInfo.group != undefined) {
				if(this.clusterGroup[pointInfo.group] == undefined) this.clusterGroup[pointInfo.group] = new ClusterMarker(this.gmap, {clusterMarkerIcon:"dynamic"});
				this.clusterGroup[pointInfo.group].addMarkers([marker]);
				this.clusterGroup[pointInfo.group].refresh();
			} else {
				this.cluster.addMarkers([marker]);
				this.cluster.refresh();
			}
		} else {
			if(pointInfo.zoom == undefined) pointInfo.zoom = 1;
			this.mgr.addMarker(marker, pointInfo.zoom);
		}
		return marker;
	},
	
	/**
	 * addPoint
	 *
	 *	Prend un array d'objet pour CfaMap.addPoint
	 *
	 */
	addPoints: function(arr, grp) {
		markers = [];
		for(var i=0; i<arr.length; i++) {
			marker = new GMarker(new GLatLng(arr[i].lat, arr[i].lng), {icon:this.generateIcon(arr[i])});
			if(arr[i].content != undefined) {
				marker.rel = this;
				marker.infoObj = arr[i]
				GEvent.addListener(marker, "click", function() {
					this.rel.addInfo(this.infoObj);
				});
			}
			markers.push(marker);
		}
		if(grp != undefined) {
			if(this.clusterGroup[grp] == undefined) this.clusterGroup[grp] = new ClusterMarker(this.gmap, {clusterMarkerIcon:"dynamic"});
			this.clusterGroup[grp].addMarkers(markers);
			this.clusterGroup[grp].refresh();
		} else {
			this.cluster.addMarkers(markers);
			this.cluster.refresh();
		}
	},
	
	/**
	 * addLine
	 *
	 *	Trace une ligne entre deux point de la carte
	 *
	 */
	addLine: function(p1, p2, opts) {
		opts = Object.extend({
			color:"#FFFFFF",
			opacity:0.6,
			stroke:6
		},opts);
		var polyline = new GPolyline([p1.getLatLng(), p2.getLatLng()], opts.color, opts.stroke, opts.opacity);
		this.gmap.addOverlay(polyline);
	},
	
	/**
	 * clear
	 *
	 *	Clear tout les overlay de la carte
	 *
	 */
	clear: function() {
		this.mgr.clearMarkers();
		this.cluster.removeMarkers();
		this.gmap.clearOverlays();
		
		this.clusterGroup.each(function(name, index) {		
			this.removeMarkers();
		}, this);
		
	},
	
	setCenter: function(lat, lng, zoom) {
		if(zoom == undefined) {
			this.gmap.panTo(new GLatLng(lat, lng));
		} else {
			this.gmap.setCenter(new GLatLng(lat, lng), zoom);
		}
	},
	setType: function(type) {
		switch(type) {
			case "terrain" :
				this.gmap.setMapType(this.gmap.getMapTypes()[1]);
			break;
			case "mixed" :
				this.gmap.setMapType(this.gmap.getMapTypes()[2]);
			break;
			default:
				this.gmap.setMapType(this.gmap.getMapTypes()[0]);
			break;
		}
	},
	
	// Ajax part
	loadJSON: function(url) {
		new Ajax.Request(url, {
			method: 'get',
			onSuccess: this.onJSON.bindAsEventListener(this)
		});		
	},
	onJSON: function(infoObj) {
		var object = JSON.parse(infoObj.responseText);
		this.addPoints(object);
		if(this.zoomFitAfterJSON) this.zoomFit();
	}
	
});

	
