/*
 * Dépendance avec jQuery v1.2.6 et/ou selon interoptabilité
 * variables définies par le serveur et présent au "niveau supérieur" càd dans la page HTML
 * var markerIcon // Icone
 * var jobDataMap // objet JSON
 */

// Charge le composant "maps" V2 de google
//google.load("maps", "2");

// Variables Globales
var PIdefaultZoomLevel = 5;
var map;
var isMapInitialized = false;					// Définie si la carte a déjà été initialisé
var jobDataArray = (window.jobDataMap) ? jobDataMap : ''; 	// Objet contenant toutes les offres localisable
var markerIcon = (window.markerIcon) ? markerIcon : '';

var jobMarkerManager;
var multiJobMarkerMapBounds; 			//bound of map
var currentInfoWindowPageIndex = 0; 	//infowindow paging
var currentInfoWindowJobMarker; 		//infowindow paging
var MultiJobMapBoundsExtendRatio = .2; 	//ratio to extend bounds on map to ensure all markers fit
var MultiJobMapInitialZoomLevel = 11;	//ratio to extend bounds on map to ensure all markers fit



// Création d'un prototype d'icone
var offreIcon = new GIcon();
offreIcon.shadow = markerIcon.replace('{extension}', 'shadow');
offreIcon.iconSize = new GSize(30, 30);
offreIcon.shadowSize = new GSize(37, 34);
offreIcon.iconAnchor = new GPoint(9, 34);
offreIcon.infoWindowAnchor = new GPoint(9, 2);
offreIcon.infoShadowAnchor = new GPoint(18, 25);


var mapIconSingle = new GIcon();
mapIconSingle.image = markerIcon.replace('{extension}', 'single');
mapIconSingle.shadow = markerIcon.replace('{extension}', 'shadow_s');
mapIconSingle.iconSize = new GSize(24, 24);
mapIconSingle.shadowSize = new GSize(24, 24);
mapIconSingle.iconAnchor = new GPoint(9, 34);
mapIconSingle.infoWindowAnchor = new GPoint(9, 2);
mapIconSingle.infoShadowAnchor = new GPoint(18, 25);

// Attends la fin du chargement de la page pour ajouter des méthodes aux fonctions de google
// Sinon les méthodes sont ajouté à des classes non existante
$(document).ready(function(){

	// Calcul automatiquement le centre de la carte et définie le zoom optimal pour voir tous les marqueurs
	GMap2.prototype.centerAndZoomOnBounds = function(bounds, maxZoomLevel, extendRatio) { 
		var zoomLevel = maxZoomLevel || 0;
		var ratio = extendRatio || 0;
		var largerBounds = bounds.extendByRatio(ratio); 
		var center_lat = (largerBounds.getNorthEast().lat() + largerBounds.getSouthWest().lat())/ 2.0; 
		var center_lng = (largerBounds.getNorthEast().lng() + largerBounds.getSouthWest().lng())/ 2.0; 
		 
		var boundsZoom = map.getBoundsZoomLevel(largerBounds);
		 
		//do not zoom in more then specificed zoom level
		var zoom =  boundsZoom <= zoomLevel ? boundsZoom : zoomLevel;
		map.setCenter(new GLatLng(center_lat, center_lng), zoom);
	} 

	//pads existing GLatLngBounds by ratio to ensure all markers are visible
	GLatLngBounds.prototype.extendByRatio = function(ratio) {
		 // initialize bounds to be the same as original 
		 var largerBounds = new GLatLngBounds( this.getSouthWest(), this.getNorthEast() ); 
		 // get lat, lng of north east and south west 
		 var northEastLat = this.getNorthEast().lat(); 
		 var northEastLng = this.getNorthEast().lng(); 
		 var southWestLat = this.getSouthWest().lat(); 
		 var southWestLng = this.getSouthWest().lng(); 
		 var diffLat = northEastLat - southWestLat; 
		 var diffLng = northEastLng - southWestLng; 
		 // multiply with ratio 
		 northEastLat += diffLat * ratio; 
		 southWestLat -= diffLat * ratio; 
		 northEastLng += diffLng * ratio; 
		 southWestLng -= diffLng * ratio; 
		 // extend north east 
		 largerBounds.extend(new GLatLng(northEastLat, northEastLng)); 
		 // extend south west 
		 largerBounds.extend(new GLatLng(southWestLat, southWestLng)); 
		 return largerBounds;
	}
});

/* END GMAP EXTENTIONS */

/* 
 * Initialise google map
 * @ param 	mapContainerJQ  	object jquery // Elt HTML où sera insérer la map
 */ 
function initGmap(mapContainerJQ){

	//on initialise la carte une seule fois
	if (!isMapInitialized) {
	
		// transforme le type [Object Object] de jQuery en [Object HTMLDivElement] pour Gmap
		var mapContainer = mapContainerJQ.get()[0];
		
		// Si la carte est compatible avec le navigateur et si l'élement HTML et définie ou initialise la carte
		if (GBrowserIsCompatible() && mapContainer) {
		
			// Paramètre pour la carte
			map = new GMap2(mapContainer);	// instancie la carte
			map.addControl(new GSmallMapControl());		// ajoute un contrôle pour le zoom
	 		map.addControl(new GMapTypeControl());		// ajoute un contrôle pour le type d'affichage
	 		map.enableContinuousZoom();		 			// aténue l'effet lors du zoom
	 		map.enableDoubleClickZoom();				// autorise le double clic pour zoomer
			map.setCenter(new GLatLng(0,0), 17);			// centre la carte
			
			// Positionne les marqueurs
			SetJobMarkers();
			
			// Définie la carte comme étant initialisé
			mapContainerJQ.attr('name','filled');		// attr name utilisé pour savoir si la carte à déjà été généré
			var isMapInitialized = (mapContainerJQ.attr('name') == 'filled'); //reload map if control hidden
		}
	} else {
		// Si la carte est déjà initialisé mais qu'on relance la fonction
		// on recenter simplement la carte
		ResetMapView();
	}
}

// @param 	mapContainer	HTMLDivElement
function initSingleGmap(mapContainerJQ,lat,lng){
	var lat = lat || 0;
	var lng = lng || 0;

	var mapContainer = mapContainerJQ.get()[0];
		
	// Si la carte est compatible avec le navigateur et si l'élement HTML et définie ou initialise la carte
	if (GBrowserIsCompatible() && mapContainer) {
	
		// Paramètre pour la carte
		map = new GMap2(mapContainer);	// instancie la carte
		map.addControl(new GSmallMapControl());		// ajoute un contrôle pour le zoom
		map.enableContinuousZoom();		 			// aténue l'effet lors du zoom
		map.enableDoubleClickZoom();				// autorise le double clic pour zoomer
		
		var point = new GLatLng(parseFloat(lat), parseFloat(lng));  
		map.setCenter(point, 17);		// centre la carte
		var marker = new GMarker(point, mapIconSingle);
		
		map.addOverlay(marker);
	}
	
	return map;
}

// réafiche la carte avec les valeurs comme pour la première visualisation
function ResetMapView() {
	map.centerAndZoomOnBounds(multiJobMarkerMapBounds, MultiJobMapInitialZoomLevel, MultiJobMapBoundsExtendRatio);	
	map.closeInfoWindow();
}


// Place les marqueurs sur la carte
function SetJobMarkers() {
		// Instancie le jobMarkerManager
		jobMarkerManager = new JobMarkerManager();
		
		// Converti le tableau jobDataArray en un tableau qui trie les jobs par coordonnée
		jobMarkerManager.Set(jobDataArray);
		
		// Instancie la méthode GLatLngBounds permettant de fixer les limites de la carte en lattitude et longitude
		multiJobMarkerMapBounds = new GLatLngBounds();
		
		//boucle sur la table des jobs pour les placer sur la carte et leur donner chacun leur comportement
		for (var key in jobMarkerManager.hashtable)
		{
			var jobMarkerObj = jobMarkerManager.hashtable[key];
			var coordinates = key.split(',');						
			var point = new GLatLng(parseFloat(coordinates[0]), parseFloat(coordinates[1]));  				
			
			// Initialise le marqueur
			var googleMarker = GetGMarker(jobMarkerObj, point);	
			
			//ajoute l'objet GMarker à l'objet jobMarker
			jobMarkerObj.GMarker = googleMarker;
			
			//ajoute le marqueur sur la carte
			map.addOverlay(googleMarker);
			
			// Elargit les limites avec les nouvelles coordonnées 
			multiJobMarkerMapBounds.extend(point);
			
			// Définie les évènements pour un marqueur
			SetGMarkerListener(jobMarkerObj);				
		}
		
		//zoom map to bounds of markers
	 	map.centerAndZoomOnBounds(multiJobMarkerMapBounds, MultiJobMapInitialZoomLevel, MultiJobMapBoundsExtendRatio);	
}

//initialise la création d'un marqueur
function GetGMarker(jobMarker, point) {
	var labelText = '';
	var imgIndex = jobMarker.Index;
	
	// Gère l'affichage du marqueur, un numéro si jobMarker n'a qu'une offre dans le tableau, sinon logo 'multi'
	var mapIcon = new GIcon(offreIcon);
	if(parseInt(jobMarker.Count) == 1){
		mapIcon = mapIconSingle;
		labelText = '';
	} else {
		mapIcon.image = markerIcon.replace('{extension}', 'multi')
		mapIcon.shadow = markerIcon.replace('{extension}', 'shadow_m');
		mapIcon.iconSize = new GSize(30, 30);
		mapIcon.shadowSize = new GSize(30, 30);
		labelText = String(jobMarker.Count);
	}
	
	// modifie le paramètre 'icon' du marqueur
	var markerOptions = {
		icon:mapIcon,
		"labelText": labelText,
		"labelOffset": new GSize(-10,-32),
		"labelClass": "labeled_marker"
	};
	
	// Instancie le marqueur et le renvoie
	// var marker = new GMarker(point, markerOptions);
	var marker = new LabeledMarker(point, markerOptions);

	return marker;   
}

function SetGMarkerListener(jobMarker) {
	var html = SetMultiJobGMarkerListenerHtml(jobMarker);

	//add info window click event	
	GEvent.addListener(jobMarker.GMarker, "click", function() {	
		var center = map.getCenter();
		currentInfoWindowJobMarker = jobMarker;						
		jobMarker.GMarker.openInfoWindowHtml(html, {maxWidth:500});			
		
		GEvent.addListener(this, "infowindowclose", function(){
			map.panTo(center);
		}); 
	});

}

function SetMultiJobGMarkerListenerHtml(jobMarkerObj) {
	var job = jobMarkerObj.Jobs[0];

	var html = new Array();
	html[html.length] = "<div class=\"mapInfoWindow\" align=\"left\">";

	// Contenu centrale de l'infobulle si plusieurs offre dans le même marqueur
	if (jobMarkerObj.Count > 1) {
		//multiple jobs @ coordinates				
		html[html.length] = GetHeadListedInfoWindowHtml(
			jobMarkerObj.Jobs[0].jobCity,
			jobMarkerObj.Count
		);
		html[html.length] = GetBodyListedInfoWindowHtml(jobMarkerObj);	
	} else {// contenu pour une seule offre dans un marqueur
		html[html.length] = GetMultiMarkerHtml(job);		
	}

	
	
	html[html.length] = "</div>";
	
	return html.join("");					
}

// Affichage des offres dans la même infodulle
function GetHeadListedInfoWindowHtml(jobsCity, totalJobCount){
	var html = new Array();
		
	//total jobs
	html[html.length] = "<p><strong>" + jobsCity + "</strong></p>";
	html[html.length] = "<p><strong><i>" + totalJobCount + " offres d'emploi trouvées pour cette localisation</i></strong></p>";
	
	return html.join("");
}

function GetBodyListedInfoWindowHtml(jobMarkerObj)
{
	var html = new Array();
	
	html[html.length] = '<div class="mapInfoWindow_content">';
	html[html.length] = '<table>';
	// Pour chaque offre un créé une ligne
	for (var i = 0, length = jobMarkerObj.Jobs.length; i < length; i++) {
		html[html.length] = "<tr><td><a href='" + base_url + "/index/fiche/id/" + jobMarkerObj.Jobs[i].jobId + "/" + jobMarkerObj.Jobs[i].jobTitle +"'>" + jobMarkerObj.Jobs[i].jobTitle + "</a><br />" + "<div class='map_Title'>" + jobMarkerObj.Jobs[i].company + "</div>" + jobMarkerObj.Jobs[i].jobCity + "</td>";
		html[html.length] = "<td>Numéro sur la carte : " + String(jobMarkerObj.Jobs[i].jobMapNum) + "</td></tr>";
	}
	html[html.length] = '</table>';
	html[html.length] = '</div>';

	return html.join("");;
}

// Appel de cette de fonction lors du clic dans la fiche pour aller d'une page à l'autre
function InfoWindowPaging(direction)
{	
	var infoDiv = $('#infoDiv');	// 
	var dir = direction || 0;	// Info suivante/précédente sinon on ne fait rien
	var jobsLength = currentInfoWindowJobMarker.Jobs.length;
		
	if ((currentInfoWindowPageIndex + dir) <= 0)
	{
		currentInfoWindowPageIndex = 0;
	}
	else if ((currentInfoWindowPageIndex + dir) >= (jobsLength))
	{
		currentInfoWindowPageIndex = jobsLength - 1;	
	}
	else {
		currentInfoWindowPageIndex = currentInfoWindowPageIndex + dir;
	}	
	
	var j = currentInfoWindowJobMarker.Jobs[currentInfoWindowPageIndex];
	
	// ajoute le corps de l'infobulle
	infoDiv.html(GetInfoWindowPagingHtml(j, (currentInfoWindowPageIndex + 1), jobsLength) + GetMultiMarkerHtml(j));
}


// Ajoute le contenu de l'infobulle avec la pagination de façon dynamique et lors du premier affichage
function GetInfoWindowPagingHtml(job, currentJobNum, totalJobCount) 
{
	var html = new Array();
		
	//total jobs
	html[html.length] = "<center><strong><i>" + totalJobCount + " offres d'emploi trouvées pour cette localisation</i></strong></center>";
	
	//page
	html[html.length] = "<div style='padding-top: 7px; padding-bottom: 5px;'>";
	
	
	html[html.length] = "Offre d'emploi  " + String(currentJobNum) + " de " + String(totalJobCount) + "&nbsp;&nbsp;&nbsp;";
	
	//page previous
	html[html.length] = ((currentJobNum - 1) > 0) ? "<a href=\"#\" onclick=\"InfoWindowPaging(-1);return false;\">Précédent</a>" : "Précédente";
	
	html[html.length] = " ... ";

	//page next
	html[html.length] = (currentJobNum < totalJobCount) ? "<a href=\"#\" onclick=\"InfoWindowPaging(1);return false;\">Suivant</a>" : "Suivant";

	html[html.length] = "</div>";

	return html.join("");
}

function GetMultiMarkerHtml(job)
{
	var htmlBase = "<a href='" + base_url + "/index/fiche/id/" +job.jobId + "/" + job.jobTitle +"' target='_parent'>" + job.jobTitle + "</a><br />" + "<div class='map_Title'>" + job.company + "</div>" + job.jobCity;
	var mapNo = "<br />Numéro sur la carte : " + String(job.jobMapNum);
			 				
	return htmlBase + mapNo;
}

//opens infoWindow for corresponding job map number
function OpenjobMapNum(jobDataIndex)
{
	var jobData = JobDataArray[jobDataIndex];
	
	var key = String(jobData.lat + "," + jobData.lng);
	var jobMarkerObj = jobMarkerManager.hashtable[key];
	
	if (jobMarkerObj != 'undefined')
	{
		currentInfoWindowPageIndex = 0;	
		currentInfoWindowJobMarker = jobMarkerObj;		
		
		if (jobMarkerObj.Count > 1) {
			for (var i = 0, length = jobMarkerObj.Jobs.length; i < length; i++) {
				if (String(jobData.jobId) == String(jobMarkerObj.Jobs[i].jobId))
				{
					currentInfoWindowPageIndex = i;
					break;
				}
			}			
		}
		
		jobMarkerObj.GMarker.openInfoWindowHtml(SetMultiJobGMarkerListenerHtml(jobMarkerObj),  {maxWidth:500});	
	}	
}

/* END MULTI-JOB MAP FUNCTIONS */

/* BEGIN JOB MARKER FUNCTIONS */

//Obj to store job data and jobCount/GMarker per coordinates
function JobMarker(jobCount, gMarker, jobData) {
	this.Count = jobCount;
	this.GMarker = gMarker;
	this.Jobs = new Array();
	this.Jobs.push(jobData);
}

//Job Marker Manager object to get hashtable of JobMarkers w/corresponding coordinates
function JobMarkerManager(){
    this.clear = hashtable_clear;
    this.containsValue = hashtable_containsValue;
    this.containsKey = hashtable_containsKey;
    this.get = hashtable_get;
    this.isEmpty = hashtable_isEmpty;
    this.keys = hashtable_keys;
    this.put = hashtable_put;
    this.remove = hashtable_remove;
    this.size = hashtable_size;
    this.toString = hashtable_toString;
    this.values = hashtable_values;
    this.Set = hashtable_set;
    this.hashtable = new Array();
}

// Retranscrit le tableau des offres dans un autres tableaux trié par coordonnées
function hashtable_set(jobDataArray){
	
	// on boucle sur toutes les valeurs de jobDataArray
	for (var i = 0, length = jobDataArray.length; i < length; i++) {
		
		// Stocke les informations du job dans la boucle courrante
		var jobData = jobDataArray[i];
		
		// Définie la clé du tableau est lat,long
		var key = jobData.lat + "," + jobData.lng;

		// si les coordonnées (clé) n'existe pas encore dans le tableau, on ajoute un nouveau marqueur dans hashtable	
		if (! this.containsKey(key)) {		
			this.put(key, new JobMarker(1, null, jobData));				
		}
		else {
			//contains coordinates, set add JobMarker to existing key			
			var jobMarkerObj = this.hashtable[key];			
			jobMarkerObj.Count = parseInt(jobMarkerObj.Count + 1);
			jobMarkerObj.Jobs.push(jobData);
			//set extended JobMarker to key
			this.hashtable[key] = jobMarkerObj;		
		}
	}		
}

/* Private hashtable helpers */
function hashtable_clear(){
    this.hashtable = new Array();
}

function hashtable_containsKey(key){
    var exists = false;
    for (var i in this.hashtable) {
        if (i == key && this.hashtable[i] != null) {
            exists = true;
            break;
        }
    }
    return exists;
}
function hashtable_containsValue(value){
    var contains = false;
    if (value != null) {
        for (var i in this.hashtable) {
            if (this.hashtable[i] == value) {
                contains = true;
                break;
            }
        }
    }
    return contains;
}

function hashtable_get(key){
    return this.hashtable[key];
}

function hashtable_isEmpty(){
    return (parseInt(this.size()) == 0) ? true : false;
}

function hashtable_keys(){
    var keys = new Array();
    for (var i in this.hashtable) {
        if (this.hashtable[i] != null) 
            keys.push(i);
    }
    return keys;
}

function hashtable_put(key, value){
    if (key == null || value == null) {
        throw "NullPointerException {" + key + "},{" + value + "}";
    }else{
        this.hashtable[key] = value;
    }
}

function hashtable_remove(key){
    var rtn = this.hashtable[key];
    this.hashtable[key] = null;
    return rtn;
}

function hashtable_size(){
    var size = 0;
    for (var i in this.hashtable) {
        if (this.hashtable[i] != null) 
            size ++;
    }
    return size;
}

function hashtable_toString(){
    var result = "";
    for (var i in this.hashtable)
    {      
        if (this.hashtable[i] != null) 
            result += "{" + i + "},{" + this.hashtable[i] + "}\n";   
    }
    return result;
}

function hashtable_values(){
    var values = new Array();
    for (var i in this.hashtable) {
        if (this.hashtable[i] != null) 
            values.push(this.hashtable[i]);
    }
    return values;
}
/* END JOB MARKER FUNCTIONS */

// Extend Google map api for labeled a marker
function LabeledMarker(latlng, options){
    this.latlng = latlng;
    this.labelText = options.labelText || "";
    this.labelClass = options.labelClass || "markerLabel";
    this.labelOffset = options.labelOffset || new GSize(0, 0);
	
	this.clickable = options.clickable || true;
	
	if (options.draggable) {
	    // This version of LabeledMarker doesn't support dragging.
	    options.draggable = false;
	}

    GMarker.apply(this, arguments);
}
// Le prototype Labeled Marker se repose sur la contruction d'un objet GMarker (on doit l'initialisé donc on passe l'objet GLatLng en arguement)
LabeledMarker.prototype = new GMarker(new GLatLng(0, 0));

// Etend le prototype LabeledMarker avec une nouvelle méthode
LabeledMarker.prototype.initialize = function(map) {
	GMarker.prototype.initialize.call(this, map);

	var div = document.createElement("div");
	div.className = this.labelClass;
	div.innerHTML = this.labelText;
	div.style.position = "absolute";
	map.getPane(G_MAP_MARKER_PANE).appendChild(div);
	
	if (this.clickable) {
		// Pass through events fired on the text div to the marker.
		var eventPassthrus = ['click', 'dblclick', 'mousedown', 'mouseup', 'mouseover', 'mouseout'];
		for(var i = 0; i < eventPassthrus.length; i++) {
			var name = eventPassthrus[i];
			GEvent.addDomListener(div, name, newEventPassthru(this, name));
		}

		// Mouseover behaviour for the cursor.
		div.style.cursor = "pointer";
	}

	this.map = map;
	this.div = div;
}

function newEventPassthru(obj, event) {
	return function() {
		GEvent.trigger(obj, event);
	};
}

LabeledMarker.prototype.redraw = function(force) {
	GMarker.prototype.redraw.call(this, map);

	// We only need to do anything if the coordinate system has changed
	if (!force) return;

	var p = this.map.fromLatLngToDivPixel(this.latlng);
	var z = GOverlay.getZIndex(this.latlng.lat());

	this.div.style.left = (p.x + this.labelOffset.width) + "px";
	this.div.style.top = (p.y + this.labelOffset.height) + "px";
	this.div.style.zIndex = z + 1; // Directly in front of the marker image
}

LabeledMarker.prototype.remove = function() {
	GEvent.clearInstanceListeners(this.div);
	this.div.parentNode.removeChild(this.div);
	this.div = null;
	GMarker.prototype.remove.call(this);
}

/*
 * Affiche un marqueur en lui passant comme paramètre une adresse
 */
function displayMarkerByAdress( map, address) {
	
	// Initialise le géocoder
	var geocoder = new google.maps.ClientGeocoder(); 
	
	// On indique au géocoder que par défault il doit chercher les adresse en France
	geocoder.setBaseCountryCode('fr');
	
	// Récupère un point en fonciton de l'adresse et affiche dans la foulée le marqueur
	geocoder.getLatLng(
		address,
		function (point) { // Récupère au passage  les données du point
			if (!point) {
				return false;
			} else {
				map.setCenter(point, 17);
				var marker = new google.maps.Marker(point);
				map.addOverlay(marker);
				return true;
			}
		}
	);
}

/*
 * Affiche un marqueur en lui passant comme paramètres les coordonnées
 */
function displayMarkerByCoords( map, latitude, longitude, zoom) {
	zoom = zoom || PIdefaultZoomLevel;
	point = new google.maps.LatLng(latitude, longitude);
	map.setCenter(point, zoom);
	var marker = new google.maps.Marker(point);
	map.addOverlay(marker);
	return true;
}

// Creates a marker whose info window displays the letter corresponding
// to the given index.
function createMarker(point, index, icon) {
	// Create a lettered icon for this point using our icon class
	var marker = new GMarker(point, icon);

	GEvent.addListener(marker, "click", function() {
		marker.openInfoWindowTabsHtml(index);
	});
	return marker; 
}

function drawCircle(lng,lat,rayon) {
	var Cradius = rayon / 1.609344; // mile radius 
	var Ccolor = '#0000ff';  			// color blue 
	var Cwidth = 3;         			// width pixels 
	var d2r = Math.PI/180;   			// degrees to radians 
	var r2d = 180/Math.PI;   			// radians to degrees 
	var Clat = (Cradius/3963)*r2d;		//  using 3963 as earth's radius 

	var Clng = Clat/Math.cos(lat*d2r); 
	var Cpoints = []; 
	for (var i=0; i < 33; i++) {
		var theta = Math.PI * (i/16); 
		Cx = lng + (Clng * Math.cos(theta)); 
		Cy = lat + (Clat * Math.sin(theta)); 
		Cpoints.push(new GPoint(Cx,Cy)); 
	}
	map.addOverlay(new google.map.Polyline(Cpoints,Ccolor,Cwidth)); 
}

// GUnload  on unload
$(window).unload( function() {
	GUnload();
} );
