//Author: Toby Travis
//Project: The Long Way Down
//Date: 29 March 2007

var myMap;
var myLog;
var currLatLng;
var entireRouteButton;

addUnloadEvent(mapUnload);

/* set map default values */
var default_lat = -1.282547;
var default_lng = 36.828747;
var default_zoom = 3;
var default_maptype = G_SATELLITE_MAP;
var default_maxzoom = 14;
var default_minzoom = 2;

/*set values for the 'Entire Route' control*/
var entireroute_lat = 16.636192;
var entireroute_lng = 11.601563;
var entireroute_zoom = 2;

/* set values for 'Proposed Route' polyline */
var proproute_color = "#FEE5BD";
var proproute_width = 3;
var proproute_opacity = 1;

/* set values for 'Actual Route' polyline */
var actualroute_color = "#FF9900";
var actualroute_width = 3;
var actualroute_opacity = 1;


/*
name: getLatLng()
description: extracts lat and long data from querystring, following format e.g. ll=1.20,4.30
params: none
returns: none
*/
function getLatLng(){
	var query = location.search.substring(1);
	var pos = query.indexOf("=");
	if(pos === -1) return -1;
	var ll = query.substring(pos+1).split(",");
	return ll;
}

/*
name: mainMapInit()
description: initialises the Google Map, sets default values - e.g. zoom, map type, lat/long; defines custom and inbuilt controls to include
params: none
returns: none
*/
function mainMapInit(){

	if(!document.getElementById) return false;
	if(!document.getElementsByTagName) return false;

	/* is there a lat/lang value in the querystring - if not, load default lat/lang */
	currLatLng = getLatLng();
	
	myMap = new Map();
	myLog = new Log();
	
	myMap.maptype = default_maptype;
	myMap.zoom = default_zoom;

	if(currLatLng === -1){
		myMap.lat = default_lat;
		myMap.lng = default_lng;
	}
	else {
		myMap.lat = currLatLng[0];
		myMap.lng = currLatLng[1];
	}

	myMap.drawMap();
	
	myMap.map.maxzoom = default_maxzoom;
	myMap.map.minzoom = default_minzoom;

	myMap.map.addControl(new CustomMapControl("zoomin"));
	myMap.map.addControl(new CustomMapControl("zoomout"));
	myMap.map.addControl(new CustomMapControl("entireroute"));
	myMap.map.addControl(new CustomMapControl("placenames"));
	
	var overviewpos = new GControlPosition(G_ANCHOR_BOTTOM_RIGHT, new GSize(0,0));
	myMap.map.addControl(new GOverviewMapControl(new GSize(80,80)),overviewpos);
	
	myMap.drawRoute(myMap.route,proproute_color,proproute_width,proproute_opacity);
	
	myMap.retrieveEntries();
}

/*
name: mainMapInit()
description: instantiates Map class
params: none
returns: none
*/
function Map(){
	this.mapdiv = "map";
}

/*
name: Map.prototype.drawMap()
description: If browser supports googlemaps, instantiates a new GMap2 class, setting basic values - center, map type, location
params: none
returns: none
*/
Map.prototype.drawMap = function(){
	if(GBrowserIsCompatible()){
		this.map = new GMap2(document.getElementById(this.mapdiv));
		this.location = new GLatLng(this.lat,this.lng);
		this.map.setCenter(this.location, this.zoom);
		this.map.setMapType(this.maptype);
	}
	else {
		alert('Your browser is unable to support Google Maps. You will need to upgrade to a more recent browser version to view this interactive.');
	}
}

/*
name: Map.prototype.retrieveEntries()
description: Uses GXmlHttp to run AJAX request for map.xml. Parses results into either LogEntry or CountryFact objects. Draws actual route taken as a Polyline
params: none
returns: none
*/
Map.prototype.retrieveEntries = function(){
	request = GXmlHttp.create();
	request.open("GET","/longwaydown/feeds/map.xml",true);
	request.onreadystatechange = function(){

		if(request.readyState == 4 && request.status == 200){

			var xmlDoc = request.responseXML;
			
			var entries = xmlDoc.getElementsByTagName("logentry");
			var totalentries = entries.length;

			for(var i=0; i<totalentries; i++){
				var video,image;
				var id = entries[i].getAttribute("id");
				var author = entries[i].getAttribute("author");
				var lat = entries[i].getAttribute("lat");
				var lng = entries[i].getAttribute("lng");
				var zoom = myMap.zoom;
				var title = entries[i].getAttribute("title");
				var logdate = entries[i].getAttribute("date");
				var href = entries[i].getAttribute("href");
				
				var summaryel = entries[i].getElementsByTagName("summary");
				var summary = summaryel[0].firstChild.nodeValue;
				var imageel = entries[i].getElementsByTagName("image");
				if(imageel[0].firstChild && imageel[0].firstChild.nodeValue!=null){var image = imageel[0].firstChild.nodeValue;}
				var videoel = entries[i].getElementsByTagName("video");
				if(videoel[0].firstChild && videoel[0].firstChild.nodeValue!=null){var video = videoel[0].firstChild.nodeValue;}

				var mylogentry = new LogEntry();
				mylogentry.id = id;
				mylogentry.lat = lat;
				mylogentry.lng = lng;
				mylogentry.zoom = zoom;
				
				mylogentry.title = title;
				mylogentry.author = author;
				mylogentry.summary = summary;
				mylogentry.image = image;
				mylogentry.video = video;
				formatdate = logdate.split("-");
				mylogentry.date = formatdate[2] + "/" + formatdate[1] + "/" + formatdate[0];
				mylogentry.href = href;
				
				
				prev = parseInt(mylogentry.id)-1;
				next = parseInt(mylogentry.id)+1;
				mylogentry.prev = String(prev);
				mylogentry.next = String(next);
				
				mylogentry.type = "log";
				if(i==0)mylogentry.first = true;
				if(i==(totalentries-1))mylogentry.last = true;
				myLog.addLogEntry(mylogentry,totalentries);
			}
	
			var facts = xmlDoc.getElementsByTagName("fact");
			var totalfacts = facts.length;

			for(var i=0; i<totalfacts; i++){
				var id = facts[i].getAttribute("id");
				var lat = facts[i].getAttribute("lat");
				var lng = facts[i].getAttribute("lng");
				var title = facts[i].getAttribute("title");
				var href = facts[i].getAttribute("country_factfile");
				var textel = facts[i].getElementsByTagName("text");
				if(textel[0].firstChild && textel[0].firstChild.nodeValue!=null){var text = textel[0].firstChild.nodeValue;}
				var mycountryfact = new LogEntry();
				mycountryfact.id = id;
				mycountryfact.lat = lat;
				mycountryfact.lng = lng;
				mycountryfact.title = title;
				mycountryfact.text = text;
				mycountryfact.href = href;
				
				prev = parseInt(mycountryfact.id)-1;
				next = parseInt(mycountryfact.id)+1;
				mycountryfact.prev = String(prev);
				mycountryfact.next = String(next);
				
				mycountryfact.type = "factfile";
				if(i==0)mycountryfact.first = true;
				if(i==(totalfacts-1))mycountryfact.last = true;
				
				myLog.addLogEntry(mycountryfact,totalfacts);
			}
		
			myMap.drawRoute(myMap.entries,actualroute_color,actualroute_width,actualroute_opacity);
			
		
		}
		
	}
	request.send(null);
}

/*
name: Map.prototype.recenterMap()
description: uses logentry.lat and logentry.lng values to open a new Info window, moving the map to display this window
params: logentry
returns: none
*/
Map.prototype.recenterMap = function(mylogentry){
	mylogentry.showwindow();
}

/*
name: Map.prototype.travel()
description: Handles navigation between log entries on the map; calls recenterMap() and passes it the logentry row defined by the id value
params: direction,mylogentryid
returns: none
*/
Map.prototype.travel = function(type,direction,mylogentryid){
	switch(direction){
		case "prev":
		id = (Number(mylogentryid)-2);
		break;
		case "next":
		id = Number(mylogentryid);
		break;
		case "first":
		id = 0;
		break;
		case "last":
			if(type=="factfile"){
				id = (Number(myMap.lastfact)-1);
			}
			else if(type=="log"){
				id = (Number(myMap.lastentry)-1);
			}
		break;
	}
	switch(type){
		case "factfile":
		myMap.recenterMap(this.factfiles[id]);
		break;
		case "log":
		myMap.recenterMap(this.entries[id]);
		break;
	}
	
}

/*
name: Map.prototype.entireRoute()
description: Called when user clicks on the 'Entire Route' button. Resets map to zoom and lat/lng values specified
params: none
returns: none
*/
Map.prototype.entireRoute = function(){
	this.lat = entireroute_lat;
	this.lng = entireroute_lng;
	this.zoom = entireroute_zoom;
	this.map.closeInfoWindow();
	this.location = new GLatLng(this.lat,this.lng);
	this.map.setCenter(this.location,this.zoom);
}

/*
name: Map.prototype.route()
description: Coordinates of vague 'overview' route
params: none
returns: none
*/
Map.prototype.route = [
/*
	{
	lat : 53.278353,
	lng : -1.757812
	},
	{
	lat : 43.552529,
	lng : 7.017517
	},
	{
	lat : 38.118352,
	lng : 13.384781
	},
	{
	lat : 36.834294,
	lng : 10.219345
	},
	{
	lat : 33.100745,
	lng : 9.272461
	},
	{
	lat : 29.688053,
	lng : 19.379883
	},
	{
	lat : 30.051265,
	lng : 31.238251
	},
	{
	lat : 12.028576,
	lng : 37.298584
	},
	{
	lat : -1.296619,
	lng : 36.811752
	},
	{
	lat : -1.668656,
	lng : 29.227753
	},
	{
	lat : -13.979047,
	lng : 33.784676
	},
	{
	lat : -20.6996,
	lng : 14.985352
	},
	{
	lat : -29.878755,
	lng : 24.213867
	}
*/
	{
	lat: 53.278353,
	lng: -1.757812
	},
	{
	lat: 47.100045,
	lng: 2.724609
	},
	{
	lat: 41.896144026994854,
	lng: 12.48870849609375
	},
	{
	lat: 36.82687474287727,
	lng: 10.17333984375
	},
	{
	lat: 32.91648534731439,
	lng: 13.18359375
	},
	{
	lat: 30.031055426540206,
	lng: 31.2451171875
	},
	{
	lat: 15.623036831528264,
	lng: 32.4755859375
	},
	{
	lat: 9.037002898469422,
	lng: 38.73779296875
	},
	{
	lat: -1.4061088354351594,
	lng: 36.7822265625
	},
	{
	lat: 0.17578097424708533,
	lng: 32.5634765625
	},
	{
	lat: -2.855263,
	lng: 23.554688
	},
	{
	lat: -1.944207,
	lng: 29.970703
	},
	{
	lat: -3.348922,
	lng: 29.932251
	},
	{
	lat: -6.293459,
	lng: 35.200195
	},
	{
	lat: -12.961736,
	lng: 33.903809
	},
	{
	lat: -18.145852,
	lng: 35.727539
	},
	{
	lat: -14.626109,
	lng: 26.213379
	},
	{
	lat: -21.616579,
	lng: 24.213867
	},
	{
	lat: -24.006326,
	lng: 16.875
	},
	{
	lat: -29.878755,
	lng: 24.213867
	},
	{
	lat: -33.938802,
	lng: 18.544921
	}
];





/*
name: Map.prototype.drawRoute()
description: adds a new Polyline to the map, requires route details (as JS object), colour (as upper case hex), width (as pixels), opacity (as 0-1 e.g. 0.6)
params: route,colour,width,opacity
returns: none
*/
Map.prototype.drawRoute = function(route,colour,width,opacity){
	var latlngs = [];
	for(ids in route){
		var routeloc = new GLatLng(parseFloat(route[ids].lat),parseFloat(route[ids].lng));
		latlngs.push(routeloc);
	}
	var routeline = new GPolyline(latlngs,colour,width,opacity);
	myMap.map.addOverlay(routeline);
}


/*
name: Log()
description: instantiates Log object
params: none
returns: none
*/
function Log(){
	myMap.entries = [];
	myMap.factfiles = [];
}

/*
name: Log.prototype.addLogEntry 
description: pushes each LogEntry object into the MyMap.entries array; creates marker for each log entry, defines which InfoWindow to open first
params: mylogentry,totalentries
returns: none
*/
Log.prototype.addLogEntry = function(mylogentry,totalentries){
	
	var currLat, currLng;
	currLat = currLatLng[0];
	currLng = currLatLng[1];

	if(mylogentry.type=="log"){
		myMap.entries.push(mylogentry);
		if(myMap.entries.length == totalentries){
			myMap.lastentry = mylogentry.id;
		}
	}
	else if(mylogentry.type=="factfile"){
		myMap.factfiles.push(mylogentry);
		if(myMap.factfiles.length == totalentries){
			myMap.lastfact = mylogentry.id;
		}
	}

	mylogentry.createMarker();

	
	if( mylogentry.lat==currLat && mylogentry.lng==currLng ){
		myMap.recenterMap(mylogentry);
	}
	else if((myMap.entries.length == totalentries)&&(currLatLng === -1)){
		myMap.recenterMap(mylogentry);
	}

	
}


/*
name: LogEntry 
description: instantiates new LogEntry object
params: none
returns: none
*/
function LogEntry(){
	this.first = false;
	this.last = false;
}

/*
name: LogEntry.prototype.Icon  
description: instantiates new GIcon object and assigns an icon image depending on author and whether it is the most recent entry
params: logentry
returns: none
*/
LogEntry.prototype.Icon = function(logentry){
	this.icon = new GIcon();
	this.icon.image = "/longwaydown/images/interface/map/log_marker.png";
	this.icon.iconSize = new GSize(20,32);
	this.icon.shadow = "/longwaydown/images/interface/map/marker_shadow.png";
	this.icon.shadowSize = new GSize(37,34);
	this.icon.iconAnchor = new GPoint(10, 32);
	this.icon.infoWindowAnchor = new GPoint(20,0);
	if(logentry.last == true) this.icon.image = "/longwaydown/images/interface/map/current_marker.png";
	if(logentry.type == "factfile") this.icon.image = "/longwaydown/images/interface/map/country_marker.png"
}

/*
name: LogEntry.prototype.formatXHTML  
description: constructs the HTML to inject into each info window. Works in conjunction with LogEntry.prototype.templates in templates.js 
params: none
returns: markerhtml
*/
LogEntry.prototype.formatXHTML = function(){
	var markerhtml = "";
	if(this.type=="log" || this.type=="factfile"){
		this.firstlink = "";
		this.prevlink = "";
		this.nextlink = "";
		this.lastlink = "";
		this.imagelink = "";
		this.videolink = "";
		if(this.first){this.prev="X";}
		if(this.last){this.next="X";}
	
		this.firstlink = this.templates["firstlink"].supplant(this);
		this.prevlink = this.templates["prevlink"].supplant(this);
		this.nextlink = this.templates["nextlink"].supplant(this);
		this.lastlink = this.templates["lastlink"].supplant(this);
					
		if(this.first == true ) {
			this.firstlink = this.templates["firstlinkoff"].supplant(this);
			this.prevlink = this.templates["prevlinkoff"].supplant(this);
		}
		if(this.last == true ) {
			this.nextlink = this.templates["nextlinkoff"].supplant(this);
			this.lastlink = this.templates["lastlinkoff"].supplant(this);
		}


		if(this.video!=""){
			this.videoimage = this.templates["videoimg_link"].supplant(this);
		}
		else{
			this.videoimage = this.templates["videoimg_nolink"].supplant(this);
		}
		
		switch(this.type){
			case "log":
			markerhtml = this.templates["logwindow"].supplant(this);
			break;
			case "factfile":
			markerhtml = this.templates["factwindow"].supplant(this);
			break;
		}

		markerhtml += this.templates["footer"].supplant(this);
	}

	return markerhtml;
}

/*
name: LogEntry.prototype.createMarker  
description: initiates a new GMarker object for each LogEntry object, adds onclick event to marker
params: none
returns: none
*/
LogEntry.prototype.createMarker = function(){

	this.visible = false;

	this.point = new GPoint(this.lng,this.lat);
	this.icon = new this.Icon(this);

	var marker = new GMarker(this.point,this.icon);
	var markerhtml = this.formatXHTML(this);
	
	this.marker = marker;
	this.markerhtml = markerhtml;

	if(this.active !== false){
		GEvent.addListener(marker,'click',
			function(){
				marker.openInfoWindowHtml(markerhtml);
			}
		);
	}
	
	this.show();

}

/*
name: LogEntry.prototype.show  
description: adds LogEntry marker to map
params: none
returns: none
*/
LogEntry.prototype.show = function(){
	if(!this.visible){
		myMap.map.addOverlay(this.marker);
		this.visible = true;
	}
}

/*
name: LogEntry.prototype.showwindow  
description: opens Info Window relating to this LogEntry object
params: none
returns: none
*/
LogEntry.prototype.showwindow = function(){
	this.marker.openInfoWindowHtml(this.markerhtml);	
}


/*
name: LogEntry.prototype.templates()
description: defines HTML for insertion in a Log Entry map info window 
params: none
returns: none
*/

/*
	logwindow : '<div class="info-window"><h3><a href="{href}">{title}</a></h3><p>Posted by {author} | {date}</p><div>{videoimage}</div><div class="map-entry-text"><p>{summary}</p></div><a href="{href}" id="map-read-more">Read More</a>',
*/

LogEntry.prototype.templates = {
	logwindow : '<div class="info-window"><h3><a href="{href}">{title}</a></h3><div class="info-window-content"><p>Posted by {author} | {date}</p><div>{videoimage}</div><div class="map-entry-text"><p>{summary}</p></div><div id="map-read-more"><a href="{href}" id="map-read-more">Read More</a></div></div>',
	footer: '<ul id="info-window-nav">{firstlink}{prevlink}{nextlink}{lastlink}</ul></div>',
	prevlink: '<li class="info-window-prev"><a href="#" onclick="myMap.travel(\'{type}\',\'prev\', {id});return false;">Prev</a></li>',
	prevlinkoff: '<li class="inactive">Prev</li>',
	nextlink: '<li class="info-window-next"><a href="#" onclick="myMap.travel(\'{type}\',\'next\', {id});return false;">Next</a></li>',
	nextlinkoff: '<li class="inactive info-window-next-off">Next</li>',
	firstlink: '<li class="info-window-prev"><a href="#" onclick="myMap.travel(\'{type}\',\'first\', {id});return false;">First</a></li>',
	firstlinkoff: '<li class="inactive info-window-prev-off">First</li>',
	lastlink: '<li class="info-window-next"><a href="#" onclick="myMap.travel(\'{type}\',\'last\');return false;">Last</a></li>',
	lastlinkoff: '<li class="inactive info-window-next-off">Last</li>',
	videoimg_link: '<div id="info-window-video"><a href="/mediaselector/check/longwaydown/video/{video}?size=16x9&amp;bgc=000000&amp;nbram=1&amp;bbram=1&amp;nbwm=1&amp;bbwm=1" onclick="window.open(this.href,this.target,\'status=no,scrollbars=yes,resizable=yes,width=409,height=269\');return false;">{image}</a></div>',
	videoimg_nolink: '<div id="info-window-video">{image}</div>',
	factwindow : '<div class="info-window"><h3><a href="{href}">{title}</a></h3><div class="info-window-content"><div class="map-entry-text">{text}</div><div id="map-read-more"><a href="{href}">Read More</a></div></div></div>'
}




/*
name: CustomMapControl()
description: instantiates CustomMapControl class, used to produce a custom Google Map control, beahviour defined by type parameter
The structure of this class is the same as for TextualZoomControl above
params: type
returns: custommapbutton
*/
function CustomMapControl(type) {
	if(!document.getElementById) return false;
	if(!document.getElementsByTagName) return false;
	this.type = type;
}

CustomMapControl.prototype = new GControl();

CustomMapControl.prototype.initialize = function(map){

	this.custommapbutton = document.createElement("div");
	this.custommapbutton.className = "mapbutton";

	switch(this.type){
	
	case "zoomin":

		this.custommapbutton.appendChild(document.createTextNode("Zoom In"));
		GEvent.addDomListener(this.custommapbutton, "click", function() {
		  	if(map.getZoom() < map.maxzoom){map.zoomIn();}
		});

	break;
	
	
	case "zoomout":
	
		this.custommapbutton.appendChild(document.createTextNode("Zoom Out"));
		GEvent.addDomListener(this.custommapbutton, "click", function() {
			if(map.getZoom() > map.minzoom){map.zoomOut();}
		});
		
	break;

	case "entireroute":

		this.custommapbutton.appendChild(document.createTextNode("Entire route"));
		GEvent.addDomListener(this.custommapbutton,"click", function(){
				myMap.entireRoute();
		});
	

	break;

	case "placenames":

		var placenamebutton = this.custommapbutton;
		var placenametext = document.createTextNode("Place Names On");
		var	placenameofftext =  document.createTextNode("Place Names Off");
		placenamebutton.appendChild(placenametext);
	
		GEvent.addDomListener(this.custommapbutton,"click", function(){
			if(placenamebutton.firstChild.nodeValue == "Place Names On"){
				myMap.map.setMapType(G_HYBRID_MAP);
				placenamebutton.replaceChild(placenameofftext,placenametext);
			}
			else{
				myMap.map.setMapType(G_SATELLITE_MAP);
				placenamebutton.replaceChild(placenametext,placenameofftext);
			}
		});

	break;
	
	}
	
	map.getContainer().appendChild(this.custommapbutton);
	return this.custommapbutton;

}

CustomMapControl.prototype.getDefaultPosition = function() {
	switch(this.type){
		case "zoomin":
		return new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(7, 7));
		break;
		case "zoomout":
		//return new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(7, 30));
		return new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(133, 7));
		break;
		case "entireroute":
		//return new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(7,60));
		return new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(259, 7));
		break;
		case "placenames":
		//return new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(7,83));
		return new GControlPosition(G_ANCHOR_TOP_RIGHT, new GSize(7, 7));
		break;
	}
}