var Tesla = {};
Ajax = {};
Tesla.Collection = {};
Tesla.Debug = {};
Tesla.UI = {};

var zoomZ = 10;

function $(ctrlID)
{
	return document.getElementById(ctrlID);
}

function $$( ctrl, attr )
{
	if( ctrl )
	{
		if( ctrl[attr] )
			return ctrl[attr]

		if( ctrl.attributes && ctrl.attributes[attr] )
			return ctrl.attributes[attr].value;
	}
	return null;
}

Function.prototype.GetName = function(obj)
{
	if( this.name )
		return this.name;

	var fn = this.toString();
	
	//Safari ou tipo anonimo
	if( fn == '[function]' )
	{
		var cn = obj.constructor;
		if( cn == String ) this.name = 'String';
		else if( cn == Number ) this.name =  'Number';
		else if( cn == Function ) this.name =  'Function';
		else if( cn == Date ) this.name =  'Date';
		else if( cn == Error ) this.name =  'Error';
		else if( cn == Boolean ) this.name =  'Boolean';
		else if( cn == Array ) this.name =  'Array';
		else this.name =  'Object';

		return this.name;
	}

	var start = fn.indexOf( 'function' ) + 9;
	for(;fn.charAt(start) == ' ';)
		start++;
	var end = start;
	while( fn.charAt(end) != ' ' && fn.charAt(end) != '(' )
		end++;
	this.name = fn.substring( start, end )
	return this.name;
}

Object.prototype.GetType = function()
{
	var self = this;
	if( self.constructor )
		return self.constructor.GetName(self);
	return null;
}


Tesla.Map = {};

Tesla.Map.MapControl = function(divID)
{
	////Fields
	var mapNS = Tesla.Map;
	var _map = new GMap2(document.getElementById(divID));;
	var _controls = {};
	var _markers = [];
	var _mapDiv = document.getElementById(divID);
	var self = this;
	var geoCoder = null;
	var _supressMoveEvent = null;
	var gdir = null;
	var gdirErrors = {};
	gdirErrors[G_GEO_UNKNOWN_ADDRESS] = 'Local não encontrado'; gdirErrors[G_GEO_MISSING_QUERY] = 'Local não encontrado';
	gdirErrors[G_GEO_BAD_REQUEST] = 'Local não encontrado'; gdirErrors[G_GEO_BAD_KEY] = 'Chave de API incorreta';
	gdirErrors[G_GEO_SERVER_ERROR] = 'Erro no servidor de mapas do Google';
	////Fields

	////Constructor code
	GEvent.addListener( _map, 'moveend', function(){ if( self.OnMove && !_supressMoveEvent ) self.OnMove(); _supressMoveEvent = null; } );
	GEvent.addListener( _map, 'click', function(overlay, point){ if( self.OnClick ) self.OnClick( mapNS.Point.FromApiObject(point) ); } );
	GEvent.addListener( _map, 'dblclick', function(overlay, point){ if( self.OnDoubleClick ) self.OnDoubleClick( mapNS.Point.FromApiObject(point) ); } );
	Tesla.Map.CurrentMap = this;
	////Constructor code

	////Internal Events	
	function _onMarkerEvent(map, marker, eventName)
	{
		markerEvent = 'On'+eventName;
		mapEvent = 'OnMarker'+eventName;
		if( marker['__'+markerEvent] )
			marker['__'+markerEvent]();

		var rt;
		if( marker[markerEvent] )
			rt = marker[markerEvent]();
		
		if( (rt === undefined || rt) && map[mapEvent] )
			map[mapEvent](marker);
	}
	////Internal Events

	////Public Methods
	function GetRouteObject()
	{
		var route = gdir.getRoute(0);
		var steps = route.getNumSteps();
		var rtRoute = [];
		for(var i=0; i<steps; i++)
		{
			var step = route.getStep(i);
			rtRoute[i] = {};
			rtRoute[i].Text = step.getDescriptionHtml();
			rtRoute[i].Distance = step.getDistance().html;
		}
		rtRoute.Distance = route.getDistance().html;
		return rtRoute;
	}
	this.SetRoute = function( source, destination, completeCallBack, errorCallBack )
	{
		if( !gdir )
			gdir = new GDirections(_map, null);
        GEvent.addListener(gdir, 'load', function(){completeCallBack( GetRouteObject() );} );
		if( errorCallBack )
			GEvent.addListener(gdir, 'error', function(){ var c = gdir.getStatus().code; errorCallBack(c, (gdirErrors[c]||'Erro desconhecido'));});

		gdir.load( String.Format( 'from:{0} to:{1}', source, destination ), {'locale':'pt_BR', 'getSteps': true} );
    }

	this.SetCenter = function( point, zoom, supressMoveEvent ) { _supressMoveEvent = supressMoveEvent; _map.setCenter( point.ToApiObject(), zoom); }
	this.GetCenter = function(){ return mapNS.Point.FromApiObject( _map.getCenter() ); }
	this.MoveTo = function( point ){ _map.panTo( point.ToApiObject() ); }
	this.SetZoom = function( newZoom, supressMoveEvent ) { _supressMoveEvent = supressMoveEvent; _map.setZoom(newZoom); }
	this.GetZoom = function() { return _map.getZoom(); }
	this.ZoomIn = function(){ this.SetZoom( this.GetZoom()+1 ); }
	this.ZoomOut = function(){ this.SetZoom( this.GetZoom()-1 ); }
	this.CreateMarker = function(point){ return new mapNS.Marker( this, point); }
	this.GetMarkers = function(){ return _markers.Clone(); }
	this.GetMapDiv = function(){ return _mapDiv; }
	this.GetPixelFromPoint = function( point ){ var p1 = _map.fromLatLngToDivPixel(_map.fromContainerPixelToLatLng(new GPoint(0,0),true)); var p2 = _map.fromLatLngToDivPixel( point.ToApiObject() ); return new mapNS.Point( p2.x-p1.x, p2.y-p1.y ); } 
	this.GetRectangle = function(){ return mapNS.Rectangle.FromApiObject( _map.getBounds() ); }
	this.toString = function(){ return "{TeslaMapControl}"; }

	this.SearchGeoCode = function( address, callBack )
	{
		var self = this;
		if( !geoCoder )
			geoCoder = new GClientGeocoder();
		geoCoder.getLocations( address, function(result){ if( result.Status.code != 200 ) callBack( null ); else callBack( LoadPoints(result.Placemark) ); } );
	}
	function LoadPoints( placeMark )
	{
		var rt = [];
		for(var i=0; i<placeMark.length; i++)
		{
			obj = {};
			obj.Address = placeMark[i].address;
	
			var temp = obj.Address.split(',');
			obj.Address = temp[0];
			obj.District = temp[1];
			obj.City = temp[2];
			obj.State = temp[3];

			obj.Point = new Tesla.Map.Point(placeMark[i].Point.coordinates[1], placeMark[i].Point.coordinates[0]);
			rt.push( obj );
		}
		return rt;
	}

	this.CloseToolTips = function()
	{
		if(!_markers)
			return;
		for(var i=0; i<_markers.length; i++)
			_markers[i].HideAllToolTips(true);
	}

	this.AddMarker = function( marker )
	{
		if( marker != null && marker instanceof mapNS.Marker && !marker.ApiObject )
		{
			var self = this;
			marker.Index = _markers.Add(marker);
			var opts;
			marker.ApiObject = new GMarker( marker.GetPoint().ToApiObject(), marker.GetApiOptions() );
			GEvent.addListener( marker.ApiObject, 'click', function(){ _onMarkerEvent(self, marker, 'Click');} );
			GEvent.addListener( marker.ApiObject, 'dblclick', function(){ _onMarkerEvent(self, marker, 'DoubleClick');} );
			GEvent.addListener( marker.ApiObject, 'mouseover', function(){ _onMarkerEvent(self, marker, 'MouseOver');} );
			GEvent.addListener( marker.ApiObject, 'mouseout', function(){ _onMarkerEvent(self, marker, 'MouseOut');} );
			_map.addOverlay( marker.ApiObject );
			if( this.OnMarkerAdded )
				this.OnMarkerAdded( marker );
		}
	}
	this.RemoveMarker = function( marker )
	{
		if( marker != null && marker instanceof mapNS.Marker && marker.ApiObject )
		{
			_markers.Remove( marker );
			_map.removeOverlay( marker.ApiObject );
			marker.ApiObject = null;
			if( this.OnMarkerRemoved )
				this.OnMarkerRemoved( marker );
		}
	}
	this.ClearMarkers = function()
	{
		_markers = [];
		_map.clearOverlays();
		if( this.OnClearMarkers )
			this.OnClearMarkers();
	}

	this.EnableZoomPanel = function() { if( this.IsZoomPanelEnabled() ) return; _controls['ZoomPanel'] = new GSmallMapControl(); _map.addControl( _controls['ZoomPanel'] ); }
	this.DisableZoomPanel = function() { if( !this.IsZoomPanelEnabled() ) return; _map.removeControl( _controls['ZoomPanel'] ); _controls['ZoomPanel'] == null;  }
	this.IsZoomPanelEnabled = function(){ return _controls['ZoomPanel'] != null; };

	this.EnableMapTypesPanel = function() { if( !this.IsMapTypesPanelEnabled() ) _map.addControl( _controls['MapTypesPanel'] = new GMapTypeControl() ); }
	this.DisableMapTypesPanel = function() { if( !this.IsMapTypesPanelEnabled() ) return; _map.removeControl( _controls['MapTypesPanel'] ); _controls['MapTypesPanel'] == null;  }
	this.IsMapTypesPanelEnabled = function(){ return _controls['MapTypesPanel'] != null; };
	////Public Methods
}
Tesla.Map.MapControl.IsBrowserCompatible = function()
{
	return GBrowserIsCompatible();
}
Tesla.Map.MapControl.Unload = function()
{
	GUnload();
}

Tesla.Map.Point = function( latitude, longitude )
{
	///Properties
	this.Latitude = latitude;
	this.Longitude = longitude;
	this.X = latitude;
	this.Y = longitude;
	///Properties

	///Public Methods
	this.ToApiObject = function(){ return new GLatLng( this.Latitude, this.Longitude ); };
	this.toString = function(){ return '(' + this.Latitude + ', ' + this.Longitude + ' )'; };
	this.toLocation = function(){ return this.Latitude + ', ' + this.Longitude; };
	///Public Methods
}
Tesla.Map.Marker = function( map, point, markerIcon )
{
	///Fields
	var _map = map;
	var _models = {};
	var _point = point;
	this.Map = map;
	///Fields

	///Constructor Code
	this.constructor.name = 'Marker';
	if( markerIcon )
		this.Icon = markerIcon;	
	///Constructor Code
	
	///Public Methods / Properties
	this.HighlightToolTip = [];
	this.GetPixelPoint = function(){ return _map.GetPixelFromPoint( this.GetPoint() ); }
	this.GetPoint = function(){ return _point; }
	this.Hide = function(){ this.ApiObject.hide(); }
	this.Show = function(){ this.ApiObject.show(); }
	this.SetImage = function(newImage){ this.Icon.IconImage = newImage; this.ApiObject.setImage(newImage); }
	this.GetApiOptions = function() { if( this.Icon ) return { icon:this.Icon.ToApiObject() }; }
	
	this.SetToolTipModel = function( modelName, modelDivID, alignment, hPadding, vPadding)
	{ 
		_models[modelName] = {};
		_models[modelName].ModelDiv = document.getElementById(modelDivID);
		_models[modelName].Alignment = alignment||Tesla.Map.AutoAlignment.None;
		_models[modelName].HPadding = hPadding||0;
		_models[modelName].VPadding = vPadding||0;
	}
	
	this.ShowToolTip = function( modelName )
	{
		if( !_models[modelName] )
			return null;
			
		if( !_models[modelName].Window )
		{
			_models[modelName].Window = document.createElement("div");
			Object.CopyProperties( _models[modelName].ModelDiv.style, _models[modelName].Window.style );
			this.BindToolTipTemplate(modelName);
		}
			
		var point = _map.GetPixelFromPoint( this.GetPoint() );
		_models[modelName].Window.style.position = 'absolute';
		_models[modelName].Window.style.display = 'block';
		_map.GetMapDiv().appendChild( _models[modelName].Window );
		
		var s = parseInt(_models[modelName].Window.clientWidth);
		_models[modelName].Window.style.top = point.Y - this.ApiObject.getIcon().iconAnchor.y - _models[modelName].Window.clientHeight - (_models[modelName].VPadding||0);
		_models[modelName].Window.style.left = point.X + (_models[modelName].HPadding||0);

		var v = parseInt(_map.GetMapDiv().offsetWidth) - s - parseInt(point.X);
		if( v < 0 && (_models[modelName].Alignment == Tesla.Map.AutoAlignment.Horizontal || _models[modelName].Alignment == Tesla.Map.AutoAlignment.Both) )
			this.__AdjustHorizontal1( _models[modelName].Window, s );
			//_models[modelName].Window.style.left = parseInt(_models[modelName].Window.style.left) - s /*- (parseInt(this.ApiObject.getIcon().iconAnchor.x)/2)*/;
		else if( v < 0 )
			_models[modelName].Window.style.left = -8 + parseInt(_models[modelName].Window.style.left) + v;
	
		if( _models[modelName].Alignment == Tesla.Map.AutoAlignment.Vertical || _models[modelName].Alignment == Tesla.Map.AutoAlignment.Both )
			if( parseInt(_models[modelName].Window.style.top) <= 0 )
				_models[modelName].Window.style.top = parseInt(_models[modelName].Window.style.top) + parseInt(_models[modelName].Window.clientHeight) + this.ApiObject.getIcon().iconAnchor.y + (2*_models[modelName].VPadding) - 2;

		_models[modelName].Window.style.display = 'block';
		if( this.OnShowToolTip )
			this.OnShowToolTip();

		return _models[modelName].Window;
	}
	this.__AdjustHorizontal1 = function(w, s){ w.style.left = parseInt(w.style.left) - s + parseInt(this.ApiObject.getIcon().iconAnchor.x) ; }

	this.BindToolTipTemplate = function(modelName)
	{
		if(_models[modelName].Window)
			_models[modelName].Window.innerHTML = BindTemplate(_models[modelName].ModelDiv.innerHTML, this);
	}
	this.HideAllToolTips = function(remove)
	{
		this._ToolTipWindow = null;
		for( var model in _models )
			this.HideToolTip( model, remove );
	}
	this.HideToolTip = function( modelName, remove )
	{
		this.__RemoveIcon();
			
		if( !_models[modelName].Window )
			return;

		if( this.OnHideToolTip )
			this.OnHideToolTip();
			
		if(remove)
		{
			_map.GetMapDiv().removeChild( _models[modelName].Window );
			_models[modelName].Window = null;
		}
		else
			_models[modelName].Window.style.display = 'none';
	}
	this.__OnMouseOver = function()
	{
		if( this.HighlightToolTip && this.HighlightToolTip.GetType() == 'String' )
		{
			var model = this.HighlightToolTip;
			this.HighlightToolTip = [ model ] ;
		}
		else if( !this.HighlightToolTip || !this.HighlightToolTip.length )
			return;
			
		this.__ClearTimer();
		if( !this.DontBounceIcon )
		{
			this.__OnMouseOut = null;
			this.__ShowIcon();
		}
		else
		{
			this.__OnMouseOut = this._OnMouseOut;
		}
			
		if( _map.CurrentMarker && _map.CurrentMarker != this )
			_map.CurrentMarker.HideAllToolTips();
		
		var self = this;
		_map.CurrentMarker = this;
		for(var i=0; i<this.HighlightToolTip.length; i++)
		{
			this._ToolTipWindow = this.ShowToolTip( this.HighlightToolTip[i] );
			this._ToolTipWindow.onmouseover = function(){ self.__ClearTimer(); };
			this._ToolTipWindow.onmouseout = function(){ self.Timer = setTimeout( 'if(Tesla.Map.CurrentMap.CurrentMarker) Tesla.Map.CurrentMap.CurrentMarker.HideAllToolTips();', 50 ); };
		}
	}
	this._OnMouseOut = function()
	{
		if( !this.HighlightToolTip.length || !_map )
			return true;

		_map.CurrentMarker = this;
		this.Timer = setTimeout( 'Tesla.Map.CurrentMap.CurrentMarker.HideAllToolTips();', 50 );
	}
	this.__ClearTimer = function()
	{
		if( this.Timer )
			clearTimeout(this.Timer);
	}
	this.ShowHighlight = function()
	{
		if( this.HighlightToolTip && this.HighlightToolTip.GetType() == 'String' )
		{
			var model = this.HighlightToolTip;
			this.HighlightToolTip = [ model ] ;
		}

		for(var i=0; i<this.HighlightToolTip.length; i++)
			this.ShowToolTip( this.HighlightToolTip[i] );
	}
	this.HideHighlight = function()
	{
		this.HideAllToolTips(true);
	}
	this.__ShowIcon = function()
	{
		if( !this.Icon )
			return;

		if( this.DImage )
		{
			_map.GetMapDiv().removeChild(this.DImage);
			this.DImage = null;
		}

		var self = this;
		var p = _map.GetPixelFromPoint(this.GetPoint());
		var img = document.createElement("img");
		img.style.cursor = 'hand';
		this.DImage = img;
		img.onmouseout = function(){ self._OnMouseOut(); };
		img.onclick = function(){ if(self.Map.OnMarkerClick) self.Map.OnMarkerClick(self); if(self.OnClick) self.OnClick(); };
		img.src = this.Icon.IconImage;
		img.style.position = 'relative';
		img.style.top = p.Y - this.Icon.IconSize.Height + 5;
		img.style.left = p.X - this.Icon.IconSize.Width - 293;
		img.style.zIndex = 500;
		_map.GetMapDiv().appendChild(img);
	}
	this.__RemoveIcon = function()
	{
		if( this.DImage )
		{
			_map.GetMapDiv().removeChild(this.DImage);
			this.DImage = null;
		}
	}
	///Public Methods
}

//Validadores


Array.prototype.Add = function(obj)
{
	var rt = this.length;
	this[this.length] = obj;
	return rt;
}


