/*
 * MapObject - The JavaScript Mapping Library.
 * http://MapObject.net
 * 
 * Copyright (c) 2006-2007, Mashup Technologies, LLC
 * All rights reserved.
 * http://www.MashupTechnologies.com
 * 
 * The JavaScript code distributed with Google Maps Samples (the "Software") is licensed under the
 * Lesser GNU (LGPL) open source license version 3.
 * 
 * http://www.gnu.org/licenses/lgpl.html
 * 
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 */

/*
 * MapObject - The JavaScript Mapping Library.
 * http://MapObject.net
 * 
 * Copyright (c) 2006-2007, Mashup Technologies, LLC
 * All rights reserved.
 * http://www.MashupTechnologies.com
 * 
 * The JavaScript code distributed with Google Maps Samples (the "Software") is licensed under the
 * Lesser GNU (LGPL) open source license version 3.
 * 
 * http://www.gnu.org/licenses/lgpl.html
 * 
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 */

// Create MapObject namespace
Ext.namespace('MapObject.proxy');

/**
* Google map proxy class.
* @namespace MapObject.widget
* @class StatusBar
* @constructor
* @param {HTMLElement | String} The html element that represents the StatusBar.
* @param {Object} A key-value map of initial config names and values.
	* @cfg {String} TODO.
	* @cfg {Boolean} TODO.
* Notes: See notes.txt
*/
MapObject.proxy.GoogleMap = function (mapEl, opt) {
    MapObject.proxy.GoogleMap.superclass.constructor.call(this);

	this.init(mapEl, opt); 
}

Ext.extend(MapObject.proxy.GoogleMap, MapObject.proxy.Abstract, {

	mapEl: null,
	// Google Map Object
	mapObject: null, 

	// Default zoom, used to set initial map center
	defaultZoom: 14,

	// Map configuration options
	defaultMapType: 'normal',							//Need to match undercase line  711
	// Available map types
	mapTypes: {
		normal: true,
		satellite: true,
		hybrid: true,
		physical: false
	},

	// Map Controls
	mapControls: {
		// Creates a control with buttons to pan in four directions, and zoom in and zoom out.
		smallMapControl: false,
		// 	Creates a control with buttons to pan in four directions, and zoom in and zoom out, and a zoom slider.
		largeMapControl: false,
		// Creates a control with buttons to zoom in and zoom out.
		smallZoomControl: true,
		// Creates a control that displays the map scale.
		scaleControl: false
	},

	// Tooltip HTML element
	tooltip: null,

	//Possible values: Overlay, MarkerManager
	markersEngine: 'Overlay',

	// Storage for markers added to a map
	gmarkers: [],


	/**
	* Registered icons
	* @private
	* @type {Array} array of GIcons
	*/
	iconsSet: [], 

	// Tooltip engines: v1, v2 
	// v2 uses enhanced positioning
	tooltipEngine: 'v1',

	// Active marker
	activeMarker: null, 

	// Active marker icon image URL
	activeMarkerURL: null,

	/* allow maltiple draggable markesr */
	multipleDragableMarkers: true,

	//Storage for selected markers IDs
	selectedMarkersIDs: [],

	// Register events
	init: function(mapEl, config) {
		this.mapEl = document.getElementById(mapEl);
		if (GBrowserIsCompatible()) {

			//First of all, add event to free resources on window unload
			GEvent.addListener(window, 'unload', GUnload);

			// Set options
			if(config && config.activeMarkerURL) {
				this.activeMarkerURL = config.activeMarkerURL;
			}
			if(config && config.tooltipEngine) {
				this.tooltipEngine = config.tooltipEngine;
			}
			if(config && config.mapTypes) {
				this.mapTypes = config.mapTypes;
			}
			if(config && config.mapControls) {
				this.mapControls = config.mapControls;
			}
			if(config && config.defaultMapType) {
				this.defaultMapType = config.defaultMapType;
			}
			if(config && config.icons) {
				this.iconsSet = config.icons;
			}
			
		}
		else {
			alert("Sorry, the Google Maps API is not compatible with this browser");
		}
	},

	loadMap: function()
	{

		// Apply singleton pattern
		if (this.mapObject == null)
		{
			// Map options
			var opt = {}
			opt.mapTypes = []
			if (this.mapTypes.normal) {
				opt.mapTypes.push(G_NORMAL_MAP);
			}
			if (this.mapTypes.satellite) {
				opt.mapTypes.push(G_SATELLITE_MAP);
			}
			if (this.mapTypes.hybrid) {
				opt.mapTypes.push(G_HYBRID_MAP);
			}
			if (this.mapTypes.physical) {
				opt.mapTypes.push(G_PHYSICAL_MAP);
			}

			// Fire beforeload event
	        this.fireEvent("beforeloadmap", this);

			this.mapObject = new GMap2(this.mapEl, opt); 

			//Enable double click
			//this.mapObject.enableDoubleClickZoom();
			//Enable zooming using a mouse's scroll wheel
			this.mapObject.enableScrollWheelZoom();

			// Add controls
			if (this.mapControls.smallMapControl) {
				this.mapObject.addControl(new GSmallMapControl());
			}
			if (this.mapControls.largeMapControl) {
				this.mapObject.addControl(new GLargeMapControl());
			}
			if (this.mapControls.smallZoomControl) {
				this.mapObject.addControl(new GSmallZoomControl());
			}
			if (this.mapControls.scaleControl) {
				this.mapObject.addControl(new GScaleControl());
			}

			if (opt.mapTypes.length > 1) {
				this.mapObject.addControl(new GMapTypeControl());
			}
			
			// Fire beforeload event
	        this.fireEvent("afterloadmap", this);
		}
	},

	// Set map center
	setCenter: function(lat, lon, zoom, type)
	{
		var point = new GLatLng(lat, lon);
		this.mapObject.setCenter(point, zoom || this.defaultZoom, type || this.getDefaultMapType());
	},

	// Add overlay to a map
	addOverlay: function(lat, lon, markerOptions)
	{
		var point = new GLatLng(lat, lon);
		//Check if the map was initialized by setCenter() since it was created.
		if (!this.mapObject.isLoaded()) {
			this.mapObject.setCenter(point, this.defaultZoom);
		}
		var marker = new GMarker(point, markerOptions);
		this.mapObject.addOverlay(marker);
		return marker;
	},

	// Remove overlay from a map
	removeOverlay: function(overlay) {
		this.mapObject.removeOverlay(overlay);
	},
	// return zoom level
	getZoom: function()
	{
		return this.mapObject.getZoom();
	},

	// clearOverlays
	clearOverlays: function()
	{
		this.mapObject.clearOverlays();
	},

	/**
	* Register map click event
	* @param	none
	*/
	registerMapClick: function() 
	{
		GEvent.bind(this.mapObject, "click", this, function(overlay, point) {
			// Add dragable marker
			this.addDragableMarker(point);

			// Run callback method
			this.onMapClick(point.lat(), point.lng());
		});
	},

	/**
	* Add dragable marker to a map
	* @param	none
	*/
	addDragableMarker: function(point) 
	{
		if (!this.multipleDragableMarkers)  {
			this.mapObject.clearOverlays();
		}

		var marker = new GMarker(point, {draggable: true});
		this.mapObject.addOverlay(marker);
			
		GEvent.bind(marker, "dragend", this, function() {
			// Run callback method
			var point = marker.getPoint();
			this.onMapClick(point.lat(), point.lng());
		});
	},

	// Add dragable overlay to a map
	addDragableOverlay: function(lat, lon)
	{
		var point = new GLatLng(lat, lon);
		//Check if the map was initialized by setCenter() since it was created.
		if (!this.mapObject.isLoaded()) {
			this.mapObject.setCenter(point, this.defaultZoom);
		}
		this.addDragableMarker(point);
	},

	// Return tooltip html element
	getTooltip: function() {
		if (!this.tooltip)
		{
			// set up marker mouseover tooltip div
			this.tooltip = document.createElement("div");
			/* element.setAttribute("class", "somename") works in Firefox and Safari, but not IE. 
			* The gotca is that IE requires element.setAttribute("className", "somename").
			* element.className = "somename"; which works in all browsers.
			* 
			* this.tooltip.setAttribute('className', 'tooltip');
			* this.tooltip.setAttribute('class', 'tooltip');
			*/
			this.tooltip.className =  "tooltip";

			this.mapObject.getPane(G_MAP_FLOAT_PANE).appendChild(this.tooltip);
			this.tooltip.style.visibility="hidden";
		}
		return this.tooltip;
	},


	// A function to create the marker and set up the event window
	addMarker: function(id, lat, lng, icon) {
		
		lat = parseFloat(lat);
		lng = parseFloat(lng);
		point = new GLatLng(lat,lng);

		// Standard icon
		var markerOptions = {}; 
		// Set icon
		try 
		{
			if (!icon) {
				markerOptions.icon = new GIcon(G_DEFAULT_ICON); 
			}
			else {
				markerOptions.icon = icon; 
			}
		}
		catch (e)
		{
			// In case of error set default icon
			markerOptions.icon = new GIcon(G_DEFAULT_ICON); 
		}

		// Instatiate new Gmarker
		var marker = new GMarker(point, markerOptions);

		// Set marker id
		marker.id = id;
		
		GEvent.bind(marker, "click", this, function() {
			this.onMarkerClick(marker);

		});

		this.gmarkers[id] = marker;

		
		// Add overley to a marker
		//Do we use Marker Manager 
		if (this.markersEngine != 'MarkerManager')
		{
			try {
				this.mapObject.addOverlay(marker);
			} catch (e){
				alert (e.message)
			}
		}

		// *********** The new marker "mouseover" and "mouseout" listeners ***********
		GEvent.bind(marker,"mouseover", this, function() {
			this.showTooltip(marker);
			
			// Change active sidebar row background color
			this.onMarkerMouseOver(marker);
					
			// Change marker icon
			this.setActiveMarkerIcon(marker, true);
		});
		   
		GEvent.bind(marker,"mouseout", this, function() {
			this.tooltip.style.visibility="hidden";

			this.onMarkerMouseOut(marker.id);

			// Change marker icon
			this.setActiveMarkerIcon(marker, false);
		});

		// Finally return marker
		return marker; 
	},

	// Return marker by ID
	getMarker: function(id) {
		return this.gmarkers[id];
	},

	// This functions used to pan map to a givem marker
	panTo: function(marker)
	{
		var latLongToCheck = marker.getPoint();
		// Do not recenter if marker is visible
		if (!this.mapObject.getBounds().contains(latLongToCheck))
		{
			// maybe use panTo ?
			this.mapObject.panTo(latLongToCheck);
		}
	},


	// Register an active marker
	setActiveMarker:  function(marker) {
		// Restore icon of previos active marker
		if (this.activeMarker) {
			this.activeMarker.setImage(this.activeMarker.getIcon().image);
		}

		// Register active marker
		this.activeMarker = marker;
	},

	// Set an icon for active marker
	setActiveMarkerIcon:  function(marker, active) {

		this.setSelectedMarkers();

		if (!marker) {
			return;
		}

		if (this.activeMarker && (this.activeMarker.id == marker.id)) {
			return;
		}

		// Change marker icon
		if (active) {

			if (this.activeMarkerURL !== null) {
				marker.setImage(this.activeMarkerURL);
			}
			else {
				// use activeImage property of the GIcon
				var activeImage = marker.getIcon().activeImage || null;
				if (activeImage !== null) {
					marker.setImage(activeImage);
				}
			}
		}
		else {
			// Check if row is already selected
			var alreadySelected = this.isMarkerIDSelected(marker.id);
			if (!alreadySelected) {
				marker.setImage(marker.getIcon().image);
			}
		}

	},

	// set selected markers
	setSelectedMarkers: function(selectedIDs) {
		// Set selected markers
		if (Ext.type(selectedIDs) == 'array') {
			this.selectedMarkersIDs = selectedIDs;
		}

		// Remove active icons
		this.clearSelectedMarkerIcons();

		// Process selected records
		if (this.selectedMarkersIDs.length > 0) {
			for (var i = 0, len = this.selectedMarkersIDs.length; i < len; i++){
				var marker = this.getMarker(this.selectedMarkersIDs[i]);

				if (this.activeMarkerURL !== null) {
					marker.setImage(this.activeMarkerURL);
				}
				else {
					// use activeImage property of the GIcon
					var activeImage = marker.getIcon().activeImage || null;
					if (activeImage !== null) {
						marker.setImage(activeImage);
					}
				}

			}
		}

	},

	// Remove active icons
	clearSelectedMarkerIcons: function() {
		for(var key in this.gmarkers){
			if(typeof this.gmarkers[key] != "function"){
				var marker = this.gmarkers[key];
				marker.setImage(marker.getIcon().image);
			}
		}
	},
	// Check if markers is selected
	isMarkerIDSelected: function(id) {
		var selected = false;
		if (this.selectedMarkersIDs.length > 0) {
			for (var i = 0, len = this.selectedMarkersIDs.length; i < len; i++){
				if (this.selectedMarkersIDs[i] == id) {
					selected = true;
					break;
				}
			}
		}
		return selected;
	},

	// ***************** This function displays the tooltip *****************
	// it can be called from an icon mousover or a sideBar mouseover
	showTooltip: function(marker) {
		// marker.id - Active record id
				
		var tooltip = this.getTooltip();
		//Remove previous tooltip
		try
		{
			//tooltip.removeChild(this.tooltipTable); 
		}
		catch(e) {}

		tooltip.innerHTML = this.getToolTipText(marker.id);

		if (this.tooltipEngine == "v2")
		{
			var point = this.mapObject.getCurrentMapType().getProjection().fromLatLngToPixel(this.mapObject.fromDivPixelToLatLng(new GPoint(0,0),true),this.mapObject.getZoom());
			var offset = this.mapObject.getCurrentMapType().getProjection().fromLatLngToPixel(marker.getPoint(),this.mapObject.getZoom());
			var anchor = marker.getIcon().iconAnchor;
			var iconW = marker.getIcon().iconSize.width;
			var iconH = marker.getIcon().iconSize.height;

			// Get tooltip width using PrototypeLib
			/* Prototype code is deprecated 
			* var oTTDim = Element.getDimensions(tooltip);
			*/
			var el = Ext.get(tooltip);
			var oTTDim = {
				height: el.getHeight(),
				width: 	el.getWidth()
			};
			// -- or --
			// use simple DOM methods
			/*
			var oTTDim = {}
			oTTDim.width = tooltip.clientWidth; 
			oTTDim.height = tooltip.clientHeight; 
			*/

			// Map size in pixels
			// Useless var oMapSize = this.mapObject.getSize();
			var maxOffset_NE = this.mapObject.getCurrentMapType().getProjection().fromLatLngToPixel(this.mapObject.getBounds().getNorthEast(), this.mapObject.getZoom());
			var maxOffset_SW = this.mapObject.getCurrentMapType().getProjection().fromLatLngToPixel(this.mapObject.getBounds().getSouthWest(), this.mapObject.getZoom());
			var ttOffset = this.mapObject.getCurrentMapType().getProjection().fromLatLngToPixel(this.mapObject.fromDivPixelToLatLng(new GPoint(oTTDim.width, oTTDim.height),true),this.mapObject.getZoom());

			if (this.debug) 
			{
				var logMsg = "oTTDim.height: " + oTTDim.height;
				logMsg +=  " maxOffset_NE.x:  " +  maxOffset_NE.x + " maxOffset_NE.y: " + maxOffset_NE.y;
				logMsg +=  " maxOffset_SW.x:  " +  maxOffset_SW.x + " maxOffset_SW.y: " + maxOffset_SW.y;
				logMsg +=  " offset.y: " + offset.y + " point.y: " + point.y + " anchor.y: " + anchor.y;

				logMsg +=  " COND X: " + (maxOffset_NE.x - offset.x ) + " < " + oTTDim.width;
			}

			if (maxOffset_NE.x - offset.x  <  oTTDim.width)
			{
				// Display left side tooltip
				posX = offset.x - point.x - anchor.x - oTTDim.width;
			}
			else
			{
				// Display right side tooltip
				posX = offset.x - point.x - anchor.x + iconW;
			}

			// Check top position
			if (this.debug) logMsg +=  " COND Y: " + (offset.y - maxOffset_NE.y - oTTDim.height) + "< 0";

			// offset.y - maxOffset_NE.y === actual Y marker's coordinate relative to the NORTH map border 
			if (offset.y - maxOffset_NE.y - oTTDim.height < 0)
			{
				posY = offset.y - point.y - anchor.y /* + iconH */;
			}
			else
			{
				posY = offset.y - point.y - anchor.y /* - iconH */ - oTTDim.height;
			}

			var pos  =  new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(posX, posY)); 
			if (this.debug) this.LogWriter.log(logMsg, "info");

			pos.apply(tooltip);
		}		
		else 
		// Use default engine
		{
			var point=this.mapObject.getCurrentMapType().getProjection().fromLatLngToPixel(this.mapObject.fromDivPixelToLatLng(new GPoint(0,0),true),this.mapObject.getZoom());
			var offset=this.mapObject.getCurrentMapType().getProjection().fromLatLngToPixel(marker.getPoint(),this.mapObject.getZoom());
			var anchor=marker.getIcon().iconAnchor;
			var width=marker.getIcon().iconSize.width;
			var height=tooltip.clientHeight;
			var pos = new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(offset.x - point.x - anchor.x + width, offset.y - point.y -anchor.y -height)); 
			pos.apply(tooltip);
		}

		tooltip.style.visibility = "visible";

	},



	/**
	* build GIcons and fill iconsSet property
	* @param {Array} icons definition
	* @return none
	*/
	buildIcons: function(icons){

		var appIcon = null;
		var name = null;
		var x = null;
		var y = null;
		for (var i = 0; i < icons.length ; i++)
		{

			// Required: icon name
			name = icons[i].name;		

			// Test Variable
			x = "" ;				
			// Test Variable
			y = "" ;				

			// Check for an icon copy
			x = icons[i].copy;

			// Make sure icon to copy has been built
			if (x && gicons[x])			
			{
				appIcon = new GIcon(gicons[x]);
			} 
			else
			{
				appIcon = new GIcon();
			}

			// Required: Icon Image
			appIcon.image = icons[i].image.src;	

			x = "" ;
			y = "" ;
			// Check for an icon anchor
			x = icons[i].xanchor;
			y = icons[i].yanchor;

			if (x != null && y!= null && x && y)
			{
				appIcon.iconAnchor = new GPoint(parseInt(x),parseInt(y));
			}

			x = "" ;
			y = "" ;

			// Check for an icon infowindow anchor
			x = icons[i].xiwanchor;
			y = icons[i].yiwanchor;

			if (x != null && y!= null && x && y)
			{
				appIcon.infoWindowAnchor = new GPoint(parseInt(x),parseInt(y));
			}

			x = "" ;
			y = "" ;
			// Check for icon image height and width
			x = icons[i].image.width;
			y = icons[i].image.height;

			if (x != null && y!= null && x && y)
			{
				appIcon.iconSize = new GSize(parseInt(x),parseInt(y));
			}

			x = "" ;
			// Check for an shadow image
			x = icons[i].shadow.src;

			if (x != null && x)
			{
				appIcon.shadow = x ;
			}

			x = "" ;
			y = "" ;
			// Check for an icon shadow dimension
			x = icons[i].shadow.width;
			y = icons[i].shadow.height;

			if (x != null && y!= null && x && y)
			{
				appIcon.shadowSize = new GSize(parseInt(x),parseInt(y));
			}
      
			this.iconsSet[name] = appIcon;
		}
	},

	// Get map type
	getDefaultMapType: function() 
	{
		switch(this.defaultMapType)
		{
			case 'Normal':
				return G_NORMAL_MAP;
				break;
			case 'Satellite':
				return G_SATELLITE_MAP;
				break;
			case 'Hybrid':
				return G_HYBRID_MAP;
				break;
			case 'Physical':
				return G_PHYSICAL_MAP;
				break;
			default:
				/* Use default map type, do not throw exception
				* throw (this.DefaultMapType + " map type is not supported.");
				*/
				return G_NORMAL_MAP;
		}
	},

	// Return map proxy object
	getMapProxy: function() {
		return this;
	},

	//////////////////////////////////////////////////
	// Geocode hook
	onGeocode: function(lat, lon, accuracy){},
	// Map click hook
	onMapClick: function(lat, lon){},
	// Zoom in hook
	onZoomEnd: function(oldzoom, newzoom){},
	// Hook to get tooltip text
	getToolTipText: function(id){},
	// Marker's on mouse out hook
	onMarkerMouseOut: function(marker){},
	// Marker's on mouse over hook
	onMarkerMouseOver: function(marker){},
	// Marker's on click hook
	onMarkerClick: function(marker){},

	// saved from extra commas after the last method
	EOF:null
});
