/**
 * Classe contenant les données d'une station

 TODO:
 - recherche + handling des recherches venant du bloc rechercher une station
 */
function Station (name, number, address, fullAddress, latitude, longitude, opened) {
	this.name = name;
	this.number = number;
	this.address = address;
	this.fullAddress = fullAddress;
	this.ticket = 0;
	this.opened = opened;
	this.lat = latitude;
	this.lng = longitude;
	this.marker = null;
	this.state = 1; // 1 pour données de base chargées, 2 pour données étendues chargées
	this.drawn = false; // false si le marker n'a pas été dessiné (avec Station.drawMarker() ), true sinon
	this.availableBikes = 0;
	this.freeCapacity = 0;
	this.totalCapacity = 0;

	//gestion des événements

	this.createMarker = function (myIcon) {
		this.marker = new GMarker( new GLatLng( this.lat, this.lng), {icon:myIcon} );
		var instance = this;
		GEvent.addListener(this.marker, "click", function() {
			instance.openInfoWindow();
		} );
	}

	/**
	 * Dessine le marqueur associé à la station
	 */
	this.drawMarker = function() {
		if(! this.drawn && this.state >= 1) {
			map.addOverlay(this.marker);
			this.drawn = true;
		}
	}

	/**
	 * Ouvre l'infobulle de la station
	 * ATTENTION : ne pas utiliser sur des marqueurs dessinées par GMarkerManager !
	 */
	this.openInfoWindow = function () {
		map.closeInfoWindow();

		var html = "<div class=\"infobulle\">";
		if(this.name != "") {
			html += "<p class=\"titre\">"+this.name+"</p>";
			html += "<p class=\"numero\">"+GmapsTexts.station_num+" "+this.number+"</p>";
		} else {
			html += "<p class=\"titre\">"+GmapsTexts.station_num+" "+this.number+"</p>";
		}

		if(this.address != "") {
			html += "<p>"+this.address+"</p>";
		}

		// si elles ne sont pas en cache, va chercher les infos de disponibilité sur le webservice
		if(this.state == 1) {
			html += "<div class=\"details\"><p><b>"+GmapsTexts.station_details+"</b></p>";
			html += "<p>"+GmapsTexts.loading_message+"</p></div>";
		}
		else {
			html += "<div class=\"details\"><p>"+GmapsTexts.available_bikes+" <strong>"+this.availableBikes+"</strong></p>";
			html += "<p>"+GmapsTexts.free_capacity+" <strong>"+this.freeCapacity+"</strong></p></div>";
		}
		html += "<a href=\"#\" id=\"getStationDetails\">"+GmapsTexts.more_details+"</a>";
		html += "<p>&nbsp;</p></div>";

		this.marker.openInfoWindowHtml(html);

		var instance = this;
		var theLink = document.getElementById('getStationDetails');
		theLink.onclick = function () {
			//clic sur le lien "plus de détails" : actualise l'url envoyer à un ami, et ouvre la boite des détails
			tipAFriend(0, 0, 0, instance.number);
			leftBlock.updateDetails(instance);
			return false;
		}

		if(this.state == 1) {

			var request = GXmlHttp.create();
		    request.open("GET", stationDetailsUrl + "/"+ this.number, true);
			request.onreadystatechange = function() {

		   		if (request.readyState == 4) {
					var xmlDoc = request.responseXML;
					instance.availableBikes = xmlDoc.getElementsByTagName('available')[0].firstChild.nodeValue;
					instance.freeCapacity = xmlDoc.getElementsByTagName('free')[0].firstChild.nodeValue;
					instance.totalCapacity = xmlDoc.getElementsByTagName('total')[0].firstChild.nodeValue;
					instance.ticket = xmlDoc.getElementsByTagName('ticket')[0].firstChild.nodeValue;

					if(cacheEnabled) {
						instance.state = 2;
					}

					var html = "<p>"+GmapsTexts.available_bikes+" <b>"+instance.availableBikes+"</b></p>";
					html += "<p>"+GmapsTexts.free_capacity+" <b>"+instance.freeCapacity+"</b></p>";

					var infoWin = map.getInfoWindow();
					var content = infoWin.getContentContainers();
					if ( content.length > 0) {
						var ct = content[0].firstChild.getElementsByTagName('div')[0].getElementsByTagName('div');
						if(ct.length > 0) {
							ct[0].innerHTML = html;
						}
					}
                    else
                    {
                        setTimeout("station2(\"" + instance + "\", \"" + html + "\")",500);
                    }
					//actualise l'url de "envoyer à un ami"
					tipAFriend(0, 0, 0, instance.number);
				}
			}
			request.send(null);
		}
	}
}

function station2 (instance, html) {
    var infoWin = map.getInfoWindow();
    var content = infoWin.getContentContainers();
    if ( content.length > 0) {
        var ct = content[0].firstChild.getElementsByTagName('div')[0].getElementsByTagName('div');
        if(ct.length > 0) {
            ct[0].innerHTML = html;
        }
    }
}

/**
 * Classe contenant toutes les stations
 */
function Stations () {

	this.stations = [];
	this.startStation = null;		//référence vers la station qui doit être affichée en premier, avec l'infobulle ouverte
	this.markers = [];				//contient les références vers les marqueurs des stations "normales"
	this.closestMarkers = [];		//contient les références vers les marqueurs les plus proches
	this.minDists = [];				//contient les distances des n plus proches stations, trié par ordre croissant
	this.n = 0;	 //nombre de stations à marquer comme "plus proches" (0 = désactivé) -> ne pas changer la vlaeur à cet endroit !
	this.startIsClosest = false;
	this.init = false;

	/**
	 * Ajoute la station dans la liste
	 */
	this.add = function (newStation) {
		this.stations.push(newStation);
	}

	/**
	 * Dessine tous les marqueurs de la carte
	 * - desssine le marqueur de départ par la méthode Station.drawMarker
	 * - dessine les autres par le système GMarkerManager
	 */
	this.drawAllMarkers = function () {
		mm = new GMarkerManager(map, {borderPadding:1});
	    mm.addMarkers(this.markers,10,17);
	    mm.addMarkers(this.closestMarkers,10,17);
	    mm.refresh();

		if(this.startStation != null && ! this.startIsClosest) {
			this.startStation.createMarker(startIcon);
			map.addOverlay(this.startStation.marker);
			this.startStation.openInfoWindow();
			leftBlock.updateDetails(this.startStation);
		}
	}

	/**
	 * Recalcule tous les marqueurs en séparant les plus N proches de l'origine (orgin de type GLatLng)
	 */
	this.computeClosest = function (origin) {

		//vide les tableaux de marqueurs
		this.markers = [];
		this.closestMarkers = [];
		this.minDists = [];
		this.startIsClosest = false;

		var c = map.getBounds();
		var j = 0;
		var isClosest = false;
		var toSort = [];

		if(this.startStation != null) { var x = this.startStation.lat; var y = this.startStation.lng; }
		else { var x = 0; var y = 0 };



		for(var i = 0; i < this.stations.length ; i++)
		{
			if(this.stations[i].lat != x && this.stations[i].lng != y || this.init == false) {
				var coords = new GLatLng (this.stations[i].lat, this.stations[i].lng)
				//si la station est affichable à l'écran, et si la différenciation des N plus proches est activée
				if ( c.contains(coords) && this.n > 0) {

					//alors on regarde si elle est parmis les N plus proches
					var dist = origin.distanceFrom(coords);
					isClosest = false;

					if(j < this.n) {
						//pour les premiers points de la boucle, ils sont forcément parmi les plus proches
						//on les insère et on trie le tableau des plus proches
						this.minDists[this.minDists.length] = dist;
						this.minDists = ArrayFloatSort(this.minDists);
						j++;
					}
					else {
						//pour les autres, on regarde si ils sont parmi les N plus proches
						dist = parseFloat(dist);
						var biggest = parseFloat(this.minDists[this.minDists.length-1]);
						if(dist < biggest) {
							//si on a trouvé un plus proche, on remplace le plus éloginé des anciens plus proches
							//par le nouveau plus proche
							this.minDists[this.minDists.length-1] = dist;
							this.minDists = ArrayFloatSort(this.minDists);
						}
					}
					toSort.push(this.stations[i]);
				}
				else {
					//sinon, ajoute le marqueur dans le tableau des marqueurs "normaux"
					if(this.stations[i].opened == 1) {
						this.stations[i].createMarker(normalIcon);
					} else {
						this.stations[i].createMarker(closedIcon);	
					}
					this.markers.push(this.stations[i].marker);
				}
			}
		}	//fin du parcours de tous les points

		if(this.minDists.length > 0)
		{
			//ici on possède la distance du Nieme point le plus proche
			var thresold = this.minDists[this.minDists.length-1];

			//pour les points restants à trier, on regarde si leur distance est <= au seuil
			for(var i = 0; i< toSort.length; i++) {

				if(origin.distanceFrom(new GLatLng (toSort[i].lat, toSort[i].lng)) <= thresold && this.n > 0) {
					//si elle est parmi les + proches, on change sa couleur et on la range a part
					toSort[i].createMarker(closestIcon);
					this.closestMarkers.push(toSort[i].marker);
					//ici la station de départ est égaleemnt la plus proche. on le marque pour ne pas afficher 2 fois la station de départ
					if(toSort[i].lat == x && toSort[i].lng == y && this.init == false) {
						this.startIsClosest = true;
					}
				}
				else {
					//sinon, ajoute le marqueur dans le tableau des marqueurs "normaux"
					if(toSort[i].opened == 1) {
						toSort[i].createMarker(normalIcon);
					} else {
						toSort[i].createMarker(closedIcon);
					}
					this.markers.push(toSort[i].marker);
				}
			}
		}
	}

	/**
	 * Renvoie la station d'identifiant "number"
	 */
	this.getStationByNumber = function (number) {
		for(var i = 0 ; i < this.stations.length ; i ++) {
			if(this.stations[i].number == number) {
				return this.stations[i];
			}
		}
		return null;
	}
}

function Arrondissement (number, center, zoom, bounds) {
	this.number = number;
	this.center = center;
	this.zoom = zoom;
	this.bounds = bounds;
	
	this.toString = function () {
		return "number="+this.number+", center="+this.center+", zoom="+this.zoom;
	}
	this.contains = function (coords) {
		return this.bounds.contains(coords);
	}
}

function Arrondissements() {
	this.arrdts = new Array();
	
	this.add = function (arrdt) {
		this.arrdts.push(arrdt);
	}
	this.getByNumber = function(number) {
		for(var i = 0; i < this.arrdts.length ; i++) {
			if(this.arrdts[i].number == number) {
				return this.arrdts[i];
			}
		}
		return false;
	}
}

function Home() {
	this.isEnabled = false;
	this.icon = null;
	this.lat = 0;
	this.lng = 0;
	this.marker = null;

	/**
	 * Création de la home aux coordonnées spécifiées
	 */
	this.create = function (lat, lng) {
		this.icon = homeIcon;
		this.lat = lat;
		this.lng = lng;
		map.closeInfoWindow();
		this.marker = new GMarker( new GLatLng( this.lat, this.lng), {icon:homeIcon, draggable: true} );
		map.addOverlay(this.marker);
		this.isEnabled = true;


		//gestion des événements
		var instance = this;
		GEvent.addListener(this.marker, "dragstart", function() {
			map.closeInfoWindow();
		} );
		GEvent.addListener(this.marker, "dragend", function() {
	  		//opérations menées lors du déplacement de la home
	  		var newPoint = instance.marker.getPoint();
	  		this.lat = newPoint.lat();
	  		this.lng = newPoint.lng();
	  		map.panTo(newPoint);
	  		instance.computeClosestPoints();
	  	});
	  	GEvent.addListener(this.marker, "click", function() {
			instance.marker.openInfoWindowHtml('<div style="width:200px;"><p><b>'+GmapsTexts.home_info_title+'</b></p><p>'+GmapsTexts.home_info_text+'</p></div>');
		} );

		this.computeClosestPoints();
	}
	/**
	 * Déplacement de la home aux coordonnées spécifiées
	 */
	this.move = function(lat, lng) {
		map.closeInfoWindow();
		this.lat = lat;
		this.lng = lng;
		this.marker.setPoint(new GLatLng( this.lat, this.lng));
		this.computeClosestPoints();
	}

	/**
	 * Calcule les points les plus proches de la home et change leur couleur
	 */
	this.computeClosestPoints = function () {
  		if(stations.n > 0) {
	  		this.marker.openInfoWindowHtml(GmapsTexts.computing_message);
			stations.computeClosest(this.marker.getPoint());
			stations.drawAllMarkers();
			map.closeInfoWindow();
		}
	}
}

/**
 * Classe de gestion du bloc de gauche (détails d'une station + formulaire de recherche)
 */
function LeftBlock (id) {
	this.id=id;
	this.elem = document.getElementById(id);

	this.showLoadMessage = function() {
		this.elem.innerHTML = '<p>'+GmapsTexts.loading_message+'</p>';
	}

	this.updateDetails = function (theStation) {

		if(theStation.state == 1) {
			this.showLoadMessage();
			var instance = this;
			var request = GXmlHttp.create();
		    request.open("GET", stationDetailsUrl + "/"+ theStation.number, true);
			request.onreadystatechange = function() {

		   		if (request.readyState == 4) {
					var xmlDoc = request.responseXML;
					theStation.availableBikes = xmlDoc.getElementsByTagName('available')[0].firstChild.nodeValue;
					theStation.freeCapacity = xmlDoc.getElementsByTagName('free')[0].firstChild.nodeValue;
					theStation.totalCapacity = xmlDoc.getElementsByTagName('total')[0].firstChild.nodeValue;
					theStation.ticket = xmlDoc.getElementsByTagName('ticket')[0].firstChild.nodeValue;

					theStation.state = 2;

					instance.updateDetails(theStation);
				}
			}
			request.send(null);

		} else {
			var html = "";
			if(theStation.name != "") {
				html += "<p class=\"pad_bot\">"+GmapsTexts.lc_name+" <span>"+theStation.name+"</span></p>";
				html += "<p class=\"pad_bot\">"+GmapsTexts.lc_borne+" <span>"+theStation.number+"</span></p>";
			} else {
				html += "<p class=\"pad_bot\">"+GmapsTexts.lc_borne+" <span>"+theStation.number+"</span></p>";
			}

			if(theStation.fullAddress != "") {
				html += "<p class=\"pad_bot\">"+GmapsTexts.lc_full_address+"<br  /><span>"+theStation.fullAddress+"</span></p>";
			}

			html += "<div class=\"details\">";
			html += "<p>"+GmapsTexts.lc_available_bikes+" <span>"+theStation.availableBikes+"</span></p>";
			html += "<p>"+GmapsTexts.lc_free_capacity+" <span>"+theStation.freeCapacity+"</span></p>";
			html += "<p>"+GmapsTexts.lc_total_capacity+" <span>"+theStation.totalCapacity+"</span></p>";
			html += "</div>";
			html += "<p>"+GmapsTexts.lc_details_cb+" <span>";
			if(theStation.ticket == 1) { html += GmapsTexts.lc_yes; }else{ html += GmapsTexts.lc_no; }
			html += "</span></p>";

			this.elem.innerHTML = html;
			if(!cacheEnabled) {
				theStation.state = 1;
			}
		}
	}
}

var leftBlock = null;
var stations = new Stations();
var normalIcon = null;
var startIcon = null;
var closestIcon = null;
var home = new Home();
var mm = null;
var geo = new GClientGeocoder();
var arrdts = new Arrondissements();

//error handling
var reasons=[];
reasons[G_GEO_MISSING_ADDRESS]    = GmapsTexts.search_not_found;
reasons[G_GEO_UNKNOWN_ADDRESS]    = GmapsTexts.search_not_found;
reasons[G_GEO_UNAVAILABLE_ADDRESS]= GmapsTexts.search_not_found;
reasons[G_GEO_BAD_KEY]            = GmapsTexts.search_error;
reasons[G_GEO_TOO_MANY_QUERIES]   = GmapsTexts.search_error;
reasons[G_GEO_SERVER_ERROR]       = GmapsTexts.search_error;

function gMapsInit() {

	stations.n = nbOfClosest;
	leftBlock = new LeftBlock('stationDetails');

	/*if(keyWords)
	{
		alert(keyWords);
	}*/

    // Read the stations list
    var request = GXmlHttp.create();
    request.open("GET", stationsListUrl, true);

    request.onreadystatechange = function() {

	   	if (request.readyState == 4) {
			var xmlDoc = request.responseXML;
			// obtain the array of stations and loop through it
			var markers = xmlDoc.documentElement.getElementsByTagName("marker");

			var flag = 0;

			//pour chaque point lu, crée la station
			for (var i = 0; i < markers.length; i++) {
				var lat = parseFloat(markers[i].getAttribute("lat"));
				var lng = parseFloat(markers[i].getAttribute("lng"));
				var name = markers[i].getAttribute("name");
				var number = markers[i].getAttribute("number");
				var address = markers[i].getAttribute("address");
				var fullAddress = markers[i].getAttribute("fullAddress");
				var opened = markers[i].getAttribute("open");

				//if we are adding the start station
				if(displayMode == 2 && lat == orig_lat && lng == orig_long && !flag)
				{
					var station = new Station(name, number, address, fullAddress, lat, lng, opened);
					stations.startStation = station;
					flag = 1;
				}
				else {
					//add the other stations
					var station = new Station(name, number, address, fullAddress, lat, lng, opened);
				}
				stations.add(station);
			}

			//initialise la map
			map = new GMap2(document.getElementById(mapid));
			map.addControl(new GLargeMapControl());
		    map.addControl(new GMapTypeControl());
		    if(minimapEnabled) {
			    map.addControl(new GOverviewMapControl());
			}

			//récupère les coordonnées des arrondissements
			var arrdtsNodes = xmlDoc.documentElement.getElementsByTagName("arrondissement");
			for (var i = 0; i < arrdtsNodes.length; i++) {
				var minLat = parseFloat(arrdtsNodes[i].getAttribute("minLat"));
				var minLng = parseFloat(arrdtsNodes[i].getAttribute("minLng"));
				var maxLat = parseFloat(arrdtsNodes[i].getAttribute("maxLat"));
				var maxLng = parseFloat(arrdtsNodes[i].getAttribute("maxLng"));
				var number = arrdtsNodes[i].getAttribute("number");
				
				var zone = new GLatLngBounds(new GLatLng(minLat, minLng), new GLatLng(maxLat, maxLng));
				var arrdtCenter = zone.getCenter();
				var arrdtZoomLevel = map.getBoundsZoomLevel(zone);
				
				var myArr = new Arrondissement(number, arrdtCenter, arrdtZoomLevel, zone);
				arrdts.add(myArr);
			}

			//param map
			var mt = map.getMapTypes();
		    for (var i=0; i<mt.length; i++) { mt[i].getMinimumResolution = function() {return 10} }
			map.setCenter(startPoint, 15);

			var ctr = map.getCenter();
			tipAFriend(ctr.lat(), ctr.lng() , 0, 0);

			//gestion d'événements pour le lien "envoyer à un ami"
			GEvent.addListener(map, "moveend", function() {
				var ctr = map.getCenter();
				tipAFriend(ctr.lat(), ctr.lng() , 0, 0);
			});

			if(stations.n > 0) {

				//si on est en mode != 2 (c a dire pas dans un affichage de station), on crée la home
				if(displayMode != 2) {
					home.create(orig_lat, orig_long);
				}

				//ajoute la gestion d'événements pour créer/déplacer la home au clic sur la carte
				GEvent.addListener(map, "click", function(marker, point) {
					if(point) {
						if(! home.isEnabled) {
							home.create(point.lat(), point.lng());
						}
					}
				});
				GEvent.addListener(map, "dblclick", function(marker, point) {
					if(point) {
						if(home.isEnabled) {
							home.move(point.lat(), point.lng());
						} else {
							home.create(point.lat(), point.lng());
						}
					}
				});
			}

			stations.init = true;
			stations.computeClosest(map.getCenter());
			stations.init = false;
			stations.drawAllMarkers();

			//lance la recherche si on l'a fait depuis un paramètre GET
			if(keyWords != "")
			{
				launchSearch(keyWords);
			}
		}
	}
	request.send(null);

	//build the icons
	normalIcon = new GIcon();
	normalIcon.image = "http://labs.google.com/ridefinder/images/mm_20_red.png";
	normalIcon.shadow = "http://labs.google.com/ridefinder/images/mm_20_shadow.png";
	normalIcon.iconSize = new GSize(12, 20);
	normalIcon.shadowSize = new GSize(22, 20);
	normalIcon.iconAnchor = new GPoint(6, 20);
	normalIcon.infoWindowAnchor = new GPoint(5, 1);

	closestIcon = new GIcon();
	closestIcon.image = "http://labs.google.com/ridefinder/images/mm_20_blue.png";
	closestIcon.shadow = "http://labs.google.com/ridefinder/images/mm_20_shadow.png";
	closestIcon.iconSize = new GSize(12, 20);
	closestIcon.shadowSize = new GSize(22, 20);
	closestIcon.iconAnchor = new GPoint(6, 20);
	closestIcon.infoWindowAnchor = new GPoint(5, 1);

	startIcon = new GIcon();
	startIcon.image = "http://labs.google.com/ridefinder/images/mm_20_green.png";
	startIcon.shadow = "http://labs.google.com/ridefinder/images/mm_20_shadow.png";
	startIcon.iconSize = new GSize(12, 20);
	startIcon.shadowSize = new GSize(22, 20);
	startIcon.iconAnchor = new GPoint(6, 20);
	startIcon.infoWindowAnchor = new GPoint(5, 1);

	homeIcon = new GIcon();
	homeIcon.image = "http://labs.google.com/ridefinder/images/mm_20_yellow.png";
	homeIcon.shadow = "http://labs.google.com/ridefinder/images/mm_20_shadow.png";
	homeIcon.iconSize = new GSize(12, 20);
	homeIcon.shadowSize = new GSize(22, 20);
	homeIcon.iconAnchor = new GPoint(6, 20);
	homeIcon.infoWindowAnchor = new GPoint(5, 1);

	closedIcon = new GIcon();
	closedIcon.image = "http://labs.google.com/ridefinder/images/mm_20_white.png";
	closedIcon.shadow = "http://labs.google.com/ridefinder/images/mm_20_shadow.png";
	closedIcon.iconSize = new GSize(12, 20);
	closedIcon.shadowSize = new GSize(22, 20);
	closedIcon.iconAnchor = new GPoint(6, 20);
	closedIcon.infoWindowAnchor = new GPoint(5, 1);
}
