/**
 * 	Integrated Google Maps/jQuery UI code for d3mls.
 * 	@author D3
 *	@version 1.1
 *	@date 2009-12-09
 *	@requires jQuery and Google Maps API
 */
var d3mls_maps = 
{
	/**
	 * Google Map properties
	 */
	googleMaps : {
		map : null,													//Main map, in the d3mls templates
		mapNode : null,												//	- main map dom node
		mapNodeId : "gMap",											//	- main map dom node id
		dialogMap : {												//Second map, in the jQuery dialog
			map : null,												//	- GMap2 object
			gMarker : null,											//  - dialog map must have only one marker
			domNode : null,											//	- dialog map's dom node
			domNodeId : "gMapDialog"								//	- dialog map's dom node id
		},
		clickedMarker : {											//Object holding the clicked marker on the main map and its references
			gMarker : null,											//	- reference to the selected, clicked GMarker object
			gOverlay: null,											//	- reference to the clicked marker's GOverlay parent
			icon : null,											//	- reference to the clicked marker's icon
			balloonHtml: null										//	- reference to the selected GMarker's balloon content
		},
		gDirectionsObj : null,
		gDirectionsNodeId : "gDirectionsNodeId",
		gDirectionsInputAddressId : "gDirectionsInputAddress",
		gStreetviewClientObj : null,
		gStreetviewPanoramaObj : null,
		gStreetviewPanoramaNodeId : "gStreetviewPanoramaNodeId",
		gLatLngArray : [],											//Array to hold gLatLng objects for bounding
		gMarkerTemplate : "<div class='d3mls_maps_balloon'><div class='d3mls_maps_balloon_image'><img src='{$image}' alt='Image' /></div><div class='d3mls_maps_balloon_content'><p><strong>{$address}</strong><br /> {$city}, {$state}<br /><strong>Price:</strong> {$price}<br /><strong>Beds:</strong> {$bedrooms} <strong>Baths:</strong> {$bathrooms_full}</p><p>{$description}</p></div><div class='d3mls_maps_balloon_links'><span id='showDialogLink'><a href='javascript:void(0)' onclick='d3mls_maps.showDialog(true)'>Streetview & Directions</a></span>&nbsp;&nbsp;&nbsp;<span><a href='{$system_id}'>More Details</a></span></div><div class='clearfix'></div></div>"	
	},
	/**
	 * Data from the d3mls_maps table and class. Set properties in the d3mls_maps class and pass into this.init method as a json-encoded object
	 */
	d3mlsMaps : {
		width : null,				// Width of the map in the d3mls template 
		height : null,				// Height of the map oi the d3mls template
		map_type : null,			// Normal, satellite, physical, or hybrid
		zoom : null,				// Default zoom to use in the absence of markers
		lat : null,					// Default latitude to use in the absence of markers
		lon : null,					// Default longitude to use in the absence of markers
		directions : null,			// Show (1) directions dialog or (0) not
		street_view : null,			// Show (1) streetview dialog or (0) not
		markers : null,				// Array of property listings data as well as coordinates and flag colors
		detailsPage : null,			// The page where d3mls listings are detailed
		markerClickAction : null,	// What to do when marker is clicked - open infowindow or show dialog
		dialogNodeId : null,		// Id of the node holding the jQueryUI dialog
		tabsNodeId : null,			// Id of the node holding the jQueryUI tabs
		dialogObj : null,			// jQuery Object holding the jQueryUI dialog widget
		tabsObj : null,				// jQuery Object holding the jQueryUI tabs widget
		balloonImgResizer : "/app/modules/d3mls/ninja_code/ninja_resize.php?h=100&w=120"
	},
	/**
	 *	Initialization
	 *	On window load, 
	 *		1. Load properties for this map from the d3mls_maps table 
	 *		2. Initialize jQuery dialogs
	 *		3. Load the Google Ajax API for Maps 2.x 
	 *		4. Load the map and markers
	 * 	@param {Object} mapData Map of d3mls_maps data and markers
	 */
	init : function(/* Object */ mapData)
	{
		// Load all d3mls_maps table data and parameters
		for (var propKey in mapData)
			eval("("+ "this.d3mlsMaps." + propKey + " = '" + mapData[propKey] + "')");
		this.d3mlsMaps.markers = eval("("+this.d3mlsMaps.markers+")");
		//console.log(this.d3mlsMaps.markers);
		// Initialize the dialogs
		this._initjQueryUI();
		// Load the maps api and when loaded, create the map and markers and display it
		this._initGoogle();
		//set up the map
		this._setUpMap();
	},
	/**
	 *	Set up the dialog for the clicked marker, containing map, streetview, directions. Called from d3mls_maps.init()
	 *	@see d3mls_maps#init
	 */
	_initjQueryUI : function()
	{
		//set up the dialog
		jQuery("#"+d3mls_maps.d3mlsMaps.dialogNodeId).dialog({
			autoOpen : false,
			width : parseInt(jQuery("#"+d3mls_maps.d3mlsMaps.dialogNodeId).width()),
			height : parseInt(jQuery("#"+d3mls_maps.d3mlsMaps.dialogNodeId).height()),
			open : function()
			{
				jQuery(this).dialog('option', 'title', "Google Map for " + jQuery(this).attr("title"));
				d3mls_maps.googleMaps.dialogMap.map.checkResize();
				d3mls_maps.googleMaps.gStreetviewPanoramaObj.checkResize();
			},
			close : function(){
				d3mls_maps.googleMaps.dialogMap.map.clearOverlays();
				jQuery(d3mls_maps.d3mlsMaps.tabsObj).tabs('select', 0); // on close, switch to first tab so that the dialog map will redraw
			},
			resize : function()
			{
				var dialogWidth = jQuery(".d3mls_maps_dialog").width();
				var dialogHeight = jQuery(".d3mls_maps_dialog").height();
				var tabsWidth = dialogWidth - 20;
				var tabsHeight = dialogHeight - 40;
				var tabPanelWidth = tabsWidth - 20;
				var tabPanelHeight = tabsHeight - 40;
				var tabPanelItemWidth = tabPanelWidth - 20;
				var tabPanelItemHeight = tabPanelHeight - 40;
				
				jQuery(".d3mls_maps_dialog_tabs").css({ 'width': tabsWidth, 'height': tabsHeight });
				jQuery(".d3mls_maps_dialog_tabpanel").css({ 'width': tabPanelWidth, 'height': tabPanelHeight });
				jQuery(".d3mls_maps_dialog_tabpanel_item").css({ 'width': tabPanelItemWidth, 'height': tabPanelItemHeight });
				d3mls_maps.googleMaps.dialogMap.map.checkResize();
				d3mls_maps.googleMaps.gStreetviewPanoramaObj.checkResize();
			}
		});
		//set up the tabs
		this.d3mlsMaps.tabsObj = jQuery("#"+d3mls_maps.d3mlsMaps.tabsNodeId).tabs();
	},
	/**
	 *	Once the Maps API is loaded, instantiate the map and associated map objects. Called from d3mls_maps.init()
	 *	@see d3mls_maps#init
	 */
	_initGoogle : function()
	{
		//Set up map in the d3mls template
		this.googleMaps.mapNode = document.getElementById(this.googleMaps.mapNodeId);
    	this.googleMaps.map = new google.maps.Map2(this.googleMaps.mapNode);
		this.googleMaps.map.setUIToDefault();
		this.googleMaps.map.setMapType(eval(this.d3mlsMaps.map_type));
		//Set up map in the dialog
		this.googleMaps.dialogMap.domNode = document.getElementById(this.googleMaps.dialogMap.domNodeId);
    	this.googleMaps.dialogMap.map = new google.maps.Map2(this.googleMaps.dialogMap.domNode);
		this.googleMaps.dialogMap.map.setUIToDefault();
		this.googleMaps.dialogMap.map.setMapType(eval(this.d3mlsMaps.map_type));
		//Set up google maps API features
		this.googleMaps.gDirectionsObj = new GDirections(((this.d3mlsMaps.markerClickAction == "infowindow") ? this.googleMaps.map : this.googleMaps.dialogMap.map), 
			document.getElementById(this.googleMaps.gDirectionsNodeId));
		this.googleMaps.gStreetviewClientObj = new GStreetviewClient();
		this.googleMaps.gStreetviewPanoramaObj = 
			new GStreetviewPanorama(document.getElementById(this.googleMaps.gStreetviewPanoramaNodeId));
	},
	/**
	 *	Set up the map. Invoked at the end of d3mls_maps._initGoogle()
	 *	@see d3mls_maps#_initGoogle
	 */
	_setUpMap : function() 
	{
		//if no markers, set default point and zoom
		if (this.d3mlsMaps.markers.length == "undefined" || this.d3mlsMaps.markers.length == 0)
			this.googleMaps.map.setCenter(new GLatLng(this.d3mlsMaps.lat, this.d3mlsMaps.lon), this.d3mlsMaps.zoom);
		else 
		{
			this._setMarkers();
			this._setBounds();
		}
	},
	/**
	 *	Set up the markers, infowindow content and add markers to the map. Invoked in the else body of d3mls_maps._setUpMap()
	 *	@see d3mls_maps#_setUpMap
	 */
	_setMarkers : function()
	{
		for (var i=0; i < this.d3mlsMaps.markers.length; i++)
		{
			//create the gLatLng object with marker coordinates
			var point = new GLatLng(this.d3mlsMaps.markers[i].lat, this.d3mlsMaps.markers[i].lon);
			//add it to the gLatLngArray for bounding
			this.googleMaps.gLatLngArray.push(point);
			//icon for marker
			var markerIcon = this._createMarkerIcon({width: 24, height: 24, primaryColor: this.d3mlsMaps.markers[i].color});
			//Array of properties when user clicks on a marker
			var eventProperties = {
				gMarker : new GMarker(point, {	icon : markerIcon	} ),
				icon : markerIcon,
				dialogTitle : this.d3mlsMaps.markers[i].address + ", " + this.d3mlsMaps.markers[i].city + ", " + this.d3mlsMaps.markers[i].state,
				balloonHtml : this.googleMaps.gMarkerTemplate
			};
			//foreach marker property, replace its corresponding placeholder tag in the template
			for (var markerProp in this.d3mlsMaps.markers[i])
			{
				if (markerProp == "image" && this.d3mlsMaps.markers[i]["system"]!=null)
					eventProperties.balloonHtml = eventProperties.balloonHtml.replace("{$image}", this.d3mlsMaps.balloonImgResizer + "&s=" + this.d3mlsMaps.markers[i].system + 
						"&img="+ this.d3mlsMaps.markers[i].image);
				else if (markerProp == "image")
					eventProperties.balloonHtml = eventProperties.balloonHtml.replace("{$image}", this.d3mlsMaps.balloonImgResizer + "&img="+ this.d3mlsMaps.markers[i].image);
				else if (markerProp == "system_id")
					eventProperties.balloonHtml = eventProperties.balloonHtml.replace("{$"+markerProp+"}", this.d3mlsMaps.detailsPage + this.d3mlsMaps.markers[i][markerProp]);
				else
					eventProperties.balloonHtml = eventProperties.balloonHtml.replace("{$"+markerProp+"}", this.d3mlsMaps.markers[i][markerProp]);
			}
			//(re)create temporary closure for onclick event
			function addEvent(eventProperties) 
			{
				GEvent.addListener(eventProperties.gMarker, "click", 
					function(overlay) 
					{
						//onclick, set the d3mls_maps object's clickedMarker property to contain this marker's references
						d3mls_maps.googleMaps.clickedMarker.gOverlay = overlay;
						d3mls_maps.googleMaps.clickedMarker.gMarker = this;
						d3mls_maps.googleMaps.clickedMarker.icon = eventProperties.icon;
						d3mls_maps.googleMaps.clickedMarker.balloonHtml = eventProperties.balloonHtml;
						//set the title for the dialog
						jQuery("#"+d3mls_maps.d3mlsMaps.dialogNodeId).attr("title", eventProperties.dialogTitle);
						//decide whether to open the dialog or to open an infowindow balloon
						if (d3mls_maps.d3mlsMaps.markerClickAction == "infowindow")
							this.openInfoWindowHtml(eventProperties.balloonHtml);
						else if (d3mls_maps.d3mlsMaps.markerClickAction == "dialog")
							d3mls_maps._setDialogMap();
						//load the street view, if available
						d3mls_maps.googleMaps.gStreetviewClientObj.getNearestPanorama(overlay, d3mls_maps._showStreetviewData);
					});
			}
			//add the event properties
			addEvent(eventProperties);
			//add marker to map
			this.googleMaps.map.addOverlay(eventProperties.gMarker);
		}
		
	}, 
	/**
	 *	Get icon from chart.apis.google.com
	 *	@author Pamela Fox http://code.google.com/p/gmaps-utility-library/
	 *	@return	GIcon	icon
	 */
	_createMarkerIcon : function(opts)
	{
		var width = opts.width || 32;
		var height = opts.height || 32;
		var primaryColor = opts.primaryColor || "#ff0000";
		var strokeColor = opts.strokeColor || "#000000";
		var cornerColor = opts.cornerColor || "#ffffff";
		
		var baseUrl = "http://chart.apis.google.com/chart?cht=mm";
		var iconUrl = baseUrl + "&chs=" + width + "x" + height + 
			"&chco=" + cornerColor.replace("#", "") + "," + primaryColor.replace("#", "") + "," + strokeColor.replace("#", "") + "&ext=.png";
		var icon = new GIcon(G_DEFAULT_ICON);
		icon.image = iconUrl;
		icon.iconSize = new GSize(width, height);
		icon.shadowSize = new GSize(Math.floor(width*1.6), height);
		icon.iconAnchor = new GPoint(width/2, height);
		icon.infoWindowAnchor = new GPoint(width/2, Math.floor(height/12));
		icon.printImage = iconUrl + "&chof=gif";
		icon.mozPrintImage = iconUrl + "&chf=bg,s,ECECD8" + "&chof=gif";
		var iconUrl = baseUrl + "&chs=" + width + "x" + height + 
			"&chco=" + cornerColor.replace("#", "") + "," + primaryColor.replace("#", "") + "," + strokeColor.replace("#", "");
		icon.transparent = iconUrl + "&chf=a,s,ffffff11&ext=.png";
		
		icon.imageMap = [
			width/2, height,
			(7/16)*width, (5/8)*height,
			(5/16)*width, (7/16)*height,
			(7/32)*width, (5/16)*height,
			(5/16)*width, (1/8)*height,
			(1/2)*width, 0,
			(11/16)*width, (1/8)*height,
			(25/32)*width, (5/16)*height,
			(11/16)*width, (7/16)*height,
			(9/16)*width, (5/8)*height
		];
		for (var i = 0; i < icon.imageMap.length; i++)
			icon.imageMap[i] = parseInt(icon.imageMap[i]);
			
		return icon;
	},
	/**
	 * Set the boundaries of both maps and center them. Invoked in the else body of d3mls_maps._setUpMap()
	 *	@see d3mls_maps#_setUpMap
	 */
	_setBounds : function()
	{
		var latlngbounds = new GLatLngBounds( );
		for (var i=0; i<this.googleMaps.gLatLngArray.length; i++)
			latlngbounds.extend(this.googleMaps.gLatLngArray[i]);
		this.googleMaps.map.setCenter(latlngbounds.getCenter(), this.googleMaps.map.getBoundsZoomLevel(latlngbounds));
		if (d3mls_maps.d3mlsMaps.markerClickAction == "dialog")
			this.googleMaps.dialogMap.map.setCenter(latlngbounds.getCenter(), this.googleMaps.map.getBoundsZoomLevel(latlngbounds));
		else 
			this.googleMaps.dialogMap.map.setCenter(new GLatLng(this.d3mlsMaps.lat, this.d3mlsMaps.lon), this.d3mlsMaps.zoom);
	},
	/**
	 * When a marker is clicked, the GStreetviewClient method getNearestPanorama searches for streetview data based on 
	 * the marker data and calls back this method. This method then displays the streetview data in the panorama's flash player.
	 * @param	GStreetviewData 	streetviewData	The data returned by the GStreetviewClient getNearestPanorama method.
	 */
	_showStreetviewData : function (streetviewData) 
	{
  		if (streetviewData.code != 200) 
    		document.getElementById(d3mls_maps.googleMaps.gStreetviewPanoramaNodeId).innerHTML = "There is no street view available.";
		else 
		{
			document.getElementById(d3mls_maps.googleMaps.gStreetviewPanoramaNodeId).innerHTML = "";
			//invoke the GStreetviewPanorama method setLocationAndPOV which sets the location and POV in the flash player
			d3mls_maps.googleMaps.gStreetviewPanoramaObj.setLocationAndPOV(d3mls_maps.googleMaps.clickedMarker.gOverlay);
		}
	},
	/**
	 *	Set and show the map page in the dialog
	 */
   	_setDialogMap : function ()
	{
		this.googleMaps.dialogMap.map.clearOverlays();
		this.showDialog(false);
		// using details from the clicked marker...
		var point = this.googleMaps.clickedMarker.gMarker.getLatLng();
		var markerIcon = this.googleMaps.clickedMarker.icon;
		var balloonHtml = this.googleMaps.clickedMarker.balloonHtml.replace(/<span id='showDialogLink'>(.*?)<\/span>/, '');
		//... create a new marker
		var marker = new GMarker(point, {icon : markerIcon });
		GEvent.addListener(marker, "click", 
			function() 
			{
				this.openInfoWindowHtml(balloonHtml);
			});
		//center the map and add the marker
		this.googleMaps.dialogMap.map.setCenter(point, 15);
		this.googleMaps.dialogMap.gMarker = marker;
		this.googleMaps.dialogMap.map.addOverlay(marker);
		this.googleMaps.dialogMap.map.checkResize();
   	},
	/**
	 * Show the jQueryUI dialog. Currently, this method is called only from the balloon template.
	 * @param	Boolean 	setMap		Whether to set the dialog map or not. If so, this method calls _setDialogMap() which calls back this method again with setMap set to false
	 */
   	showDialog : function (setMap)
	{
		if (setMap)
			this._setDialogMap();
		else
			jQuery("#"+d3mls_maps.d3mlsMaps.dialogNodeId).dialog('open');
   	},
	/**
	 * In the showDirections dialog is the form. When the user enters an address and clicks "Get Directions," the event
	 * fires this method.
	 */
   	getDirections : function ()
	{
	   	var inputAddress = jQuery("#"+this.googleMaps.gDirectionsInputAddressId).val();
		this.googleMaps.gDirectionsObj.load("from: " + inputAddress + " to: " + this.googleMaps.dialogMap.gMarker.getLatLng(),	{ "locale": "en_US" });
   	}
}



