var body_overflow_value = $('body').css("overflow");

var accordion_options = {
						fillSpace: true,
						changestart: function(event, ui)
									 {
										 $(".ui-corner-top").removeClass("ui-corner-top");
										 $(".ui-corner-bottom").removeClass("ui-corner-bottom");
										 $(".ui-corner-all").removeClass("ui-corner-all");
									 }
						};

function show_modal_msg(settings)
{
	//css( name, value )
	//).css("background-color","yellow");
	
	var id = settings.id || 'dealermap_loading';
	var id_selector = '#' + id;
	
	var mode = settings.mode || 'indeterminate';

	var title = settings.title || '';

	//prevent the user from scrolling
	//$('body').css("overflow","hidden");
	
	/*create the div if it doesn't exist*/
	if(!$(id_selector).length)
	{
		if(mode == 'indeterminate')
		{
			$('body').prepend('<div id="' + id + '" style="background-color:#fff;display:none;text-align:center;"><p><img src="/js/jquery_lightbox/1.3.7/images/loading.gif"></img></p></div>');
		} else {
			$('body').prepend('<div id="' + id + '" style="text-align:center;background-color:#fff;color:#000;display:none;">this should be a progress bar</div>');
		}
	}
	
	openDealerModalBox(id_selector,'auto','auto',title);
	
	/*hide the close button*/
	$('.modal_overlay .ui-dialog-titlebar-close').css("display","none");
}
function remove_modal_msg(settings)
{
	id_selector = settings.id_selector || '#dealermap_loading';
	
	$(id_selector).dialog('close');
	
	/*use the default overflow property*/
	$('body').css("overflow",body_overflow_value);
}

//show the loading message
show_modal_msg({id:'dealermap_loading', mode:'indeterminate', title:'Loading Dealer Map'});


//loads up my google apis
google.load("maps", "2");
//console.log("google = %o", google);
var map = "";
var bounds = "";
var clusterer = "";
var gdir = "";
var geo = false;
var geo_results = false;
var closest_n_dealers = [];
var dealer_name_search = [];
var dist_cache = [];
var gmarkers = [];
var htmls_info = [];
var htmls_directions = [];

// === Array for decoding the failure codes ===
var reasons=[];


// arrays to hold variants of the info window html with get direction forms open
var to_htmls = [];
var from_htmls = [];

//tab names
var tab1_name = 'Info';
var tab2_name = 'Directions';

//json data for dealers object
var json_data = {};


// A function to create the marker and set up the event window
var marker_counter = 0;
function createMarker(point,name,html) {
	//local counter
	var local_counter = marker_counter;
	
	//extend the bounds
	bounds.extend(point);
	
	//var marker = new GMarker(point,bIcon);
	var marker = new GMarker(point,storeIcon);

	// The info window version with the "to here" form open
	/*
	to_htmls[marker_counter] = '<br>Directions: <b>To here</b> - <a href="javascript:fromhere(' + marker_counter + ')">From here</a>' +
	   '<br>Start address:<form action="javascript:getDirections()">' +
	   '<input type="text" SIZE=40 MAXLENGTH=40 name="saddr" id="saddr" value="" /><br>' +
	   '<INPUT value="Get Directions" TYPE="SUBMIT">' +
	   '<input type="hidden" id="daddr" value="'+name+"@"+ point.lat() + ',' + point.lng() + 
	   '"/>';
	// The info window version with the "to here" form open
	from_htmls[marker_counter] = '<br>Directions: <a href="javascript:tohere(' + marker_counter + ')">To here</a> - <b>From here</b>' +
	   '<br>End address:<form action="javascript:getDirections()">' +
	   '<input type="text" SIZE=40 MAXLENGTH=40 name="daddr" id="daddr" value="" /><br>' +
	   '<INPUT value="Get Directions" TYPE="SUBMIT">' +
	   '<input type="hidden" id="saddr" value="'+name+"@"+ point.lat() + ',' + point.lng() +
	   '"/>';
	*/
	
	/*removed maxlength and size, added style="width:100%;"*/
	to_htmls[marker_counter] = '<br>Directions: <b>To here</b> - <a href="javascript:fromhere(' + marker_counter + ')">From here</a>' +
	   '<br>Start address:<form action="javascript:getDirections()">' +
	   '<input type="text" name="saddr" id="saddr" value="" style="width:100%;"/><br>' +
	   '<INPUT value="Get Directions" TYPE="SUBMIT">' +
	   '<input type="hidden" id="daddr" value="'+name+"@"+ point.lat() + ',' + point.lng() + 
	   '"/>';
	// The info window version with the "to here" form open
	from_htmls[marker_counter] = '<br>Directions: <a href="javascript:tohere(' + marker_counter + ')">To here</a> - <b>From here</b>' +
	   '<br>End address:<form action="javascript:getDirections()">' +
	   '<input type="text" name="daddr" id="daddr" value="" style="width:100%;"/><br>' +
	   '<INPUT value="Get Directions" TYPE="SUBMIT">' +
	   '<input type="hidden" id="saddr" value="'+name+"@"+ point.lat() + ',' + point.lng() +
	   '"/>';
	
	// save the info we need to use later for the side_bar
	gmarkers[marker_counter] = marker;
	htmls_info[marker_counter] = html;
	htmls_directions[marker_counter] = '<br>Directions: <a href="javascript:tohere('+marker_counter+')">To here</a> - <a href="javascript:fromhere('+marker_counter+')">From here</a>';
	
	//add the click event
	GEvent.addListener(marker, "click", function() {
	  //marker.openInfoWindowHtml(html);
	  marker.openInfoWindowTabsHtml([new GInfoWindowTab(tab1_name,htmls_info[local_counter]), new GInfoWindowTab(tab2_name,htmls_directions[local_counter])]);
	});
	
	//increment the marker counter (it is kept out of the function because lots of functions 
	marker_counter++;
	return marker;
}

// ===== request the directions =====
function getDirections() {
	var saddr = document.getElementById("saddr").value;
	var daddr = document.getElementById("daddr").value;
	var queryOpts = {getSteps:true};
	gdir.load("from: "+saddr+" to: "+daddr,queryOpts);
}


// This function picks up the click and opens the corresponding info window
function myclick(i,doZoom) {
	//gmarkers[i].openInfoWindowHtml(htmls[i]);
	gmarkers[i].openInfoWindowTabsHtml([new GInfoWindowTab(tab1_name,htmls_info[i]), new GInfoWindowTab(tab2_name,htmls_directions[i])]);
	
	doZoom = doZoom || false;

	
	if(doZoom)
	{
		//map.setCenter(gmarkers[i].getLatLng(),17);
		map.setCenter(gmarkers[i].getLatLng(),map.getCurrentMapType().getMaximumResolution());
	}
}

// functions that open the directions forms
function tohere(i) {
	//gmarkers[i].openInfoWindowHtml(to_htmls[i]);
	//gmarkers[i].openInfoWindowTabsHtml([new GInfoWindowTab(tab1_name,htmls_info[i]), new GInfoWindowTab(tab2_name,to_htmls[i])]);
	//updateInfoWindow(tabs:GInfoWindowTab[], onupdate?:Function)
	map.updateInfoWindow([new GInfoWindowTab(tab1_name,htmls_info[i]), new GInfoWindowTab(tab2_name,to_htmls[i])]);
}
function fromhere(i) {
	//gmarkers[i].openInfoWindowHtml(from_htmls[i]);
	//gmarkers[i].openInfoWindowTabsHtml([new GInfoWindowTab(tab1_name,htmls_info[i]), new GInfoWindowTab(tab2_name,from_htmls[i])]);
	map.updateInfoWindow([new GInfoWindowTab(tab1_name,htmls_info[i]), new GInfoWindowTab(tab2_name,from_htmls[i])]);
}

//Call this function when the page has been loaded
	function initialize() {
	if (GBrowserIsCompatible()) {
		//set my apis to unload which prevents memory leaks

		window.onunload=google.maps.Unload;
		
		/*
		// my custom icon
		bIcon = new GIcon();
			bIcon.image = "/contact/images/icon21.png";
			bIcon.shadow = "/contact/images/icon21s.png";
			bIcon.iconSize = new GSize(32.0, 32.0);
			bIcon.shadowSize = new GSize(59.0, 32.0);
			bIcon.iconAnchor = new GPoint(28.0, 15.0);
			bIcon.infoWindowAnchor = new GPoint(28.0, 15.0);
			bIcon.printImage = "/contact/images/icon21_print.gif";
			bIcon.mozPrintImage = "/contact/images/icon21_mozPrint.gif";
		*/
		storeIcon = new GIcon();
			storeIcon.image = "http://google-maps-icons.googlecode.com/files/supermarket.png";
			storeIcon.iconSize = new GSize(36, 40);
			storeIcon.iconAnchor = new GPoint(18,40);
			storeIcon.infoWindowAnchor = new GPoint(28,15);
			
		clusterIcon = new GIcon();
			clusterIcon.image = "http://google-maps-icons.googlecode.com/files/realestate.png";
			clusterIcon.iconSize = new GSize(36, 40);
			clusterIcon.iconAnchor = new GPoint(18,40);
			clusterIcon.infoWindowAnchor = new GPoint(28,15);

		//here is the map
		map = new google.maps.Map2(document.getElementById("map"));
		// === create a GDirections Object ===
		gdir = new GDirections(map);
		
		// ====== Create a Client Geocoder ======
		geo = new GClientGeocoder(); 
		
		// === Array for decoding the failure codes ===
		reasons[G_GEO_SUCCESS]            = "Success";
		reasons[G_GEO_MISSING_ADDRESS]    = "Missing Address: The address was either missing or had no value.";
		reasons[G_GEO_UNKNOWN_ADDRESS]    = "Unknown Address:  No corresponding geographic location could be found for the specified address.";
		reasons[G_GEO_UNAVAILABLE_ADDRESS]= "Unavailable Address:  The geocode for the given address cannot be returned due to legal or contractual reasons.";
		reasons[G_GEO_BAD_KEY]            = "Bad Key: The API key is either invalid or does not match the domain for which it was given";
		reasons[G_GEO_TOO_MANY_QUERIES]   = "Too Many Queries: The daily geocoding quota for this site has been exceeded.";
		reasons[G_GEO_SERVER_ERROR]       = "Server error: The geocoding request could not be successfully processed.";
		reasons[G_GEO_BAD_REQUEST]        = "A directions request could not be successfully parsed.";
		reasons[G_GEO_MISSING_QUERY]      = "No query was specified in the input.";
		reasons[G_GEO_UNKNOWN_DIRECTIONS] = "The GDirections object could not compute directions between the points.";

			//setCenter
			map.setCenter(new GLatLng(0,0),0);/*initially set center to zero*/
			
			//create bounds
			bounds = new GLatLngBounds();
			
			//create clusterer
			var clusterer = new Clusterer(map);
				//clusterer settings
					clusterer.SetIcon(clusterIcon);
					clusterer.SetMaxVisibleMarkers(200);//was 20 -- disabling because it isn't working well
					clusterer.SetMinMarkersPerCluster(150);//was 15 -- disabling because it isn't working well
					clusterer.SetMaxLinesPerInfoBox(10);
				

			
			//add controls
			/*map.addControl(new google.maps.SmallZoomControl());*/
			/*map.addControl(new google.maps.MapTypeControl());
			
			//remove satellite view (unnecessary clutter)
			map.removeMapType(G_SATELLITE_MAP);
			map.removeMapType(G_HYBRID_MAP);*/
			
			map.addMapType(G_PHYSICAL_MAP);
			
			//map.addControl(new google.maps.ScaleControl());
			//map.addControl(new google.maps.OverviewMapControl());

			
			// === catch Directions errors ===
			GEvent.addListener(gdir, "error", function() {
				var code = gdir.getStatus().code;
				var reason="Code "+code;
				if (reasons[code]) {
					reason = reasons[code]
				}
				alert("Failed to obtain directions, "+reason);
			});
			
			// ========== launch the custom Panel creator a millisecond after the GDirections finishes loading ==========
			// == The delay is required in case we rely on GDirections to perform the initial setCenter ==
			GEvent.addListener(gdir,"load", function() {
				/*create the div if it doesn't exist*/
				if(!$('#directions').length)
				{
					$("#right_side_bar").append('<h3 id="directions_header"><a href="#">Directions</a></h3><div style="padding:0;margin:0;"><div id="directions"></div></div>');
				}
				setTimeout('customPanel(map,"map",gdir,document.getElementById("directions"))', 1);
			});
		
		
		/*use json object from php. it is downloaded out of this script to utilize browser cache.  changed to this method 2009.09.07 from jquery ajax method*/
		/*
		**NOTE: the dealer_data variable comes from a different external script loaded on the page that contains this page... after this script is loaded
		*/
		/*
		**NOTE: cannot declare the function and pass the handler to setInterval because of a scope issue.  have to directly pass it the function!!!
		*/
			var interval_id = setInterval(function ()
			{
				if(typeof dealer_data!="undefined")
				{
					clearInterval(interval_id);
					process_dealer_data(dealer_data);
				}
			}, 1);
			
		function process_dealer_data(data)
		{	
			/*
				id:			user id
				sn:			store name
				rd:			registration date
				ua:			user avatar
				uaw:		user avatar width
				uah:		user avatar height
				url:		website address
				s:			stores
					a:		store address
					lat:	store latitude
					lng:	store longitude
					p:		store phone number
					f:		store fax number
					tf:		store toll free number
			*/
			
			//console.log("data = %o", data);
			//console.log("data.length = %o", data.length);
			//console.log("data[0] = %o", data[0]);
			//console.log("data[0].s.length = %o", data[0].s.length);
			//console.log("data[0].id = %o", data[0].id);
			
			//var to hold all diag boxes, open the div
			var diag_boxes = '<div id="jQueryUI_diag_boxes" style="display:none;">';
			
			var data_length = data.length;
			
			for (var i = 0; i < data_length; i++)
			{
				var uid 						= data[i].id;
				var name 						= data[i].sn;
				var reg_date 					= data[i].rd;
				var img 						= data[i].ua;
				var img_w 						= data[i].uaw;
				var img_h 						= data[i].uah;
				var url 						= data[i].url;
				
				//var to hold this diag box, open div
				var diag_box = '<div id="dealer_'+uid+'_info">';
				
				for (var j = 0; j < data[i].s.length; j++)
				{
					var lat 			= data[i].s[j].lat;
					var lng 			= data[i].s[j].lng;
					
					//make sure the address is there
					if(!data[i].s[j].a){data[i].s[j].a = 'Address Error @ Latitude: ' + lat + ', Longitude: ' + lng;}
					
					var addr 			= data[i].s[j].a;
					var phone 			= data[i].s[j].p;
					var fax 			= data[i].s[j].f;
					var toll_free 		= data[i].s[j].tf;
					
					//add this gmarker index to the json array
					data[i].s[j].gmarker_index = marker_counter;
					
					//add entry to diag_box BEFORE CALLING createMarker because I want the marker_counter before it is incremented
					diag_box += '<p style="text-transform: capitalize;"><a href="" onclick="myclick('+marker_counter+',true); return false;">' + addr + '</a></p>';
					
					/*create the point and add it to the map*/
					var point = new GLatLng(lat,lng);
					//var label = "Store: " + name + "<br/>Address: " + addr;
					//var label = escape('<table><tr><td style="text-align:right;">Store:</td><td style="text-align:left;">' + name + '<br/></td></tr><tr><td style="text-align:right;">Address:</td><td style="text-align:left;">' + addr + '</td></tr></table>');
					var label = escape('<span style="text-decoration:underline">Store:</span><p style="padding:0;margin:0;margin-left:1em;">' + name + '</p><span style="text-decoration:underline">Address:</span><p style="padding:0;margin:0;margin-left:1em;">' + addr + '</p>');
					
					//create html for the infowindow
					var html = '';
					
					//build the html string up
						var tb_page_height = window.height;
						var tb_page_width = window.width;
						//add the img
						if(img != '')
						{
							html += '<a href="/forums/memberlist.php?mode=viewprofile&u='+uid+'" style="float:left;padding-right:2px;" target="_blank"><img src="'+img+'" style="border-style:none;"></img></a>';
						}
						
						//add the profile link
						html += '<a href="/forums/memberlist.php?mode=viewprofile&u='+uid+'" style="float:left;color:blue;" target="_blank">'+name+'</a><br/>';
						
						//add member since
						html += '<span style="float:left;"><b>Member Since: </b>'+reg_date+'</span><br/>';
						
						//add the website link
						if(url != '')
						{
							html += '<span style="float:left;"><b>Website: </b><a href="'+url+'" target="_blank">'+url+'</a></span><br/>';
						}
						
						//add the phone number
						if(phone != 0)
						{
							html += '<span style="float:left;"><b>Phone: </b>'+'('+phone.toString().substr(0,3)+') '+phone.toString().substr(3,3)+' - '+phone.toString().substr(6)+'</span><br/>';
						}
						
						//add the fax number
						if(fax != 0)
						{
							html += '<span style="float:left;"><b>Fax: </b>'+'('+fax.toString().substr(0,3)+') '+fax.toString().substr(3,3)+' - '+fax.toString().substr(6)+'</span><br/>';
						}
						
						//add the toll free number
						if(toll_free != 0)
						{
							html += '<span style="float:left;"><b>Toll Free: </b>'+'('+toll_free.toString().substr(0,3)+') '+toll_free.toString().substr(3,3)+' - '+toll_free.toString().substr(6)+'</span><br/>';
						}
						
						//add address
						html += '<span style="float:left;"><b>Address: </b>'+addr+'</span><br/>';
						
						//add email and pm link
						//html += '<p style="clear:both;margin:0;padding:0;text-align:center;"><a href="/forums/memberlist.php?mode=email&u='+uid+'" target="_blank">Send e-mail</a>  |  <a href="/forums/ucp.php?i=pm&mode=compose&u='+uid+'" target="_blank">Send PM</a></p>';
						
					
					var marker = createMarker(point,label,html);
					
					//map.addOverlay(marker);
					clusterer.AddMarker(marker, name)
					
				}
				
				//var to hold this diag box, close div
				diag_box += '</div>';
				
				//add the diag_box to diag_boxes
				diag_boxes += diag_box;
				
				// add a line to the side_bar html
				//'<li><a href="" onclick="openDealerDiagBox(\'\#dealer_'+uid+'_info\',\''+escape(name)+'\'); return false;">' + name + '</a></li>';
				dealer_name_search.push({name:name,uid:uid});
				
				//store data as global var
				json_data = data;
			}
			//fit the zoom to the data
			map.setZoom(map.getBoundsZoomLevel(bounds));
			map.setCenter(bounds.getCenter());
			
			//save the default zoom level/position
			map.savePosition();
			
			//var to hold all diag boxes, close the div
			diag_boxes += '</div>';
			
			$("body").append(diag_boxes);
			
			//$("body").append('<a href="" onclick="openDealerDiagBox(0); return false;">clickmeplease</a>');
			
			// put the assembled side_bar_html contents into the side_bar div
			//side_bar_html += '</ul>';
			
			//side_bar_html = '<h3><a href="#">Store Name Search</a></h3><div style="padding:0;margin:0;">' + side_bar_html + '</div>';
			
			function sortByName(a, b) {
				var x = a.name.toLowerCase();
				var y = b.name.toLowerCase();
				return ((x < y) ? -1 : ((x > y) ? 1 : 0));
			}
			dealer_name_search.sort(sortByName);
			
			
			//format the dealer_name_search html
			var dealer_name_search_html = '<h3><a href="#">Store Name Search</a></h3><div style="padding:0;margin:0;"><ul>';
			for (var i=0; i<dealer_name_search.length; i++)
			{
				dealer_name_search_html += '<li><a href="" onclick="openDealerDiagBox(\'\#dealer_'+dealer_name_search[i].uid+'_info\',\''+escape(dealer_name_search[i].name)+'\'); return false;">' + dealer_name_search[i].name + '</a></li>';
			}
			dealer_name_search_html += '</ul><div>';
			
			
			
			
			/*create the side_bars*/
				/*left_side_bar*/
					$("#left_side_bar").append(dealer_name_search_html).css('visibility','visible');
					update_accordion("#left_side_bar", accordion_options);
		
				/*right_side_bar*/
					$("#right_side_bar").css('visibility','visible');
					update_accordion("#right_side_bar", accordion_options);
					
			/*display map*/
			$("#map").css('visibility','visible');
			
			/*finished processing, go to next step*/
			process_dealer_data_completed();
		}
		
		
		function process_dealer_data_completed()
		{
			//remove the loading message
			remove_modal_msg({id_selector:'#dealermap_loading'});
			
			var offset = $('#map').offset();
			
			//scroll to the map
			window.scrollTo(offset.left,offset.top);
		}
		
		
		
		
		
		
		
		
	// This Javascript is based on code provided by the
	// Blackpool Community Church Javascript Team
	// http://www.commchurch.freeserve.co.uk/   
	// http://econym.googlepages.com/index.htm
		
// the next two } closes initialize function and GBrowserIsCompatible()
	}
   }
	//loads founction initialize()
	google.setOnLoadCallback(initialize);
	
	
/*update accordian box*/
function update_accordion(selector,options,index_or_selector)
{
	$(selector).accordion('destroy').accordion(options).accordion('activate',index_or_selector);
	
	/*remove rounded corners*/
	$(".ui-corner-top").removeClass("ui-corner-top");
	$(".ui-corner-bottom").removeClass("ui-corner-bottom");
	$(".ui-corner-all").removeClass("ui-corner-all");
}
/*dialog box function*/
function openDealerDiagBox(selector,diag_title,h_pos,v_pos,open_func,close_func)
{
	//set title
	diag_title = unescape(diag_title) || 'Untitled';
	
	//set position
	h_pos = h_pos || 'left';
	v_pos = v_pos || 'center';
	
	//set open and close functions
	open_func = open_func || function(event, ui) {};
	close_func = close_func || function(event, ui) {};
	
	/*
	*/
	/*
	ONLY OPEN THE DIAG BOX IF THE BOX CONTAINS MORE THAN ONE LINK!
		--OTHERWISE JUST CLICK THE LINK INSIDE IT BECAUSE THERE IS NO CHOICE
	*/
	/*
	*/
	if($(selector + " a").length == 1)
	{
		//there is only one link, we should automatically show the infowindow for it.
		$(selector + " a").click();
		
		//since there is now no need to show the diag box of stores, we should return
		return;
	}
	
	/*
	*/
	/*
	If we get here, then there is more than one link and we should show the diag box to allow the user to pick which location to look at.
	*/
	/*
	*/
	
	//do it
	if(!$(selector).dialog('isOpen'))
	{
		$(selector).dialog({
						    open: open_func,
							autoOpen: true,
							bgiframe: false,
							buttons: {},
							close: close_func,
							closeOnEscape: true,
							dialogClass: 'modal_overlay',
							draggable: true,
							height: $("#map").height(),
							hide: 'slide',
							maxHeight: false,
							maxWidth: false,
							minHeight: 150,
							minWidth: 150,
							modal: false,
							position: [h_pos,v_pos],
							resizable: true,
							show: 'slide',
							stack:true,
							title: diag_title,
							width: 300,
							zIndex: 1000
						  });
		$(selector).dialog('open');
		$(selector).dialog('moveToTop');
	} else {
		/*close the current one*/
		$(selector).dialog('close');
		
		 /*without this on close, the box title won't update*/
		$(selector).dialog('destroy');
		
		/*open a new one*/
		openDealerDiagBox(selector,diag_title,h_pos,v_pos,open_func,close_func);
	}
}

/*dialog box function*/
function openDealerModalBox(selector,height,width,diag_title,h_pos,v_pos,open_func,close_func)
{
	//set height and width
	height = height || 300;
	width = width || 300;
	
	//set title
	diag_title = unescape(diag_title) || 'Untitled';
	
	//set position
	h_pos = h_pos || 'center';
	v_pos = v_pos || 'center';
	
	//set open and close functions
	open_func = open_func || function(event, ui) {};
	close_func = close_func || function(event, ui) {};
	
	
	//do it
	if(!$(selector).dialog('isOpen'))
	{
		$(selector).dialog({
						    open: open_func,
							autoOpen: true,
							bgiframe: false,
							buttons: {},
							close: close_func,
							closeOnEscape: false,
							dialogClass: 'modal_overlay',
							draggable: false,
							height: height,
							hide: null,
							maxHeight: false,
							maxWidth: false,
							minHeight: 150,
							minWidth: 150,
							modal: true,
							position: [h_pos,v_pos],
							resizable: false,
							show: null,
							stack:true,
							title: diag_title,
							width: width,
							zIndex: 1000
						  });
		$(selector).dialog('open');
		$(selector).dialog('moveToTop');
	} else {
		$(selector).dialog('close');
	}
}


/*calc distance using spherical law of cosines*/
/*orig source http://www.movable-type.co.uk/scripts/latlong.html*/
/* ex)distCosineLaw(34.0802801,-118.375268,34.0805651,-118.375272) returns 0.019714076060500626 */
function distCosineLaw(lat1, lon1, lat2, lon2, d_unit) {
	/*convert to rad*/
	lat1 = lat1 * Math.PI/180;
	lon1 = lon1 * Math.PI/180;
	lat2 = lat2 * Math.PI/180;
	lon2 = lon2 * Math.PI/180;

	//set units
	switch (d_unit)
	{
		case 'mi':
			R = 3963.0; //(statute miles)
			break;
		case 'km':
			R = 6378.7; //(kilometers) 
			break;
		case 'nm':
			R = 3437.74677; //(nautical miles)
			break;
		default:
			R = 3963.0; //(statute miles)
			d_unit = 'mi'; //fix the unit
	}
	
	//do the calculation
	var d = Math.acos(Math.sin(lat1)*Math.sin(lat2) + Math.cos(lat1)*Math.cos(lat2)*Math.cos((lon2-lon1))) * R;
	
	//return the distance/unit object
	return {d:d,u:d_unit};
}

/*geocode address*/
function geocodeAddr(settings_object)
{
	if(settings_object)
	{
		//console.log("settings_object = %o",settings_object);
		//mode
		var mode = settings_object.mode;
		    mode = (mode == 'findClosestDealers' || mode == 'findDealersInRadius') ? mode : 'findClosestDealers';
		
		//addr
		var addr = (settings_object.addr) ? settings_object.addr : false;
		
		//number of dealers to return
		var n_dealers = (settings_object.n_dealers && settings_object.n_dealers !== '' && !isNaN(settings_object.n_dealers * 1)) ? settings_object.n_dealers : 10;
		
		/*distance unit of measure*/
		var dist_unit = settings_object.dist_unit;
		    dist_unit = (dist_unit == 'mi' || dist_unit == 'km' || dist_unit == 'nm') ? dist_unit : 'mi';
			
		/*radius*/
		var radius = (settings_object.radius && settings_object.radius !== '' && !isNaN(settings_object.radius * 1)) ? settings_object.radius : false;
			if(radius === false)
			{
				switch(dist_unit)
				{
					case 'mi':
						radius = 100;
						break;
					case 'km':
						radius = 160;
						break;
					case 'nm':
						radius = 87;
						break;
					default:
						radius = 100;
				}
			}
			
	} else {
		alert('Error: Settings object not defined!');
		return;
	}
	
	//make sure we have an address
	if(addr === false){alert('Error: Address not specified!');return;}
	
	
	if(geo)
	{
		// ====== Perform the Geocoding ======        
		geo.getLocations(addr, function (result)
								{
									// If that was successful
									if (result.Status.code == G_GEO_SUCCESS)
									{
										/*store the results*/
										geo_results = result;
										
										/*have user choose correct result*/
										if(result.Placemark.length > 1)
										{
											/*create div if it isn't there*/
											if(!$('#geocodeAddr_msg').length)
											{
												$('body').prepend('<div id="geocodeAddr_msg" style="display:none;"></div>');
											}
											
											$('#geocodeAddr_msg').html("Found " +result.Placemark.length +" results");

											/*Loop through the results, placing markers*/
											$('#geocodeAddr_msg').append("<ol>");
											for (var i=0; i<result.Placemark.length; i++) {
												var p = result.Placemark[i].Point.coordinates;
												/*p is an array with lng,lat in that order*/
												//var marker = new GMarker(new GLatLng(p[1],p[0]));
												$('#geocodeAddr_msg').append("<li>" + result.Placemark[i].address + "</li>");
											}
											$('#geocodeAddr_msg').append("</ol>");

											/*open the diag box*/
											openDealerDiagBox('#geocodeAddr_msg');
										} else {
											
											var diag_id = addr.replace(/[^0-9A-Za-z]/g,"_");//remove all chars that are not alphanumeric so that we don't bork a function call
											
											/*decide what to do based on the mode*/
											if(mode == 'findClosestDealers')
											{
												diag_id = 'findClosestDealers_' + diag_id;
												diag_id_selector = '#' + diag_id;
												
												if(!$(diag_id_selector).length)
												{
													$('body').prepend('<div id="'+diag_id+'" style="display:none;"></div>');
												}
												/* there was only one result, find the closest dealers */
												var lng_lat = result.Placemark[0].Point.coordinates;
												/*lng_lat is an array with lng,lat in that order*/
												
												/*store the clostest dealers in the global array*/
												closest_n_dealers = findClosestDealers(lng_lat[1],lng_lat[0],n_dealers,dist_unit);
												//console.log('closest_n_dealers = %o',closest_n_dealers);
												$(diag_id_selector).html("The closest " + n_dealers + " dealers are:");
												$(diag_id_selector).append("<ol></ol>");
												for (var i=0; i<closest_n_dealers.length; i++)
												{
													//$('#closest_n_dealers ol').append("<li><ul></ul></li>");
													//$('#closest_n_dealers ol ul:last').append("<li>" + closest_n_dealers[i].store + "</li><li>" + closest_n_dealers[i].address + "</li><li>" + closest_n_dealers[i].distance.d + closest_n_dealers[i].distance.u + "</li>");
													$(diag_id_selector + ' ol:first').append("<li><a href='' onclick=\"myclick('" + closest_n_dealers[i].gmarker_index + "',true); return false;\"><ol style='list-style-type:square;'>"+"<li>" + closest_n_dealers[i].store + "</li><li>" + closest_n_dealers[i].address + "</li><li>&asymp;" + closest_n_dealers[i].distance.d.toFixed(1) + " " + closest_n_dealers[i].distance.u + "</li>"+"</ol></a><br/></li>");
												}
												
												/*open the diag box*/
												openDealerDiagBox(diag_id_selector,"Closest " + n_dealers + " dealers to " + addr);
												
												
											} else if(mode == 'findDealersInRadius') {
												
												diag_id = 'findDealersInRadius_' + diag_id;
												diag_id_selector = '#' + diag_id;
												
												//findDealersInRadius(in_lat,in_lng,dist_unit,radius)
												if(!$(diag_id_selector).length)
												{
													$('body').prepend('<div id="'+diag_id+'" style="display:none;"></div>');
												}
												var lat = result.Placemark[0].Point.coordinates[1];
												var lng = result.Placemark[0].Point.coordinates[0];
												
												var dealers_in_radius = findDealersInRadius(lat,lng,dist_unit,radius);
												var dealers_in_radius_len = dealers_in_radius.length;
												
												$(diag_id_selector).html("Found " + dealers_in_radius_len + " Dealers within " + radius + dist_unit + ":");
												$(diag_id_selector).append("<ol></ol>");
												for (var i=0; i<dealers_in_radius_len; i++)
												{
													$(diag_id_selector + ' ol:first').append("<li><a href='' onclick=\"myclick('" + dealers_in_radius[i].gmarker_index + "',true); return false;\"><ol style='list-style-type:square;'>"+"<li>" + dealers_in_radius[i].store + "</li><li>" + dealers_in_radius[i].address + "</li><li>" + dealers_in_radius[i].distance.d.toFixed(1) + dealers_in_radius[i].distance.u + "</li>"+"</ol></a><br/></li>");
												}
												
												/*open the diag box*/
												openDealerDiagBox(diag_id_selector,"Dealers within " + radius + dist_unit + " of " + addr);
												
												
											}
										}//close the if > 1 result else
											
									} else {
										/*flag no results*/
										geo_results = false;
										
										// ====== Decode the error status ======
										var reason="Code "+result.Status.code;
										if (reasons[result.Status.code]) {
											reason = reasons[result.Status.code]
										} 
										alert('Could not find "'+addr+ '" ' + reason);
									}
								}
						);
		
		
	} else {
		alert('Please wait for the map to load and resubmit your request');
	}
}

/*find N closest dealers, returns an array*/
function findClosestDealers(in_lat,in_lng,n_dealers,dist_unit)
{

	var dealer_dist_array = [];
	
	/*loop thru the array and check the distances for each one and store them*/
	//array holding data: json_data
	
	for (var i = 0; i < json_data.length; i++)
	{
		var name 						= json_data[i].sn;
		
		for (var j = 0; j < json_data[i].s.length; j++)
		{
			var addr 			= json_data[i].s[j].a;
			var lat 			= json_data[i].s[j].lat;
			var lng 			= json_data[i].s[j].lng;
			var index			= json_data[i].s[j].gmarker_index;
			
			//calc the distance
			var dist = distCosineLaw(in_lat,in_lng,lat,lng,dist_unit);

			//store distance
			dealer_dist_array.push({address:addr,distance:dist,gmarker_index:index,store:name});
		}
	}
	
	
	
	//sort the dealer_dist_array to get the n closest locations
	function sortByDistance(a,b)
	{
		return a.distance.d - b.distance.d;
	}
	dealer_dist_array.sort(sortByDistance);
	
	//store the closest N dealers globally
	if(dealer_dist_array.length >= n_dealers)
	{
		return dealer_dist_array.slice(0,n_dealers);
	} else {
		return dealer_dist_array.slice(0,dealer_dist_array.length);
	}
}


/*find dealers within a radius, returns an array*/
function findDealersInRadius(in_lat,in_lng,dist_unit,radius)
{
	var dealer_dist_array = [];
	
	/*loop thru the array and check the distances for each one and store them*/
	//array holding data: json_data
	
	for (var i = 0; i < json_data.length; i++)
	{
		var name 						= json_data[i].sn;
		
		for (var j = 0; j < json_data[i].s.length; j++)
		{
			var addr 			= json_data[i].s[j].a;
			var lat 			= json_data[i].s[j].lat;
			var lng 			= json_data[i].s[j].lng;
			var index			= json_data[i].s[j].gmarker_index;
			
			//calc the distance
			var dist = distCosineLaw(in_lat,in_lng,lat,lng,dist_unit);
			
			
			
			if(dist.d <= radius)
			{
				//store distance
				dealer_dist_array.push({address:addr,distance:dist,gmarker_index:index,store:name});
			}
		}
	}
	
	//sort the dealer_dist_array to get the n closest locations
	function sortByDistance(a,b)
	{
		return a.distance.d - b.distance.d;
	}
	dealer_dist_array.sort(sortByDistance);
	
	return dealer_dist_array;
}





// ============ custom direction panel ===============
function customPanel(map,mapname,dirn,div) {
	var html = "<a id='printItLink' href='javascript:printIt' onClick='printIt(); return false;'><img src='/images/pc_25.gif' style='border:none;background-color:#FFFFFF;' alt=''/> Print Directions</a>";
	
      
	// ===== local functions =====
	
	// === waypoint banner ===
	function waypoint(point, img_url, address) {
		var target = '"' + mapname+".showMapBlowup(new GLatLng("+point.toUrlValue(6)+"))"  +'"';
		html += '<table style="border: 1px solid silver; margin: 10px 0px; background-color: rgb(238, 238, 238); border-collapse: collapse; color: rgb(0, 0, 0);">';
		html += '  <tr style="cursor: pointer;" onclick='+target+'>';
		html += '    <td style="padding: 4px 15px 0px 5px; vertical-align: middle; width: 20px;">';
		html += '      <img src="'+img_url+'">'
		html += '    </td>';
		html += '    <td style="vertical-align: middle; width: 100%;">';
		html +=        address;
		html += '    </td>';
		html += '  </tr>';
		html += '</table>';
	}

	// === route distance ===
	function routeDistance(dist) {
		html += '<div style="text-align: right; padding-bottom: 0.3em;">' + dist + '</div>';
	}      

	// === step detail ===
	function detail(point, num, description, dist) {
		var target = '"' + mapname+".showMapBlowup(new GLatLng("+point.toUrlValue(6)+"))"  +'"';
		html += '<table style="margin: 0px; padding: 0px; border-collapse: collapse;">';
		html += '  <tr style="cursor: pointer;" onclick='+target+'>';
		html += '    <td style="border-top: 1px solid rgb(205, 205, 205); margin: 0px; padding: 0.3em 3px; vertical-align: top; text-align: right;">';
		html += '      <a href="javascript:void(0)"> '+num+'. </a>';
		html += '    </td>';
		html += '    <td style="border-top: 1px solid rgb(205, 205, 205); margin: 0px; padding: 0.3em 3px; vertical-align: top; width: 100%;">';
		html +=        description;
		html += '    </td>';
		html += '    <td style="border-top: 1px solid rgb(205, 205, 205); margin: 0px; padding: 0.3em 3px 0.3em 0.5em; vertical-align: top; text-align: right;">';
		html +=        dist;
		html += '    </td>';
		html += '  </tr>';
		html += '</table>';
	}

	// === Copyright tag ===
	function copyright(text) {
		html += '<div style="font-size: 0.86em;">' + text + "</div>";
	}
        

	// === read through the GRoutes and GSteps ===
	var marker_img_url = "http://maps.gstatic.com/intl/en_us/mapfiles/icon_greenA.png";/*set start marker_img_url*/
	for (var i=0; i<dirn.getNumRoutes(); i++) {
		var route = dirn.getRoute(i);
		var geocode = route.getStartGeocode();
		var point = route.getStep(0).getLatLng();
		// === Waypoint at the start of each GRoute
		waypoint(point, marker_img_url, geocode.address);
		routeDistance(route.getDistance().html+" (about "+route.getDuration().html+")");

		for (var j=0; j<route.getNumSteps(); j++) {
			var step = route.getStep(j);
			// === detail lines for each step ===
			detail(step.getLatLng(), j+1, step.getDescriptionHtml(), step.getDistance().html);
		}
	}
	marker_img_url="http://maps.gstatic.com/intl/en_us/mapfiles/icon_greenB.png";/*set stop marker_img_url*/
        
	// === the final destination waypoint ===   
	var geocode = route.getEndGeocode();
	var point = route.getEndLatLng();
	waypoint(point, marker_img_url, geocode.address);
		 
	// === the copyright text ===
	copyright(dirn.getCopyrightsHtml());

	// === drop the whole thing into the target div
	div.innerHTML = unescape('<div style="padding:5px;">'+html+'</div>');

	/*enable the div accordion*/
	update_accordion("#right_side_bar", accordion_options,'#directions_header');

} // ============ end of customPanel function ===========


/*MAP CONTROLS*/
function mapZoomIn()
{
	map.zoomIn();
}
function mapZoomOut()
{
	map.zoomOut();
}
function mapZoomReset()
{
	map.returnToSavedPosition();	
}
function mapPanLeft()
{
	//panDirection(dx:one of -1 0 +1, dy:one of -1 0 +1) 
	map.panDirection(1,0);
}
function mapPanRight()
{
	map.panDirection(-1,0);
}
function mapPanUp()
{
	map.panDirection(0,1);
}
function mapPanDown()
{
	map.panDirection(0,-1);
}
function mapTypeNormal()
{
	map.setMapType(G_NORMAL_MAP);
}
function mapTypeSatellite()
{
	map.setMapType(G_SATELLITE_MAP);
}
function mapTypeHybrid()
{
	map.setMapType(G_HYBRID_MAP);
}
function mapTypePhysical()
{
	map.setMapType(G_PHYSICAL_MAP);
}


//print function
function printIt()
{
	/*hide print link*/
	var print_html = $('#printItLink').replaceWith('<span id="printItLink"></span>');
	/*open a blank window*/
	w=window.open('about:blank');
	w.document.open();
	/*write content var html to the window*/
	w.document.write($('#directions').html());
	/*write some javascript to make the newly opened window print itself.*/
	w.document.writeln("<script>window.print(); window.onfocus = function() { window.close(); };</script>");
	w.document.writeln("</html>");
	w.document.close();
	/*replace the print link*/
	$('#printItLink').replaceWith(print_html);
}
