/*
 * Surgery.js
 * (c) Copyright 2007, Martin Cowie Ltd
 * ALL RIGHTS RESERVED
 * Distribution of this file is strictly prohibited
 */

/**
 * method init: Creates a google map, loads the location data XML, and parent the map to the DIV called 'map'
 * argument: mapDivName: ID of the DIV into which the map will go
 * argument: focusSurgeryName (optional): must match a 'name' attribute of a 'Surgery' element in the XML
 */
function initMap( mapDivName, focusSurgeryName )
{
    if (false == GBrowserIsCompatible()) { 
		alert("Sorry, the Google Maps API is not compatible with this browser");
		return;
	}

	var mapDIV = document.getElementById( mapDivName );
	var container = findParentNode( mapDIV, "table" );

	// Display the map, with some controls and set the initial location 
	var map = new GMap2( mapDIV );
	map.addControl(new GLargeMapControl());
	map.enableScrollWheelZoom();
	map.addControl( new GHierarchicalMapTypeControl() );
	map.addMapType(G_PHYSICAL_MAP); // Add terrain button

	// Set the size of the map, to consume all available space and plumb in the resize hook
	resizeGUI( container, map );
	window.onresize = function() {
		resizeGUI( container, map );
	};

	// Populate map from XML document
	GDownloadUrl( "./locations.xml", function(xmlSrc) {

		// Set the dummy centre of the map -NB: setCenter MUST be called 1st
		map.setCenter(new GLatLng(0,0),0);
		
		// Create the marker manager to stop P signs appearing too soon
		var markerManager = new GMarkerManager( map, { borderPadding:1 } );

		var xml = GXml.parse( xmlSrc );
		// Add Surgery locations
		mapArray( xml.documentElement.getElementsByTagName( "surgery" ), function( locElem )
		{
			new Surgery( locElem, markerManager );
		} );

		// Add Car Parks
		mapArray( xml.documentElement.getElementsByTagName( "parking" ), function( locElem )
		{
			new CarPark( locElem, markerManager );
		} );

		// Add Points of interet
		mapArray( xml.documentElement.getElementsByTagName( "poi" ), function( locElem )
		{
			var poi = new Location( locElem );
			poi.addBalloon( substDirections( GXml.value( locElem ), poi.getCoords() ) );
			markerManager.addMarker( poi.getMarker(), 0 );
		} );

		markerManager.refresh();
		
		if( focusSurgeryName == null ) 
		{
			// Set the map to include all the locations 
			map.setCenter( Surgery.bounds.getCenter());
			map.setZoom( map.getBoundsZoomLevel( Surgery.bounds ) );
		} else {
			// Focus on the named location only
			var focusSurgery = Surgery.byName[focusSurgeryName];
			if( focusSurgery == null )
			{
				alert( "Unknown location \"" + focusSurgeryName + "\" - silly developer mistake" );
				return;
			}
			map.setCenter( focusSurgery.getCoords() );
			map.setZoom( 15 );
		}
	} );
}
 
// The Coastway marker icon
var cwayIcon = new GIcon();
cwayIcon.image = "/images/css/marker.png";
cwayIcon.shadow = "http://www.google.com/mapfiles/shadow50.png";
cwayIcon.iconSize = new GSize(20, 34);
cwayIcon.shadowSize = new GSize(37, 34);
cwayIcon.iconAnchor = new GPoint(9, 34);
cwayIcon.infoWindowAnchor = new GPoint(9, 2);
cwayIcon.infoShadowAnchor = new GPoint(18, 25);

/**
 * Constructor for the Surgery class 
 * argument xmlElem: xml fragment describing this location
 */
function Surgery( xmlElem, markerManager )
{
	// Call parent constructor
	Location.call( this, xmlElem, cwayIcon );

	this.enableMarker();

	// Surgeries to appear at all altitudes
	markerManager.addMarker( this.marker, 0 );

	// Add this item to the bounds
	Surgery.bounds.extend( this.coords );

	// Stick this new object in the 'by name' lookup table
	Surgery.byName[this.name] = this;
}

// Derive Surgery from Location
Surgery.prototype = new Location();

Surgery.prototype.enableMarker= function()
{
	// Wire up a pop up ballon to this marker
	var myLoc = this;
	var tabs = this.xml.getElementsByTagName( "tab" );
	var windowTabs = mapArray( tabs, function( tabRec ) {
		var label = tabRec.getAttribute( "name" );
		var content = substDirections( GXml.value( tabRec ), myLoc.getCoords() );
		return new GInfoWindowTab( label, content );
	} );
	GEvent.addListener( this.getMarker(), "click", function() {
		this.openInfoWindowTabsHtml( windowTabs );
	});
}

Surgery.byName= new Object(); // map of Surgerys by their name
Surgery.bounds= new GLatLngBounds();

/* =========================== */
/* End of Surgery definition   */
/* =========================== */

function findParentNode( node, parentTag )
{
	if( node.tagName.toLowerCase() == parentTag.toLowerCase() ) return node;
	return findParentNode( node.parentNode, parentTag );
}

var markerRE = /\$\{(.+?)\}/;

/**
 * A simple & elegant function to swap ${markers} for values
 */
function substMarkers( src, valueMap )
{
	while( src.match( markerRE ) )
	{
		src = src.replace( markerRE, function( $0, $1 ) { 
			return valueMap[$1];
		}  );
	}
	return src;
}

/**
 *  Looks for & replace common place-holders in the HTML text
 */
function substDirections( src, point )
{
	var driveURL = "http://maps.google.co.uk/maps?saddr=&daddr=" + point.toUrlValue();
	var busURL = "http://maps.google.co.uk/maps?dirflg=r&saddr=&daddr=" + point.toUrlValue();
	var directionsHTML = "<hr><a href=\""+ driveURL +"\">Drive</a> or <a href=\""+ busURL +"\">get the bus here</a>"
	return substMarkers( src, { 
		directions: directionsHTML,
		author: "Martin Cowie"
	} );
}

// Horrors of IE vs the world ...
function getWindowDimensions()
{
	if( typeof( window.innerWidth ) == 'number' ) 
		return { width: window.innerWidth, height: window.innerHeight };

	//IE 6+ in 'standards compliant mode'
	if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) )
		return { width: document.documentElement.clientWidth, height: document.documentElement.clientHeight };

	//IE 4 compatible
	if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) 
		return { width: document.body.clientWidth, height: document.body.clientHeight };

	alert( "Cannot get the dimensions of this browser" );
}

function resizeGUI( domElem, mapObject )
{
	// 1. resize the table container
	var win = getWindowDimensions();
	var mapCoords = getAbsCoords( domElem );
	domElem.style.height = ( win.height - mapCoords.top )+ "px";

	// 2. Get the map to check itself
	mapObject.checkResize();
}

function getAbsCoords( div )
{
	if( div == null )
	{
		return { left: 0, top: 0 }
	} else {
		var parentCoords = getAbsCoords( div.offsetParent );
		return { 
			left: div.offsetLeft + parentCoords.left, 
			top: div.offsetTop + parentCoords.top 
		};
	}
}

/**
 * Map function 'fun' onto the array 'arr' and return the output as an array
 * ... which saves alot of tedious iteration
 * TODO: why will this NOT go into the Object prototype in IE?
 */
function mapArray( arr, fun )
{
	var len = arr.length;
	if (typeof fun != "function")
		throw new TypeError();
	
	var res = new Array( len );
	for (var i = 0; i < len; i++)
		res[i] = fun( arr[i] );
	
	return res;
};