var MooMap = new Class({
	
	Implements: [Options, Events],
	
	options:{
		onMarkClick: 	$empty,
		onMarkHover: 	$empty,
		onMarkOut:		$empty,
		onMarkAdd:		$empty,
		streetViewError: $empty,
		onMarkerDragEnd : $empty,
		mapCanvas : 	"map",
		mapMarkers: 	[],
		keyDrag:		false,
		mapOptions: {
			zoom		: 13,
			center		: "Valencia",
			//mapTypeId	: google.maps.MapTypeId.ROADMAP,
			scrollwheel	: false
		}
		
	},
	
	mapCanvas: undefined,
	map: undefined,
	geocoder:undefined,
	directionsDisplay: undefined,
	directionsService: undefined,
	markers:{},
	
	initialize: function(container, center, options)
	{
		this.setOptions(options);
		this.geocoder = new GClientGeocoder();
		this.mapCanvas = $(container);
		this.load();
		//this.map.setCenter(new GLatLng(37.4419, -122.1419), 13);
		if ($defined(center)) 
		{
			this.center(center);
		}
		
		if(this.options.mapMarkers.length > 0) this.setMarkers(this.options.mapMarkers);
		
	},
	
	load: function()
	{
		this.map = new GMap2(this.mapCanvas, this.options.mapOptions);
		this.map.addControl(new GSmallMapControl());
		if(this.options.keyDrag) this.map.enableKeyDragZoom();
	},
	
	
	/**** PUBLIC ****/
	
	streetView: function(element, point)
	{
		var panoramaOptions = { latlng:point };
      	this.streetViewPanorama = new GStreetviewPanorama(element, panoramaOptions);
		
		GEvent.addListener(this.streetViewPanorama, "error", function(errorCode){
			this.fireEvent("streetViewError", [errorCode, element]);
		}.bind(this));
		
	},
	
	hideMarkers: function()
	{
		$H(this.markers).each(function(marker){
			marker.hide();
		});
	},
	
	hideMarker: function(id)
	{
		if(!$defined(this.markers[id]))
		{
			return false;
		}
		
		this.markers[id].hide();
	},
	
	/*clearRoutes: function()
	{
		if($defined(this.directionsDisplay)) this.directionsDisplay.setMap(null);
	},*/
	
	setMarkers: function(markers)
	{
		markers.each(function(markerHash){
			try {
				this.addMarker(markerHash, markers.length)
			}
			catch(exception)
			{
				console.log(exception);
			}
		}.bind(this))
	},
	
	zoomToMarkers: function()
	{
		var GLatLngBound = new GLatLngBounds()
		$H(this.markers).each(function(marker){
			if(!marker.isHidden())
			{
				GLatLngBound.extend(marker.getLatLng())
			}
		});
		var zoom = this.map.getBoundsZoomLevel(GLatLngBound)
		var center = GLatLngBound.getCenter();
		
		this.map.setCenter(center);
		this.map.setZoom(zoom);		
	},
	
	center: function(address)
	{
		
		if($type(address) == "string")
		{
			this.centerByAddress(address);
		}
		else
		{
			this.map.setCenter(address, this.options.mapOptions.zoom);
		}
		
	},
	
	centerByAddress: function(address)
	{
		this.geocoder.getLocations(address, 

		function(response)
		{
		     if (response.Status.code == 620) {
		     } else {
		       if (response.Status.code == 200) {
		         place = response.Placemark[0];
		         point = new GLatLng(place.Point.coordinates[1], place.Point.coordinates[0]);
				this.map.setCenter(point, this.options.mapOptions.zoom);
		       }
		     }

		}.bind(this));
	},
	
	addMarker: function(markerHash, num)
	{
		if($defined(this.markers[markerHash.id]))
		{
			this.markers[markerHash.id].show();
			return;
		}
		
		if($defined(markerHash.gps))
		{
			this.addMarkersByLatLng(markerHash);
			return;
		}
		
		
		this.geocoder.getLocations(markerHash.address, 
			function(response) 
			{
		       if (response.Status.code == 200) {
			   	
					place = response.Placemark[0];
				  	point = new GLatLng(place.Point.coordinates[1],place.Point.coordinates[0]);
						
				  	var draggable = false;
				  	
				  	if($defined(markerHash.draggable))
				  	{
				  		draggable = markerHash.draggable;
				  	}
				  	
					var icon = new GIcon(G_DEFAULT_ICON);
					var marker = new GMarker(point, {draggable: draggable, icon: icon});
					
					if(draggable)
					{
						GEvent.addListener(marker, "dragend", function() {
							this.fireEvent("onMarkerDragEnd", [marker]);
					    }.bind(this));
					}
		
					this.map.addOverlay(marker);
					
					this.fireEvent("onMarkerAdd", [marker]);
					
					this.markers[markerHash.id] = marker;				 
									 
	        		this.addMarkEvents(marker, markerHash);
		     }
	        	
	        }.bind(this));
	},
	
	addMarkersByLatLng: function(markerHash)
	{
		var icon = new GIcon(G_DEFAULT_ICON);
		
		point = new GLatLng(markerHash.gps.lat, markerHash.gps.lng);
		
		var draggable = false;
	  	
	  	if($defined(markerHash.draggable))
	  	{
	  		draggable = markerHash.draggable;
	  	}
		
		if($defined(markerHash.clickable))
	  	{
	  		clickable = markerHash.draggable;
	  	}
	  	
		var icon = new GIcon(G_DEFAULT_ICON);
		var marker = new GMarker(point, {draggable: draggable, icon: icon});
		
		if(draggable)
		{
			GEvent.addListener(marker, "dragend", function() {
				this.fireEvent("onMarkerDragEnd", [marker]);
		    }.bind(this));
		}
		
		this.map.addOverlay(marker);
		this.fireEvent("onMarkerAdd", [marker]);
		
		this.markers[markerHash.id] = marker;
		this.addMarkEvents(marker, markerHash);
	},
	
	addMarkEvents: function(marker, markerHash)
	{
		GEvent.addListener(marker, 'click', function() {
		    this.fireEvent("onMarkClick", [marker, markerHash]);
		 }.bind(this));
		
		GEvent.addListener(marker, 'mouseover', function() {
			this.fireEvent("onMarkHover", [markerHash]);
		 }.bind(this));
		
		GEvent.addListener(marker, 'mouseout', function() {
			this.fireEvent("onMarkOut", [markerHash]);
		 }.bind(this));
		
		GEvent.addListener(marker, 'visible_changed', function() {
		 }.bind(this));
	},
	
	calcRoute: function(start, end, mode, onComplete)
	{
		if(!$defined(mode)) mode = google.maps.DirectionsTravelMode.DRIVING;
		
		if(!$defined(this.directionsDisplay))
		{
			this.directionsService = new google.maps.DirectionsService();
			this.directionsDisplay = new google.maps.DirectionsRenderer();
			this.directionsDisplay.setMap(this.map);
		}
		
		
		var request = {
						origin:start, 
						destination:end,
						travelMode: mode
			  		};
		
		this.directionsService.route(request, function(result, status) {
			if (status == google.maps.DirectionsStatus.OK) {
				this.directionsDisplay.setDirections(result);
				onComplete();
			}
		}.bind(this));
	}
});