
// the stuff that has to be remebered to restore the map
// mapData.polylines[i] -- { numPoints:activePolyline.numPoints, data:activePolyline.data }
// mapData.markers[i] -- { point:GLatLng, note:'' }
var mapData = false;

var map = false;
var googleGeocoder = false;
var balloonForm = false;
var mapMode = 'mark';
var mouseMoveListener = false;
var openMarker = false;
var tempLine = false;
var activePolyline = false; // activePolyline = { gpolyline:false, numPoints:0, data:'', levels:'', last:new GLatLng(0,0) };
var dotIcon = new GIcon();
dotIcon.image = 'dot-icon.gif';
dotIcon.shadow = '';
dotIcon.iconSize = new GSize(10,10);
dotIcon.shadowSize = new GSize(0, 0);
dotIcon.iconAnchor = new GPoint(5, 5);
dotIcon.infoWindowAnchor = new GPoint(5, 5);

function saveNote()
{
	if (openMarker)
	{
		mapData.markers[openMarker.yoIndex].note = balloonForm.yoTextarea.value;
		updateLinkToSelf();
	}
}

function initBalloonForm()
{
	balloonForm = document.createElement('form');
	balloonForm.id = 'balloon-form';
	balloonForm.action = '#';
	balloonForm.onsubmit = function() { return false; };

	balloonForm.yoTextarea = document.createElement('textarea');
	balloonForm.yoTextarea.name = 'yoNote';
	balloonForm.yoTextarea.rows = 6;
	balloonForm.yoTextarea.cols = 30;
	balloonForm.appendChild(balloonForm.yoTextarea);
	balloonForm.yoTextarea.onchange = saveNote;
	balloonForm.yoTextarea.onkeyup = saveNote;
	balloonForm.yoTextarea.onkeydown = saveNote;

	var div = document.createElement('div');
	div.innerHTML = "<input type='submit' value='delete marker' onclick='deleteOpenMarker();' />";
	balloonForm.appendChild(div);
}

function handleResize()
{
	var map = document.getElementById('map');
	// 1 pixel borders on each side
	var newHeight = document.documentElement.clientHeight - map.offsetTop - 6 - 2;
	var newWidth = document.documentElement.clientWidth - map.offsetLeft - 6 - 2;
	if (map.style.height != newHeight + 'px')
		map.style.height = newHeight + 'px';
	if (map.style.width != newWidth + 'px')
		map.style.width = newWidth + 'px';
}

function mouseOut()
{
	if (tempLine)
	{
		map.removeOverlay(tempLine);
		tempLine = false;
	}
}

function initMapDataFromUrl()
{
	mapData = { center:new GLatLng(40.714, -74.006), zoom:12, mapType:G_NORMAL_MAP, polylines:[], markers:[] };

	var params = window.location.search.substring(1).split('&');
	for (var i = 0; i < params.length; i++)
	{
		if (params[i].split('*').length == 2)
		{
			// alert(params[i].split('*')[1] + ' decodes to ' + decodeURIComponent(params[i].split('*')[1]));
			mapData.polylines.push({ numPoints: Number(params[i].split('*')[0]), data: decodeURIComponent(params[i].split('*')[1]) });
		}
		else if (params[i].split('=').length == 2)
		{
			paramKey = params[i].split('=')[0];
			paramValue = params[i].split('=')[1];

			if (paramKey == 'c')
			{
				var lat = Number(paramValue.split(',')[0]);
				var lng = Number(paramValue.split(',')[1]);
				mapData.center = new GLatLng(lat,lng);
			}
			else if (paramKey == 'z')
			{
				mapData.zoom = Number(paramValue); 
			}
			else if (paramKey == 't')
			{
				if (paramValue == G_NORMAL_MAP.getUrlArg())
					mapData.mapType = G_NORMAL_MAP;
				else if (paramValue == G_SATELLITE_MAP.getUrlArg())
					mapData.mapType = G_SATELLITE_MAP;
				else if (paramValue == G_HYBRID_MAP.getUrlArg())
					mapData.mapType = G_HYBRID_MAP;
			}
			else if (paramKey.split(',').length == 2)
			{
				var lat = Number(paramKey.split(',')[0]);
				var lng = Number(paramKey.split(',')[1]);
				mapData.markers.push({ point: new GLatLng(lat,lng), note: decodeURIComponent(paramValue) });
				// alert('marker(' + mapData.markers[mapData.markers.length-1].point + '): ' + mapData.markers[mapData.markers.length-1].note);
			}
		}
	}
}

function updateLinkToSelf()
{
	var link = window.location.pathname;
	link += '?c=' + map.getCenter().toUrlValue();
	link += '&z=' + map.getZoom();
	link += '&t=' + map.getCurrentMapType().getUrlArg();

	for (var i = 0; i < mapData.polylines.length; i++)
		link += '&' + mapData.polylines[i].numPoints + '*' + encodeURIComponent(mapData.polylines[i].data);

	for (var i = 0; i < mapData.markers.length; i++)
		if (mapData.markers[i])
			link += '&' + mapData.markers[i].point.toUrlValue() + '=' + encodeURIComponent(mapData.markers[i].note);

	document.getElementById('self-link').href = link;
}

function drawMapData()
{
	for (var i = 0; i < mapData.polylines.length; i++)
	{
		var levels = '';
		for (var j = 0; j < mapData.polylines[i].numPoints; j++)
			levels += 'B';

		var tempPolyline = new GPolyline.fromEncoded({ points:mapData.polylines[i].data, zoomFactor:32, levels:levels, numLevels: 4 });
		// alert('adding polyline from url data=' + mapData.polylines[i].data + ' levels=' + levels + ' tempPolyline=' + tempPolyline + ' vertex count: ' + tempPolyline.getVertexCount());
		map.addOverlay(tempPolyline); 

		var marker = new GMarker(tempPolyline.getVertex(0), dotIcon);
		marker.yoMarkerType = 'polyline-endpoint';
		map.addOverlay(marker);
		marker = new GMarker(tempPolyline.getVertex(tempPolyline.getVertexCount() - 1), dotIcon);
		marker.yoMarkerType = 'polyline-endpoint';
		map.addOverlay(marker);
	}

	for (var i = 0; i < mapData.markers.length; i++)
	{
		var marker = new GMarker(mapData.markers[i].point, { draggable: true });
		marker.yoMarkerType = 'mark';
		marker.yoIndex = i;
		map.addOverlay(marker);
		GEvent.addListener(marker, 'dragend', markerDrag);
	}
}

function load() 
{
	if (GBrowserIsCompatible()) 
	{
		initMapDataFromUrl();

		handleResize();

		map = new GMap2(document.getElementById("map"));
		map.addControl(new GLargeMapControl());
		map.addControl(new GMapTypeControl());

		map.setCenter(mapData.center, mapData.zoom); 

		updateLinkToSelf();

		GEvent.addListener(map, 'click', mapClick);
		GEvent.addListener(map, 'mouseout', mouseOut);

		GEvent.addListener(map, 'moveend', updateLinkToSelf);
		GEvent.addListener(map, 'maptypechanged', updateLinkToSelf);

		window.onresize = handleResize;

		googleGeocoder = new GClientGeocoder();
		initBalloonForm();

		map.setMapType(mapData.mapType);
		drawMapData();
	}
}

function setMode()
{
	var form = document.getElementById('mode');
	for (var i = 0; i < form.mode.length; i++) 
		if (form.mode[i].checked)
			if (mapMode != form.mode[i].value)
				mapMode = form.mode[i].value;
			else
				return; // not changed, do nothing

	resetMap();
}

/* clears out any transient stuff, like lines in the process of being
 * drawn */
function resetMap()
{
	if (activePolyline)
		activePolyline = false;

	if (mouseMoveListener)
	{
		GEvent.removeListener(mouseMoveListener);
		mouseMoveListener = false;
	}

	if (tempLine)
	{
		map.removeOverlay(tempLine);
		tempLine = false;
	}

	updateLinkToSelf();
}

function showBalloon(marker)
{ 
	map.getInfoWindow().hide();

	if (mapData.markers[marker.yoIndex].note)
		balloonForm.yoTextarea.value = mapData.markers[marker.yoIndex].note;
	else
		balloonForm.yoTextarea.value = '';

	marker.openInfoWindow(balloonForm);

	openMarker = marker;
}

function markerDrag()
{
	if (this == openMarker)
		showBalloon(openMarker);
	mapData.markers[this.yoIndex].point = this.getPoint();
	updateLinkToSelf();
}

function deleteOpenMarker()
{
	if (openMarker)
	{
		map.getInfoWindow().hide();
		map.removeOverlay(openMarker);
		mapData.markers[openMarker.yoIndex] = false;
		// alert('deleted marker ' + openMarker.yoIndex);
		openMarker = false;
		
	}
}

function showLine(point)
{
	if (activePolyline && activePolyline.last)
	{
		var pts = [];
		pts.push(activePolyline.last);
		pts.push(point);
		if (tempLine)
			map.removeOverlay(tempLine);
		tempLine = new GPolyline(pts);
		map.addOverlay(tempLine);
	}
}

// Encode a signed number in the encode format.
function encodeSignedNumber(num) 
{
	var sgn_num = num << 1;

	if (num < 0)
		sgn_num = ~sgn_num;

	return encodeNumber(sgn_num);
}

// Encode an unsigned number in the encode format.
function encodeNumber(num) 
{
	var encodeString = "";

	while (num >= 0x20) 
	{
		encodeString += (String.fromCharCode((0x20 | (num & 0x1f)) + 63));
		num >>= 5;
	}

	encodeString += (String.fromCharCode(num + 63));
	return encodeString;
}


function placeMarker(point)
{
	marker = new GMarker(point, { draggable: true });
	marker.yoMarkerType = 'mark';

	marker.yoIndex = mapData.markers.length;
	mapData.markers.push({ point: point, note: '' });

	map.addOverlay(marker);
	GEvent.addListener(marker, 'dragend', markerDrag);
	showBalloon(marker);
}

function addPolylineVertex(point)
{
	if (!activePolyline)
		activePolyline = { gpolyline:false, numPoints:0, data:'', levels:'', last:new GLatLng(0,0), index:-1, startMarker:false, endMarker:false };

	var dlat = Math.floor(point.lat() * 1e5) - Math.floor(activePolyline.last.lat() * 1e5);
	var dlng = Math.floor(point.lng() * 1e5) - Math.floor(activePolyline.last.lng() * 1e5);;

	activePolyline.data += encodeSignedNumber(dlat) + encodeSignedNumber(dlng);
	activePolyline.numPoints++;
	activePolyline.levels += 'B';
	activePolyline.last = point;

	if (activePolyline.index == -1 && activePolyline.numPoints >= 2)
	{
		activePolyline.index = mapData.polylines.length;
		mapData.polylines.push({ numPoints:activePolyline.numPoints, data:activePolyline.data });
	}
	else if (activePolyline.index != -1)
	{
		// save
		mapData.polylines[activePolyline.index].numPoints = activePolyline.numPoints;
		mapData.polylines[activePolyline.index].data = activePolyline.data;
	}

	if (activePolyline.gpolyline)
		map.removeOverlay(activePolyline.gpolyline);

	activePolyline.gpolyline = new GPolyline.fromEncoded({ points:activePolyline.data, zoomFactor:32, levels:activePolyline.levels, numLevels: 4 });

	map.addOverlay(activePolyline.gpolyline);

	if (!activePolyline.startMarker)
	{
		activePolyline.startMarker = new GMarker(point, dotIcon);
		activePolyline.startMarker.yoMarkerType = 'polyline-endpoint';
		map.addOverlay(activePolyline.startMarker);
	}
	else if (!activePolyline.endMarker)
	{
		activePolyline.endMarker = new GMarker(point, dotIcon);
		activePolyline.endMarker.yoMarkerType = 'polyline-endpoint';
		map.addOverlay(activePolyline.endMarker);
	}
	else
		activePolyline.endMarker.setPoint(point);


	if (!mouseMoveListener)
		mouseMoveListener = GEvent.addListener(map, 'mousemove', showLine);
}

function mapClick(overlay, point)
{
	if (overlay && !point)
	{
		if (overlay == openMarker && !map.getInfoWindow().isHidden())
			map.getInfoWindow().hide();
		else if (overlay.yoMarkerType == 'mark')
			showBalloon(overlay);
		else if (overlay.yoMarkerType == 'polyline-endpoint')
			resetMap();
	}
	else if (point && !overlay)
	{
		if (mapMode == 'mark')
			placeMarker(point);
		else if (mapMode == 'draw')
			addPolylineVertex(point);
	}

	updateLinkToSelf();
}

function showPoint(point, address, zoom)
{
	map.setCenter(point, zoom);
	document.getElementById('bad-address').style.display = 'none';
	map.openInfoWindowHtml(point, '<div>YO! ' + address + '</div>');
}

function ontok_geocoder_onload()
{
	if (ontok_geocoder[0]) 
	{
		var s = ontok_geocoder[0].split(";");
		if (s.length > 4) 
		{
			var s_long = Number(s[0]);
			var s_lat = Number(s[1]);
			var s_description = s[2];
			var s_q = s[3]; 
			var s_score = s[4];

			if (s_lat != 0 || s_long != 0)
			{
				showPoint(new GLatLng(s_lat, s_long), s_q, 14);
			}
			else if (s_lat == 0 && s_long == 0 && s_description != '')
			{
				call_ontok_geocoder(s_description);
			}
			else if (s_lat == 0 && s_long == 0 && s_description == '')
			{
				document.getElementById('bad-address').innerHTML = "don't know where that is";
				document.getElementById('bad-address').style.display = 'block';
			}
		}
	}
	else
	{
		document.getElementById('bad-address').innerHTML = "don't know where that is";
		document.getElementById('bad-address').style.display = 'block';
	}
}

function getZoom(googleAccuracy)
{
	if (googleAccuracy < 4)
		return googleAccuracy + 4;
	else if (googleAccuracy == 4)
		return 11;
	else
		return googleAccuracy + 7;
}

function showAddress(address)
{
	map.getInfoWindow().hide();

	var handler = function(response)
	{
		if (!response || response.Status.code != 200) 
			call_ontok_geocoder(address);
		else 
		{
			place = response.Placemark[0];
			point = new GLatLng(place.Point.coordinates[1], place.Point.coordinates[0]);
			showPoint(point, place.address, getZoom(place.AddressDetails.Accuracy));
		}
	};

	googleGeocoder.getLocations(address, handler);
}


