
/**
 * Maneja las calles del mapa.
 */
(function() {
	PorArriba.CallesMapManager = function( _map ) {
		this.mapManager = null;							// Mantiene una referencia al objeto MapManager
		
		this.map = _map; 								// El mapa donde se dinujan las calles
		
		this.calleStyle = new PorArriba.CalleStyle();	// Estilos de calles
		
		this.drawCallesEnabled = false;					// Habilita o deshabilita el dibujado de calles.
		this.calles = new Object();						// Objeto que tiene la coleccion de calles. Cada calle se guarda bajo el ID de la misma.
		this.callesZoom = new Object();					// Tiene una mapa de objetos referenciados por el zoom y cada uno de los objetos tiene un sub mapa de calles que se corresponde con el zoom indicado.
		this.callesDrawed = [];							// Vector con las calles dibujadas. Cada elemento tendrÃƒÂ¡ otro vector con los segmentos de la calle dibujados.
		this.stringIdCalles = '';						// Mantiene un string de IDs de calles separados por comas; indica las calles que estan actualmente en memoria.

		this.callesSelected = [];						// Vector que contiene las calles seleccionadas.
		this.nextPositionToAddSelectedCalle = 0;		// Siguiente posicion del vector de calles seleccionadas donde se agregarÃƒÂ¡ la proxima calle seleccionada.
		
		this.polyLines = new Object();					// Mapa de polylines de los segmentos de calles dibujados. Cada polyline se guarda con el id del segmento dibujado.
		this.arrowsIcons = new Object();				// Mapa de objetos iconos que representan la flecha de un segmento. Se guarda bajo un ID formado como [Id de Segmento] + [Nro de flecha].
		this.labels = new Object();						// Mapa de labels de calle (nombre de calle), uno por segmento, se guardan bajo el ID del segmento.
		this.alturaLabels = new Object();				// Mapa de etiquetas de altura de calles. Se guardan bajo un ID formado de la siguiente forma: [IDSegmento] + [Punto.PosX] + [Punto.PosY].

		this.checkRemoveCalles = this.calleStyle.timesToCheckTimeExpired;	//Variable local que indica cuando se debe remover una calle.
		
		this.doPanToOnRecorrido = true;					// Indica si debe hacer "panTo" al dibujar un recorrido.
		
		/*
		 * Establece las coordenadas del area de dibujo.
		 */
		this.drawAreaSouthWestLat;
		this.drawAreaSouthWestLong;
		this.drawAreaNorthEastLat;
		this.drawAreaNorthEastLong;
		
				
		this.actualRecorrido = null;					// Mantiene el recorrido del micro mostrado actualmente.
		
		
		/**
		 * Inicializa las calles
		 */
		this.initCalles = function () {
			var swf = layoutControl.getSWF();
			if (isUndefined(swf.initCalles)) {
				// Si no esta cargado, espero 2 segundo y lo vuelvo a cargar.
				setTimeout(initCalles, 2000); 
			} else {
				// Si esta cargado llamo a inicializar calles.
				var bounds = this.map.getBounds();
				var southWest = bounds.getSouthWest();
				var northEast = bounds.getNorthEast();
				swf.initCalles(this.map.getZoom(), northEast.lat(), northEast.lng() , southWest.lat(), southWest.lng());
			}
		}
		
		/**
		 * Setea el valor de enabled para habilitar o deshabilitar las calles
		 */
		this.setDrawCallesEnabled = function (value) {
			this.drawCallesEnabled = value;
		}

		/**
		 * Regresa un string con la lista de calles que estan en memoria (cache).
		 */
		this.getStringIdCalles = function(){
			return this.stringIdCalles;
		}

		/**
		 * Verifica si se ha agregado una calle
		 */
		this.containsCalle = function (calle) {
			var c = null;
			var type = typeof(calle);
			
			switch (type) {
				case 'object':
					if (isUndefined(calle.idCalle)) {
						c = this.getCalleById(calle.id);
					} else {
						c = this.getCalleById(calle.idCalle);
					}
					break;
					
				case 'string':
					c = this.getCalleById(calle);
					break;
			}
			
			return !(isUndefined(c) || c == null);
		}
		
		/**
		 * Regresa una por el id.
		 */
		this.getCalleById = function (calleId) {
			return this.calles[calleId];
		}
		
		/**
		 * Agrega una calle a la coleccion contenida en memoria, e inicializa variables de control asociada a la calle.
		 */
		this.addCalle = function (calle) {
			if (!this.containsCalle(calle)) {
				calle.selected = false;							// Si la calle esta seleccionada.
				calle.anySegmentDrawed = false;					// Si la calle tiene algun segmento dibujado.
				calle.alturaDrawed = false;						// Si la calle tiene las altura dibujadas. 
				calle.alturaDrawedByClickOnMap = false;			// Si las alturas fueron dibujadas por un click en el mapa.
				calle.lastTimeDrawed = new Date();				// La ultima vez que la calle fue dibujada.
				
				calle.showLabelNombre = this.showLabelNombreCalle(calle);	// Verifica si se debe mostrar el nombre de la calle.
				
				calle.zoomDrawed = -1;							// Zoom para el cual fue dibujada la calle.
				
				var idCalle = calle.idCalle;
				
				/*
				 * Crea idCalle e idSeg que son usados en algunos procesos, contienen los mismos datos que ID.
				 * Se duplican porque a veces fallaba al acceder por id.
				 */
				calle.idCalle = calle.id;
				idCalle = calle.id;

				var segmentos = calle.segmentos;
				var i = segmentos.length;
				while (i--) {
					var segmento = segmentos[i];
					segmento.idSeg = segmento.id; 
					segmento.calleId = calle.idCalle;											// Agrega el ID de la calle a cada segmento.
					segmento.middle = this.getMiddlePoint(segmento.esquinas.p);					// Establece el punto medio para cada segmento.
					segmento.points = this.buildArrayPoints(segmento);  						// Creo un array de puntos de las esquinas que forman parte del segmento.
					segmento.indexToDrawArrow = this.buildArrayIndexToDrawArrow(segmento);		// Creao un array de indices sobre los puntos donde se debe dibujar una flecha.
				};
				
				// Agrego la calle al mapa de calles
				this.calles[idCalle] = calle;
				
				// Agrega la calle al mapa de calles por zoom
				var cz = this.callesZoom[calle.zoomLevel];
				if (isUndefined(cz)){
					 cz = new Object();
					 this.callesZoom[calle.zoomLevel] = cz;
				}
				cz[idCalle] = calle;
				
				// Agrego la calle al string de calles (idCalles)
				if (this.stringIdCalles == '') this.stringIdCalles = idCalle;
				else this.stringIdCalles += ',' + idCalle ;
				
				return calle;
			} else {
				return this.getCalleById(calle.idCalle);
			}
		}
		
		/**
		 * Devuelve verdadero o falso, indicando si el nombre de la calle se debe mostrar o no.
		 */
		this.showLabelNombreCalle = function (calle){
			var names = this.calleStyle.noShowNames;
			var nombreCalle = calle.stdName.toLowerCase();
			var i = names.length;
			while (i--)	if (names[i] == nombreCalle) return false;
			return true;
		}
		
		/**
		 * Crea un array de puntos para el segmento indicado.
		 */
		this.buildArrayPoints = function (segmento) {
			var sPoints = [];
			var point;
			var esquina;
			var segEsquinasP = segmento.esquinas.p;
			var i = segEsquinasP.length;
			while (i--) {
				esquina = segEsquinasP[i];
				point = new GLatLng(esquina.la, esquina.lo);
				point.altura = esquina.al;
				sPoints.push(point);
			}
			
			return sPoints;
		}
		
		/**
		 * Crea un array de indices de los puntos donde se debe dibujar una flecha de direccion.
		 */
		this.buildArrayIndexToDrawArrow = function (segmento) {
			// Verifica si la calle tiene direccion indefinida, en ese caso no calcula nada
			if (segmento.dm == 2) return;
			
			/*
			 * Calcula un point (incremento) que debe sumar para colocar las flechas a intervalos similares
			 * en el segmento.
			 */
			var pReal = segmento.points.length / (this.calleStyle.maxArrowDrawed + 1);
			var point = parseInt(pReal);

			pReal = parseInt((pReal - point) * 10);  //Redondeo decimales
			if (pReal > 4) point++;
			
			var arrayIndexs = [];
			
			var i = this.calleStyle.maxArrowDrawed;
			while (i--) {
				var p = point * (i+1) - 1;
				arrayIndexs.push(p);
			} 
			
			return arrayIndexs;
		}
		
		/**
		 * Remueve una calle del cache
		 */
		this.removeCalle = function (calle) {
			if (this.containsCalle(calle)) {
				var calleId;
				if (typeof(calle) == 'object') {
					calleId = calle.idCalle;
				} else  {
					calleId = calle;
				}
				
				// Des-selecciona la calle que voy a remover
				this.unSelectCalle(calle);
				
				// Borra la calle del mapa de google.
				this.deleteCalle(calleId);
				
				// Remueve la calle de los distintos arrays (labels, alturas, etc.)
				this.removeCalleFromArrays(calleId);
				
				// Elimina la calle del string de id de calles
				this.stringIdCalles = this.stringIdCalles.replace(calleId + ',', '');		// Elimino en el medio, ej.: a,b,c,d ; elimino "b," -> a,c,d
				this.stringIdCalles = this.stringIdCalles.replace(',' + calleId, '');		// Elimino al final, ej.: a,b,c,d ; elimino ",d" -> a,b,c 
				this.stringIdCalles = this.stringIdCalles.replace(calleId, '');				// Elimino uno solo, ej.: a ; elimino "a" -> ""
				
				// Elimino la calle del mapa de callesZoom
				var c = this.getCalleById(calleId);
				var cz = this.callesZoom[c.zoomLevel];
				delete(cz[calleId]);

				// Elimino la calle del mapa de calles
				delete(this.calles[calleId]);
			}
		}


		/**
		 * Remueve de la memoria distintas partes de la calle, etiquetas de altura, nombre, polylines, etc.
		 */
		this.removeCalleFromArrays = function (calleId){
			var calle = this.getCalleById(calleId);
			
			/*
			 * Recorro los segmentos de la calle y elimino los datos asociados a cada segmento
			 * desde alturas hasta nombres de calle.
			 */
			var i = calle.segmentos.length;
			while (i--) {
				var segmento = calle.segmentos[i];
				var idSegmento = segmento.idSeg;

				//Borra el nombre de la calle
				delete(this.labels[idSegmento]);
	
				//Remove polyLines and arrows
				delete(this.polyLines[idSegmento]);
				this.removeArrowFromSegment(idSegmento);
	
				//Remove altura
				if (!isUndefined(segmento.points)) {
					var j = segmento.points.length;
					while (j--) {
						var labelAltura = this.alturaLabels[this.buildAlturaIndex(segmento,j)];
						if (!isUndefined(labelAltura)) delete(labelAltura);
					}
				}
			}
		} 

		/**
		 * Remueve las flechas
		 */
		this.removeArrowFromSegment = function(idSeg) {
			var idArrow;
			var i = this.calleStyle.maxArrowDrawed;
			while (i--) {
				idArrow = idSeg + i;
				if (!isUndefined(this.arrowsIcons[idArrow])) delete(this.arrowsIcons[idArrow]);
			}
		}

		/**
		 * Dibuja las calles para un nivel de zoom indicado, y en caso de no dibujar 
		 * verifica si la calle debe ser removida o no del cache.
		 */
		this.drawCalles = function(level){
			// Dibuja las calles si esta habilitado el dibujo de calles
			if (this.drawCallesEnabled) this.drawCallesByLevel(level);
			
			// Dibuja las calles seleccionadas. Estas se dibujan si o si.
			this.drawSelectedCalles();

			// Remueve las calles que hace tiempo no se dibujan.
			this.removeOldCalles();

			/*
			if (isUndefined(level) || !this.drawCallesEnabled) return;
			
			// Dibuja las calles del zoom indicado y de los zoom que son menores al indicado.
			var minLevel = this.calleStyle.minZoomLevel - 1;
			for( var l = level; l > minLevel; l-- ){
				var cz = this.callesZoom[l];
				if (!isUndefined(cz)) for (var c in cz) this.drawCalle(cz[c]);
			}
			
			this.drawSelectedCalles();
			
			// Remueve las calles que hace tiempo no se dibujan.
			this.removeOldCalles();
			*/
		}
		
		/**
		 * Dibuja calles por nivel
		 */
		this.drawCallesByLevel = function(level) {
			if (!isUndefined(level)) {
				// Dibuja las calles del zoom indicado y de los zoom que son menores al indicado.
				var minLevel = this.calleStyle.minZoomLevel - 1;
				for( var l = level; l > minLevel; l-- ){
					var cz = this.callesZoom[l];
					if (!isUndefined(cz)) for (var c in cz) this.drawCalle(cz[c]);
				}
			}
		}
		
		/**
		 * Dibuja las calles seleccionadas
		 */
		this.drawSelectedCalles = function () {
			// Dibuja las calles seleccionadas
			var cSelected = this.callesSelected;
			var i = cSelected.length;
			while (i--) {
				var calle = this.getCalleById(cSelected[i]);
				if (!calle.anySegmentDrawed) this.drawCalle(calle);
			}
		}
		
		/**
		 * Si es necesario remueve las calles.
		 */
		this.removeOldCalles = function() {
			//Verifica si tiene que eliminar ahora.
			this.checkRemoveCalles--;
			if (this.checkRemoveCalles) return;

			//Verifica y remueve las calles				
			this.checkRemoveCalles = this.calleStyle.timesToCheckTimeExpired;
			for(var c in this.calles){
				var calle = this.calles[c];
				if (!calle.anySegmentDrawed && this.isExpired(calle)) this.removeCalle(calle);
			}
		}
		
		/**
		 * Verifica si una calle a excedido el maximo de tiempo sin ser dibujada.
		 */
		this.isExpired = function(calle) {
			var t1 = new Date().getTime();
			var t2 = calle.lastTimeDrawed.getTime();
			var tExpired = t1 - t2;
			return (tExpired > this.calleStyle.maxTimeWithoutDrawed);
		}

		/**
		 * Dibuja una calle indicada por el ID
		 */
		this.drawCalleById = function (idCalle) {
			if (this.containsCalle(idCalle)) this.drawCalle(this.getCalleById(idCalle));
		}

		/**
		 * Dibuja una calle
		 */
		this.drawCalle = function(calle) {
			//try {
				
			if (this.containsCalle(calle)){
				if (isUndefined(calle.idCalle)) {
					calle = this.getCalleById(calle.id);
				} else {
					calle = this.getCalleById(calle.idCalle);
				}
			} else {
				calle = this.addCalle(calle);
			}
			
			// Verifica si se deben dibujar las calles o si la calle esta seleccionada
			// en ese caso las dibuja.
			if (!this.drawCallesEnabled && !calle.selected) return;
			
			// Setea los datos del area de dibujo actual
			this.setCurrentDrawArea(this.mapManager.drawingScopeBounds);
			
			// Si tiene algun segmento dibujado borro la calle para redibujarla
			if (calle.anySegmentDrawed) this.deleteCalle(calle.idCalle);


			// COMIENZA EL PROCESO DE DIBUJO DE LA CALLE
			
			var segmentos = calle.segmentos;
			var segmentoDrawed = false;
			var segmentosDrawed = [];
			var showOneLabel = (this.calleStyle.minZoomToShowLabelBySegment > this.map.getZoom());
			var notShowOneLabel = !showOneLabel;
			
			/*
			 * Dibuja los segmentos de la calle que sean necesarios 
			 */
			var i = segmentos.length;
			while (i--) {
				var segmento = segmentos[i];
				if (this.pasaPorAreaDeDibujo(segmento)) {
					if( this.drawSegmento(calle, segmento) ) {
						if (notShowOneLabel) this.drawLabelNombreCalle(calle, segmento);
						
						segmentoDrawed = true;
						segmento.zoomDrawed = this.map.getZoom();
						segmentosDrawed.push(segmento);
					}
				}
			}
			
			/*
			 * Si se dibujo algun segmento verifica si debe dibujanse la altura.
			 * Actualiza variables de dibujo.
			 */
			if (segmentoDrawed) {
				if (showOneLabel) this.drawOneLabelNombreCalle(calle);
				
				if (calle.alturaDrawed || calle.alturaDrawedByClickOnMap) 
					this.drawAlturaFromCalleId(calle.idCalle, calle.alturaDrawedByClickOnMap);
					
				calle.anySegmentDrawed = true;
				calle.lastTimeDrawed = new Date();
				calle.zoomDrawed = this.map.getZoom();
				this.callesDrawed.push(segmentosDrawed);
			}
			
			/*
			} catch (e) {
				alert("Conflictos al dibujar la calle:" + calle.nombre + ".\nErr: "+ e + ".");
			}
			* */
		}
		
		/**
		 * Setea las coordenadas del area de dibujo
		 */
		this.setCurrentDrawArea = function (drawingScopeBounds){
			var southWest = drawingScopeBounds.getSouthWest();
			var northEast = drawingScopeBounds.getNorthEast();

			this.drawAreaSouthWestLat = southWest.lat();
			this.drawAreaSouthWestLong = southWest.lng();
			this.drawAreaNorthEastLat = northEast.lat();
			this.drawAreaNorthEastLong = northEast.lng();
		}
		
		/**
		 * Verifica si un segmento de calle intersecta o esta contenido en el plano visible actual.
		 */
		this.pasaPorAreaDeDibujo = function (s){
			var southWestLat = this.drawAreaSouthWestLat;
			var southWestLong = this.drawAreaSouthWestLong;
			var northEastLat = this.drawAreaNorthEastLat;
			var northEastLong = this.drawAreaNorthEastLong;

			/**
			 * Verifica si los puntos extremos SouthWest y NorthEast, que determinan el area que 
			 * abarca el segmento, estan contenidos por el area de dibujo. 
			 * Esto se hace por si todo el segmento esta contenido, en cuyo caso no intersectara
			 * con ninguno de los lados del area visible.
			 * this.dentroDelAreaDeDibujo = function (pLat, pLong);
			 */
			if (this.mapManager.dentroDelAreaDeDibujo(s.southWestLat, s.southWestLong)) return true;
			if (this.mapManager.dentroDelAreaDeDibujo(s.northEastLat, s.northEastLong)) return true;

			/**
			 * Verifico si el segmento intersecta con alguno de los lados del area de dibujo
			 * seIntersectan(a1x, a1y, a2x, a2y, b1x, b1y, b2x, b2y) 
			 */
			//Segmento que identifica el lado este (derecho)
			if (seIntersectan(northEastLong, northEastLat, northEastLong, southWestLat, 
					s.northEastLong, s.northEastLat, s.southWestLong, s.southWestLat)) return true;
					
			//Segmento que identifica el lado oeste (izquierdo)
			if (seIntersectan(southWestLong, northEastLat, southWestLong, southWestLat, 
					s.northEastLong, s.northEastLat, s.southWestLong, s.southWestLat)) return true;

			//Segmento que identifica el lado norte (superior)
			if (seIntersectan(southWestLong, northEastLat, northEastLong, northEastLat, 
					s.northEastLong, s.northEastLat, s.southWestLong, s.southWestLat)) return true;

			//Segmento que identifica el lado sur (inferior)
			if (seIntersectan(southWestLong, southWestLat, northEastLong, southWestLat, 
					s.northEastLong, s.northEastLat, s.southWestLong, s.southWestLat)) return true;
					
			return false;
		}
		
		/**
		 * Retorna verdadero o falso, para indicar si se dibujo alguna parte del segmento o no.
		 */
		this.drawSegmento = function(calle, segmento) {
			// Verifica si el segmento ya esta dibujado, en ese caso no lo dibuja.
			if (segmento.drawed) return true;
			
			var idSegmento = segmento.idSeg;
			
			// Verifique que no existan datos cargados en memoria sobre este segmento
			var pLines = this.polyLines[idSegmento];
			
			// Verifica que no se este dibujando en un zoom distinto, si es asi elimina el segmento que esta en memoria
			// para forzar la creacion de uno nuevo.
			if (segmento.zoomDrawed != this.map.getZoom()){
				delete(this.polyLines[idSegmento]);
				pLines = this.polyLines[idSegmento];
			}
			
			if (isUndefined(pLines)) {
				//Crea el polyLines
				var pLines = new GPolyline(segmento.points, this.calleStyle.getLineColor(calle.zoomLevel), this.calleStyle.getCalleWeight(this.map.getZoom()), this.calleStyle.opacity);
			 	this.polyLines[idSegmento] = pLines;

			 	GEvent.addListener(pLines, "click", function() {
			 		var calle = mapManager.callesMapManager.getCalleById(segmento.calleId);
	 				drawOrRemoveAlturaCalle(calle, true);
				});
			}

			// Muestra el polyline
		 	this.map.addOverlay(pLines);
			
			// Verifica si se deben mostrar las flechas.
			// Si no se muestra el nombre de la calle, entonces tampoco se muestran las flechas.
			if (this.calleStyle.minLevelToDrawArrow <= this.map.getZoom() && calle.showLabelNombre) this.drawArrow(segmento);
			
			segmento.drawed = true;
			
			return true;
		}

		/**
		 * Dibuja las flechas de orientacion del segmento.
		 */
		this.drawArrow = function (segmento) {
			// No dibuja las flechas
			return;
			
			// Verifica si la calle tiene direccion indefinida, en ese caso no dibuja nada
			/*
			if (segmento.dm == 2) return;
			
			var indexs = segmento.indexToDrawArrow;
			var points = segmento.points;
			
			var i = indexs.length;
			while (i--) this.drawArrowByPoint(segmento, indexs[i], i);
			*/
		}
		
		/**
		 * Dibuja una flecha para un punto en particular
		 */
		this.drawArrowByPoint = function(segmento, pointIndex, nro) {
			var points = segmento.points;
			
			/*
			 * pointIndex indica el punto inicial de la flecha.
			 * Verifico que este no sea menor que cero ni mayor que la longitud de puntos - 2.
			 * Esto es asi porque la flecha siempre se dibuja desde el "pointIndex" uno hacia el "pointIndex+1", 
			 * (con el punto inicial y final se marca la direccion de la flecha).
			 */
			if (pointIndex > points.length-2) pointIndex = points.length-2;
			if (pointIndex < 0) pointIndex = 0;

			/*
			 * Selecciono los puntos para dibujar la flecha 
			 */
			var p0 = points[pointIndex];
			var p1 = points[pointIndex+1];

			// Creo el indice para identificar la flecha.
			var arrowIndex = segmento.idSeg + nro;

			// Busco si ya existe una flecha creada con ese indice
			var arrowIcon = this.arrowsIcons[arrowIndex];
		
			// Verifico si ya existe una flecha para este indice
	        if(isUndefined(arrowIcon)){
		        // Genero las nuevas coordenadas para la flecha
				var newLat = (p0.lat() + p1.lat())/2;
				var newLong = (p0.lng() + p1.lng())/2;
	
				// Calculo el tamaÃƒÂ±o de la flecha
				var size = this.calleStyle.getCalleWeight(this.map.getZoom())*2.5;
				
				// Genero una nueva flecha
				// Se pasa el punto p1 y luego el p0, porque el vector de points esta ordenado al reves, por la forma de los while
	       		arrowIcon = arrowHead(p1, p0, size, new GLatLng(newLat, newLong), segmento.dm);
	       		
			 	// Agrego la flecha en el cache de flechas.
			 	this.arrowsIcons[arrowIndex] = arrowIcon;
	        }
       	
       		// Si la generacion salio correcta la agrego en el mapa.
		 	if(arrowIcon != null) this.map.addOverlay(arrowIcon);
		}
		
		/**
		 * Dibuja solo un nombre de calle
		 */
		this.drawOneLabelNombreCalle = function(calle) {
			// Si no se debe dibujar el nombre de la calle.
			if (!calle.showLabelNombre) return;
			
			//Selecciona el segmento sobre el cual se va a dibujar el label
			var segmentos = calle.segmentos;
			var arraySeg = [];
			var segmento;
			var i = segmentos.length;
			while(i--) {
				segmento = segmentos[i];
				if (segmento.drawed) arraySeg.push(segmento);
			}
			
			// Si ninguno esta dibujado retorna
			if (arraySeg.length == 0) return;

			segmento = arraySeg[parseInt(arraySeg.length/2)];
			this.drawLabelNombreCalle(calle, segmento)
		}
		
		/**
		 * Dibuja la etiqueta con el nombre de la calle sobre un segmento en particular.
		 * Si se establece la variable override = true, esto obliga a que la etiqueta se 
		 * redibuje aunque la misma este o no dibujada.
		 */
		this.drawLabelNombreCalle = function (calle, segmento, override){
			return;
			
			/*
			// Si no se debe dibujar el nombre de la calle.
			if (!calle.showLabelNombre) return;

			// Si el segmento no esta dibujado, no dibujo la etiqueta.
			if (!segmento.drawed) return;
			
			var idSegmento = segmento.idSeg;
			var labelNombreCalle = this.labels[idSegmento];

			if (isUndefined(override)) override = false;
			
			if (override && !isUndefined(labelNombreCalle)) {
			 	this.map.removeOverlay(labelNombreCalle);
				delete(this.labels[idSegmento]);
			}
			
			if (override || isUndefined(labelNombreCalle)) {
		        var latlng = segmento.middle;
		        var labelMouseOverColor;
		
				// Selecciono el color de la etiqueta de acuerdo a si esta seleccionada o no.
				if (calle.selected) labelMouseOverColor = this.calleStyle.selectedLabelColorMouseOver;
		        else labelMouseOverColor = this.calleStyle.getLabelColorMouseOver(calle.zoomLevel);
		
				var lblStyle = this.calleStyle.getNewLabelNombreStyle(calle.zoomLevel, calle.selected);
				lblStyle.labelText = calle.stdName;
				lblStyle.labelOffset = new GSize(-calle.nombre.length*2.5, -10);
		
		        labelNombreCalle = this.createCalleLabel(calle, latlng, lblStyle);
		        
				this.setupLabelMouseEvent(labelNombreCalle, lblStyle.color, labelMouseOverColor, lblStyle.backgroundColor);
			 	this.labels[idSegmento] = labelNombreCalle;
			}

		 	this.map.addOverlay(labelNombreCalle);
		 	labelNombreCalle.drawed = true;
		 	* */
		}
		
		/**
		 * Regresa el punto medio de una coleccion de puntos
		 */
		this.getMiddlePoint = function(points){
			var middlePointIndex = parseInt((points.length-1)/2);
			var p0 = points[middlePointIndex];
			var p1 = points[middlePointIndex+1];
			
			var la;
			var lo;

			// Verifica si el punto tiene el parametro "la", sino busca por lat
			if (isUndefined(p0.la)) {
				/*
				 * Calcula mitad de la distancia entre el punto p0 y p1.
				 */
				la = Math.abs((p0.lat() - p1.lat())/2);
				lo = Math.abs((p0.lng() - p1.lng())/2);
				
				/*
				 * Verfica cual es el menor punto y con ese se determina la latitud final. 
				 */
				if (p0.lat() < p1.lat()) la += p0.lat();
				else la += p1.lat();
				
				/*
				 * Verfica cual es el menor punto y con ese se determina la longitud final. 
				 */
				if (p0.lng() < p1.lng()) lo += p0.lng();
				else lo += p1.lng();
			} else {
				/*
				 * Calcula mitad de la distancia entre el punto p0 y p1.
				 */
				var la = Math.abs((p0.la - p1.la)/2);
				var lo = Math.abs((p0.lo - p1.lo)/2);
				
				/*
				 * Verfica cual es el menor punto y con ese se determina la latitud final. 
				 */
				if (p0.la < p1.la) la += p0.la;
				else la += p1.la;
				
				/*
				 * Verfica cual es el menor punto y con ese se determina la longitud final. 
				 */
				if (p0.lo < p1.lo) lo += p0.lo;
				else lo += p1.lo;
			}
			
			return new GLatLng(la, lo);
		}

		/**
		 * Setea propiedades al label de una calle.
		 */
		this.setupLabelMouseEvent = function(labelNombreCalle, textColor,labelMouseOverColor, lineColor){
			GEvent.addListener(labelNombreCalle, "mouseover", function() {
				labelNombreCalle.setColor(textColor,labelMouseOverColor )
			});
			GEvent.addListener(labelNombreCalle, "mouseout", function() {
				labelNombreCalle.setColor(textColor, lineColor)
			});
		}

		/**
		 * Dibuja la altura de una calle, por el id de calle.
		 * clickOnMap: true o false, para indicar si la instruccion fue dada haciendo click sobre
		 * la equitqueta en el mapa o no.
		 */
		this.drawAlturaFromCalleId = function(idCalle, clickOnMap){
		 	this.drawAltura(this.getCalleById(idCalle), clickOnMap);
		}

		/**
		 * Borra la etiquetas de la vista del plano, pero no las remueve de la memoria.
		 */
		this.deleteAlturaFromCalleId = function(idCalle){
		 	this.deleteAltura(this.getCalleById(idCalle));
		}

		/**
		 * Genera un indice para identificar a las alturas de las calles
		 */
		this.buildAlturaIndex = function (segmento, pointNumber) {
			return ("" + segmento.idSeg + segmento.points[pointNumber].x + segmento.points[pointNumber].y);
		}

		/**
		 * Dibuja la altura de una calle.
		 * clickOnMap: true o false, para indicar si la instruccion fue dada haciendo click sobre
		 * la equitqueta en el mapa o no.
		 */
		this.drawAltura = function(calle, clickOnMap){
			if (isUndefined(clickOnMap)){
				clickOnMap = false;
			}
			if (isUndefined(calle.alturaDrawed) || !calle.alturaDrawed) {
				var segmentos = calle.segmentos;
				var i = segmentos.length;
				while (i--) {
			 		var segmentoActual = segmentos[i];
			 		if (segmentoActual.drawed) {
			 			var points = segmentoActual.points;
			 			var j = points.length;
			 			while (j--) {
			 				var lblStyle = this.calleStyle.getNewLabelAlturaStyle();
				 			var actualPoint = points[j];
				 			var alturaIndex = this.buildAlturaIndex(segmentoActual,j);
				 			if (isUndefined(this.alturaLabels[alturaIndex])) {
				 				lblStyle.labelText = actualPoint.altura;
					 			this.alturaLabels[alturaIndex] = new LabeledMarker(actualPoint, lblStyle);
				 			}
			 				this.map.addOverlay(this.alturaLabels[alturaIndex]);
				 		}
			 		}
			 	}
			 	
			 	calle.alturaDrawed = true;
			 	calle.alturaDrawedByClickOnMap = clickOnMap;
			}
		}

		/**
		 * Borra las etiquetas de altura de una calle.
		 * clickOnMap: true o false, para indicar si la instruccion fue dada haciendo click sobre
		 * la equitqueta en el mapa o no.
		 */
		this.deleteAltura = function (calle, clickOnMap){
			if (!calle.alturaDrawed) return;
			
			var segmentos = calle.segmentos;
			var i = segmentos.length;
			while (i--) {
				var segmento = segmentos[i];
				if (segmento.drawed) this.deleteAlturaFromSegment(segmento);
			}
		 	
		 	calle.alturaDrawed = false;
		 	if (clickOnMap) calle.alturaDrawedByClickOnMap = false;
		}
		
		/**
		 * Borra las alturas de un segmento
		 */
		this.deleteAlturaFromSegment = function(segmento){
 			var points = segmento.points;
 			var i = points.length;
 			while (i--) {
	 			var actualPoint = points[i];
	 			var alturaIndex = this.buildAlturaIndex(segmento,i);
 				this.map.removeOverlay(this.alturaLabels[alturaIndex]);
 				delete(this.alturaLabels[alturaIndex]);
	 		}
		}
		
		/**
		 * Borra las calles del mapa, pero no las remueve de la memoria.
		 */
		this.deleteCalles = function (deleteSelected){
			var i = this.callesDrawed.length;
			while (i--) this.deleteSegmentosDrawed(this.callesDrawed[i], deleteSelected);
			this.callesDrawed = [];
		}
		
		/**
		 * Borra una calle del mapa, pero no las remueve de la memoria.
		 */
		this.deleteCalle = function (calleId, deleteSelected){
			var calle = this.getCalleById(calleId);
			
			if (isUndefined(deleteSelected)) deleteSelected = false;
			
			// Si la calle esta seleccionada no la borra.
			if (calle.selected && !deleteSelected && calle.zoomDrawed == this.map.getZoom() ) return;
			
			if (!calle.anySegmentDrawed) return;
			
			var segmentos = calle.segmentos;
			var i = segmentos.length;
			var segmento;

			if (calle.alturaDrawed) {
				// Debo borrar la altura
				while (i--) {
					segmento = segmentos[i];
					if (segmento.drawed) {
					 	this.deleteNombreCalleFromSegment(segmento);
					 	this.deleteAlturaFromSegment(segmento);
				 		this.deletePolyLinesAndArrowsFromSegment(segmento);
					}
				}
			 	calle.alturaDrawed = false;
			} else {
				// No hace falta borrar las alturas, no estan dibujadas
				while (i--) {
					segmento = segmentos[i];
					if (segmento.drawed) {
					 	this.deleteNombreCalleFromSegment(segmento);
				 		this.deletePolyLinesAndArrowsFromSegment(segmento);
					}
				}
			}
			
			//Elimino la calle del vector de calles
			this.removeCalleFromCallesDrawed(calle);
			
			calle.anySegmentDrawed = false;
		}
		
		/**
		 * Elimina una calle del vector de calles dibujadas
		 */
		this.removeCalleFromCallesDrawed = function (calle) {
			var newArray = [];
			var cDrawed = this.callesDrawed;
			var max = cDrawed.length;
			//Uso for y no while porque necesito recorrer el vector desde menor a mayor
			for (var i=0; i<max; i++) if (cDrawed[i].idCalle != calle.idCalle) newArray.push(cDrawed[i]);
			this.callesDrawed = newArray;
		}

		/**
		 * Borra una calle que esta dibujada.
		 */
		this.deleteSegmentosDrawed = function(segmentos, deleteSelected) {
			var calle = this.getCalleById(segmentos[0].calleId);
			
			if (isUndefined(deleteSelected)) deleteSelected = false;
			
			// Si la calle esta seleccionada no la borra.
			if (calle.selected && !deleteSelected && calle.zoomDrawed == this.map.getZoom()) return;
			
			var i = segmentos.length;
			var segmento;

			if (calle.alturaDrawed) {
				// Debo borrar la altura
				while (i--) {
					segmento = segmentos[i];
				 	this.deleteNombreCalleFromSegment(segmento);
				 	this.deleteAlturaFromSegment(segmento);
			 		this.deletePolyLinesAndArrowsFromSegment(segmento);
				}
			 	calle.alturaDrawed = false;
			} else {
				// No hace falta borrara las alturas, no estan dibujadas
				while (i--) {
					segmento = segmentos[i];
				 	this.deleteNombreCalleFromSegment(segmento);
			 		this.deletePolyLinesAndArrowsFromSegment(segmento);
				}
			}
			calle.anySegmentDrawed = false;
		}

		/**
		 * Borra las etiquetas del nombre de una calle (1 por segmento)
		 */
		this.deleteNombreCalle = function (calle){
			var segmentos = calle.segmentos;
			var i = segmentos.length;
			while (i--) this.deleteNombreCalleFromSegment(segmentos[i]);
		}
		
		/**
		 * Borra el label de calle de un segmento
		 */
		this.deleteNombreCalleFromSegment = function (segmento) {
			var labelNombreCalle = this.labels[segmento.idSeg];
			if (!isUndefined(labelNombreCalle)){
				this.map.removeOverlay(labelNombreCalle);
				labelNombreCalle.drawed = false;
			}
		}
		
		/**
		 * Borra del mapa las lineas y flechas asociadas a una calle.
		 */
		this.deletePolyLinesAndArrows = function (calle){
			var segmentos = calle.segmentos;
			var i = segmentos.length;
			while (i--) this.deletePolyLinesAndArrowsFromSegment(segmentos[i]);
		}
		
		/**
		 * Borra las lineas y flechas de un segmento
		 */
		this.deletePolyLinesAndArrowsFromSegment = function (segmento){
	 		var o = this.polyLines[segmento.idSeg];

	 		if (!isUndefined(o)) this.map.removeOverlay(o);

	 		this.deleteArrowsFromSegment(segmento.idSeg);
 			segmento.drawed = false;
		}

		/**
		 * Borra las flechas
		 */		
		this.deleteArrowsFromSegment = function(idSeg) {
			var i = this.calleStyle.maxArrowDrawed;
			while (i--) {
				var idArrow = idSeg + i;
				var arrow = this.arrowsIcons[idArrow];
				if (!isUndefined(arrow)) this.map.removeOverlay(arrow);
			}
		}

		/**
		 * Selecciona una calle. Le cambia el color de la etiqueta y la agrega a un vector de calles seleccionadas.
		 * Pero antes des-selecciona las calles seleccionadas actualmente.
		 */
		this.selectCalle = function (idCalle){
	 		var calle = this.getCalleById(idCalle);
	 		if (calle.selected) return;

			//Si llego al maximo empieza reemplazando la primera calle.
			if (this.nextPositionToAddSelectedCalle == this.calleStyle.maxCallesSelected)
				this.nextPositionToAddSelectedCalle = 0;
				
			//Si hay una calle previamente seleccionada la deselecciona
			var idOldCalle = this.callesSelected[this.nextPositionToAddSelectedCalle];
			if (!isUndefined(idOldCalle)) {
				this.unSelectCalle(idOldCalle, false);
				if (!this.drawCallesEnabled) this.deleteCalle(idOldCalle, true);
			}
		
			//Dibuja y agrega a la lista la calle actual
	 		calle.selected = true;
			if (!calle.anySegmentDrawed) this.drawCalle(calle);
	 		this.callesSelected[this.nextPositionToAddSelectedCalle] = idCalle;
	 		
	 		//Coloca el nombre de la calle en los segmentos
	 		var segmentos = calle.segmentos;
	 		var i = segmentos.length;
	 		while (i--) this.drawLabelNombreCalle(calle, calle.segmentos[i], true);

	 		this.nextPositionToAddSelectedCalle++;
		}
		
		/**
		 * Des-selecciona las calles seleccionadas actualmente.
		 */
		this.unSelectCalles = function () {
			var cSelected = this.callesSelected;
			var i = cSelected.length;
			while (i--) this.unSelectCalle(cSelected[i].idCalle, true);
		}

		/**
		 * Des-selecciona una calle en particular.
		 */
		this.unSelectCalle = function (idCalle, removeFromVector) {
			var calle = this.getCalleById(idCalle);
			if (calle.selected) {
				calle.selected = false;
				
				var segmentos = calle.segmentos;
				var i = segmentos.length;
				while (i--) {
					this.deleteNombreCalle(calle);
					this.drawLabelNombreCalle(calle, segmentos[i], true);
				}
				
				if (isUndefined(removeFromVector)) removeFromVector = true;
				
				if (removeFromVector) {
					//Remueve la calle del vector de calles seleccionadas
					var noMoveElement = true;
					var cSelected = this.callesSelected; 
					i = cSelected.length;
					for (var j=0; j<i && noMoveElement; j++) noMoveElement = (cSelected[j] != idCalle);
					for (j=j; j<i; j++) cSelected[j-1] == cSelected[j];
					delete(cSelected[i]);
					
					if (this.nextPositionToAddSelectedCalle == i) this.nextPositionToAddSelectedCalle--; 
				}
			}
		}
		
		/**
		 * Crea una etiqueta.
		 */
		this.createCalleLabel = function(calle, latlng, lblStyle) {
			var cLabel = new LabeledMarker(latlng, lblStyle);
 			GEvent.addListener(cLabel, "click", function() {
 				drawOrRemoveAlturaCalle(calle, true);
			});
			return cLabel;
		}
		
		/**
		 * Efectua la operacion panTo, hacia una calle en particular.
		 */
		this.panToCalle = function(idCalle){
			var calle = this.getCalleById(idCalle);
			// Busco cual es el segmento del medio.
			var i = Math.round((calle.segmentos.length-1)/2);	
			this.map.panTo(calle.segmentos[i].middle);
		}
		
		/**
		 * Selecciona y se posiciona en una calle.
		 */
		this.selectAndPanToCalle = function (idCalle){
			this.selectCalle(idCalle);
			this.panToCalle(idCalle);
		}

		/**
		 * Dibuja un recorrido.
		 */		
		this.drawRecorrido = function(points){
			var arrowIcon;
			var recorridoPoints = [];
			
			var arrows = [];
			var arrowCounts = this.calleStyle.showArrowOnRecorridoByPoints(this.map.getZoom());
			var arrowWidth = this.calleStyle.getCalleWeight(this.map.getZoom())*2;

			var previousPoint = null;
			var count=0;
			for(var i=0; i<points.length; i++) {
				var point = new GLatLng(points[i].la, points[i].lo);

				if ( i && (previousPoint == null || (previousPoint != null && previousPoint.lat() != point.lat() && previousPoint.lng() != point.lng()))){
			 		recorridoPoints.push(point);
			 		
			 		if (!(arrowCounts--)) {
			 			// Calculo el punto donde se debe dibujar una flecha.
			 			var p0 = recorridoPoints[count-1];
			 			var p1 = recorridoPoints[count];
			 			var lat = (p1.lat() - p0.lat()) / 2 + p0.lat();
			 			var lng = (p1.lng() - p0.lng()) / 2 + p0.lng();
			 			var pArrow = new GLatLng(lat, lng);		// El punto donde debe dibujarse la flecha
			 			
			 			arrowIcon = arrowHead(p0, p1, arrowWidth, pArrow, 3); // Ultimo parametro igual a 0 indica flechas de una sola mano
			 			arrowCounts = this.calleStyle.showArrowOnRecorridoByPoints(this.map.getZoom());
			 			arrows.push(arrowIcon);
			 		}
	
		 			count++;
				}
		 		previousPoint = point;
			}
			
			//Busca el punto medio y hace panTo
			/*
			if (this.doPanToOnRecorrido) {
				var middlePoint = this.getMiddlePoint(points);
				this.map.panTo(middlePoint);
			}
			*/
			
			var recorridoPolyline = 
					new GPolyline(recorridoPoints, 
							this.calleStyle.recorridoColor, this.calleStyle.getRecorridoWeight(this.map.getZoom()), 
							this.calleStyle.recorridoOpacity);
			
			recorridoPolyline.points = points;
			
			this.removeRecorrido();
			this.actualRecorrido = recorridoPolyline;
			this.actualRecorrido.arrowsIcons = arrows;
			this.actualRecorrido.zoomDrawed = this.map.getZoom();
			this.drawActualRecorrido();
			this.doPanToOnRecorrido = true;
		}
		
		/**
		 * Dibuja el recorrido actual
		 */
		this.drawActualRecorrido = function () {
			if (this.actualRecorrido != null && !this.actualRecorrido.drawed) {
				if (this.actualRecorrido.zoomDrawed != this.map.getZoom()) {
					//Si cambio el zoom redibuja el recorrido
					this.doPanToOnRecorrido = false;
					this.drawRecorrido(this.actualRecorrido.points);
				} else {
					//Dibuja el recorrido
					this.map.addOverlay(this.actualRecorrido);
					
					//Dibuja las flechas				
					var arrows = this.actualRecorrido.arrowsIcons;
					var i = arrows.length;
					while (i--) this.map.addOverlay(arrows[i]);
	
					//Indica que el recorrido esta dibujado
					this.actualRecorrido.drawed = true;
				}
			}
		}

		/**
		 * Borra el recorrido actual
		 */
		this.deleteActualRecorrido = function () {
			if(this.actualRecorrido != null){
				//Borra el recorrido
				this.map.removeOverlay(this.actualRecorrido);

				//Borra las flechas del recorrido
				var arrows = this.actualRecorrido.arrowsIcons;
				var i = arrows.length;
				while (i--)	this.map.removeOverlay(arrows[i]);

				//Indica que el recorrido no esta dibujado
				this.actualRecorrido.drawed = false;
			}
		}
		
		/**
		 * Redibuja un recorrido de micros
		 */
		this.reDrawRecorrido = function() {
			this.deleteActualRecorrido();
			this.drawActualRecorrido();
		}
		
		/**
		 * Remueve un recorrido de la memoria
		 */
		this.removeRecorrido = function () {
			if(this.actualRecorrido != null){
				//Borra el recorrido
				this.map.removeOverlay(this.actualRecorrido);

				//Borra las flechas del recorrido
				var arrows = this.actualRecorrido.arrowsIcons;
				var i = arrows.length;
				while (i--)	{
					this.map.removeOverlay(arrows[i]);
					delete(arrows[i]);
				}
				
				//Elimina el vector de flechas
				delete(this.actualRecorrido.arrowsIcons);

				//Elimino la referencia al objeto actual
				this.actualRecorrido = null;
			}
		}
	}
})();

PorArriba.CallesMapManager.prototype = new PorArriba.CallesMapManager(null);

fileLoaded("CallesMapManager.js");
