function getUrlArg( name )
{
	var assarr = location.search.substr(1).split("&");
	var i;
	for ( i=0; i<assarr.length; i++)
	{
		if ( name == assarr[i].split("=")[0] )
		{
			return assarr[i].split("=")[1];
		}
	}
	return name=="filename" ? "libie.gpx" : "";
}

function getTextContent(el)
{
	if (el.textContent) return el.textContent;
	if (el.innerText) return (el.innerText);

	var cNode, cNodes = el.childNodes;
	var txt = '';
	for (var i=0, len=cNodes.length; i<len; ++i){
		cNode = cNodes[i];
		if (1 == cNode.nodeType) {
			txt += getTextContent(cNode);
		}
		if (3 == cNode.nodeType){
			txt += cNode.data;
		}
	}
	return txt;
}

function getNextSibling( elem ) 
{
	elem = elem.nextSibling;
	while ( elem && elem.nodeType != 1 )
		elem = elem.nextSibling;
	return elem;
}

function skipTo( elem, name ) 
{
	while ( elem && elem.nodeName != name )
		elem = getNextSibling( elem );

	return elem;
}

function getFirstChild( elem ) 
{
	if ( elem )
		elem = elem.firstChild;
	while ( elem && elem.nodeType != 1 )
		elem = elem.nextSibling;
	return elem;
}

function GPXTrack()
{
	this.name = "???";
	this.desc = "";
	this.overlay = null;
	this.points = new Array();
	this.colour = "#ff0000"; // red
	this.width = 3;
}

GPXTrack.prototype.display = function( map )
{
	if ( this.overlay == null )
	{
		var polyline = new GPolyline(this.points, this.colour, this.width,1);
		map.addOverlay(polyline);
		this.overlay = polyline;
	}
	else
	{
		map.removeOverlay( this.overlay );
		this.overlay = null;
	}
}

function GPXRoute()
{
	this.name = "???";
	this.desc = "";
	this.overlay = null;
	this.points = new Array();
	this.colour = "#0000ff"; // blue
	this.width = 3;
}

GPXRoute.prototype.display = function( map )
{
	if ( this.overlay == null )
	{
		var polyline = new GPolyline(this.points, this.colour, this.width,1);
		map.addOverlay(polyline);
		this.overlay = polyline;
	}
	else
	{
		map.removeOverlay( this.overlay );
		this.overlay = null;
	}
}

function GPXWaypoint()
{
	this.name = "???";
	this.desc = "";
	this.lat = 0;
	this.lon = 0;
	this.ele = 0;
	this.overlay = null;
}

GPXWaypoint.prototype.display = function( map )
{
	if ( this.overlay == null )
	{
		var marker = new GMarker(new GLatLng(this.lat,this.lon));
		GEvent.addListener(marker, "click",
			function()
			{
                                marker.showMapBlowup(3, G_SATELLITE_TYPE);
				// marker.openInfoWindowHtml(this.desc);
			}
		);
		map.addOverlay(marker);
		this.overlay = marker;
	}
	else
	{
		map.removeOverlay( this.overlay );
		this.overlay = null;
	}
}

function parseEmail(elem)
{
	var id = elem.getAttribute("id");
	var domain = elem.getAttribute("domain");
	return this.to+"@"+this.domain;
}


function Link(elem)
{
	this.href = elem.getAttribute("href");
	this.text = "";
	this.type = "";

	elem = getFirstChild( elem );

	if ( elem && elem.nodeName == "text" )
	{
		this.text = getTextContent(elem);
		elem = getNextSibling( elem );
	}
	if ( elem && elem.nodeName == "type" )
	{
		this.type = getTextContent(elem);
		elem = getNextSibling( elem );
	}
}

function Person(elem)
{
	this.name = "";
	this.email = "";
	this.link = "";

	var elem = getFirstChild( elem );

	if ( elem && elem.nodeName == "name" )
	{
		this.name = getTextContent(elem);
		elem = getNextSibling( elem );
	}
	if ( elem && elem.nodeName == "email" )
	{
		this.email = parseEmail(elem);
		elem = getNextSibling( elem );
	}
	if ( elem && elem.nodeName == "link" )
	{
		this.link = new Link(elem);
		elem = getNextSibling( elem );
	}
}

function MetaData()
{
	this.name = "";
	this.desc = "";
	this.author = null;
	this.link = null;
	this.time = null;
	this.bounds = null;
	this.center = null;
	this.keywords = "";
}

MetaData.prototype.parseBounds = function(elem)
{
	var minlat = elem.getAttribute("minlat");
	var minlon = elem.getAttribute("minlon");
	var maxlat = elem.getAttribute("maxlat");
	var maxlon = elem.getAttribute("maxlon");

	// Center around the middle of the points
	var centerlon = (maxlon + minlon) / 2;
	var centerlat = (maxlat + minlat) / 2;

	this.bounds = new GLatLngBounds(new GLatLng(minlat, minlon),
						  new GLatLng(maxlat, maxlon));
	this.center = new GLatLng(centerlat, centerlon);
}

MetaData.prototype.parse = function(metadoc)
{
	if ( ! metadoc.hasChildNodes() ) return;

	var elem = getFirstChild( metadoc );

	if ( elem && elem.nodeName == "name" )
	{
		this.name =  getTextContent(elem);
		elem = getNextSibling( elem );
	}
	if ( elem && elem.nodeName == "desc" )
	{
		this.desc =  getTextContent(elem);
		elem = getNextSibling( elem );
	}
	if ( elem && elem.nodeName == "author" )
	{
		this.author = new Person(elem);
		elem = getNextSibling( elem );
	}
	if ( elem && elem.nodeName == "copyright" )
	{
		elem = getNextSibling( elem );
	}
	if ( elem && elem.nodeName == "link" )
	{
		this.link = new Link(elem);
		elem = getNextSibling( elem );
	}
	if ( elem && elem.nodeName == "time" )
	{
		this.time =  getTextContent(elem);
		elem = getNextSibling( elem );
	}
	if ( elem && elem.nodeName == "keywords" )
	{
		this.keywords = getTextContent(elem);
		elem = getNextSibling( elem );
	}
	if ( elem && elem.nodeName == "bounds" )
	{
		this.bounds = this.parseBounds(elem);
		elem = getNextSibling( elem );
	}
	if ( elem && elem.nodeName == "extensions" )
	{
		elem = getNextSibling( elem );
	}
}

function TOCParser(elem)
{
	var elem = getFirstChild( elem );
	elem = skipTo( elem, "toc" );

	return elem == null ? null : TOCEntry( elem );
}

function TOCEntry(elem)
{
	var entries = new Array();

	entries.push( elem.getAttribute("name") );
	//this.icon = elem.getAttribute("icon");
	entries.push( elem.getAttribute("url") );
	//this.target = elem.getAttribute("target");

	var child = getFirstChild( elem );
	while ( child != null )
	{
		entries.push( TOCEntry( child ) );
		child = getNextSibling( child );
	}
	return entries;
}

function GPXParser(xmlDoc)
{
	this.metadata = new MetaData();
	this.trcarr = new Array(); // Contains GPXTracks
	this.rtearr = new Array(); // Contains GPXRoutes
	this.wptarr = new Array(); // Contains GPXWaypoints

	var elem = getFirstChild( xmlDoc );
	elem = skipTo( elem, "gpx" );

	if ( elem == null ) return;

	this.version = elem.getAttribute("version"); // latest 1.1
	this.creator = elem.getAttribute("creator");

	elem = getFirstChild( elem );

	if ( elem && elem.nodeName == "metadata" )
	{
		this.metadata.parse(elem);
		elem = getNextSibling( elem );
	}

	while ( elem && elem.nodeName != "wpt" &&
			elem.nodeName != "rte" &&
			elem.nodeName != "trk" )
		elem = getNextSibling( elem );
	
	while ( elem && elem.nodeName == "wpt" )
	{
		var wpt = this.createWaypoint(elem);
		if ( wpt != null )
			this.wptarr.push(wpt);
		elem = getNextSibling( elem );
	}

	while ( elem && elem.nodeName == "rte" )
	{
		var rte = this.createRoute(elem);
		if ( rte != null )
			this.rtearr.push(rte);
		elem = getNextSibling( elem );
	}

	while ( elem && elem.nodeName == "trk" )
	{
		var trc = this.createTrack(elem);
		if ( trc != null )
			this.trcarr.push( trc );
		elem = getNextSibling( elem );
	}

	this.SetCenterAndBounds(xmlDoc);
}

GPXParser.prototype.centerZoom = function (map,maptype)
{
	map.setCenter(this.metadata.center, map.getBoundsZoomLevel(this.metadata.bounds), maptype);
}


GPXParser.prototype.display = function(map,maptype)
{
	map.clearOverlays();
	this.centerZoom(map,maptype);

	for ( i = 0; i < this.trcarr.length; i++ )
	{
		trc = this.trcarr[i];
		trc.display( map );
	}
}

GPXParser.prototype.createRoute = function(rtedoc)
{
	if ( ! rtedoc.hasChildNodes() ) return null;

	var rte = new GPXRoute();
	var elem = getFirstChild( rtedoc );

	if ( elem && elem.nodeType != 1) 
		elem = getNextSibling(elem);

	if ( elem && elem.nodeName == "name" )
	{
		rte.name =  getTextContent(elem);
		elem = getNextSibling( elem );
	}
		
	if ( elem && elem.nodeName == "cmt" ) // optional
	{
		elem = getNextSibling( elem );
	}
	
	if ( elem && elem.nodeName == "desc" ) // optional
	{
		rte.desc = getTextContent(elem);
		elem = getNextSibling( elem );
	}
	
	if ( elem && elem.nodeName == "src" ) // optional
	{
		elem = getNextSibling( elem );
	}
	
	if ( elem && elem.nodeName == "link" ) // optional
	{
		elem = getNextSibling( elem );
	}

	if ( elem && elem.nodeName == "number" ) // optional
	{
		elem = getNextSibling( elem );
	}

	if ( elem && elem.nodeName == "type" ) // optional
	{
		elem = getNextSibling( elem );
	}

	if ( elem && elem.nodeName == "extensions" ) // optional
	{
		elem = getNextSibling( elem );
	}
	while ( elem && elem.nodeName != "rtept" )
	{
		elem = getNextSibling( elem );
	}

	while ( elem && elem.nodeName == "rtept" )
	{
		var wpt = this.createWaypoint(elem);
		var latlng = new GLatLng(wpt.lat,wpt.lon);
		rte.points.push(latlng);
		elem = getNextSibling( elem );
	}

	return rte;
}

GPXParser.prototype.createWaypoint = function(elem)
{
	var wpt = new GPXWaypoint();
	wpt.lon = parseFloat(elem.getAttribute("lon"));
	wpt.lat = parseFloat(elem.getAttribute("lat"));

	var elem = getFirstChild(elem);

	if ( elem && elem.nodeName == "ele" )
	{
		this.ele = getTextContent(elem);
		elem = getNextSibling(elem);
	}
	if ( elem && elem.nodeName == "time" )
	{
		this.time = getTextContent(elem);
		elem = getNextSibling(elem);
	}
	if ( elem && elem.nodeName == "magvar" )
	{
		elem = getNextSibling(elem);
	}
	if ( elem && elem.nodeName == "geoidheight" )
	{
		elem = getNextSibling(elem);
	}
	if ( elem && elem.nodeName == "name" )
	{
		wpt.name = getTextContent(elem);
            wpt.desc = wpt.name;
		elem = getNextSibling(elem);
	}
	if ( elem && elem.nodeName == "cmt" )
	{
		elem = getNextSibling(elem);
	}
	if ( elem && elem.nodeName == "desc" )
	{
		wpt.desc = getTextContent(elem);
		elem = getNextSibling(elem);
	}
	if ( elem && elem.nodeName == "src" )
	{
		elem = getNextSibling(elem);
	}
	if ( elem && elem.nodeName == "link" )
	{
		elem = getNextSibling(elem);
	}
	if ( elem && elem.nodeName == "sym" )
	{
		elem = getNextSibling(elem);
	}
	if ( elem && elem.nodeName == "type" )
	{
		elem = getNextSibling(elem);
	}
	if ( elem && elem.nodeName == "fix" )
	{
		elem = getNextSibling(elem);
	}
	if ( elem && elem.nodeName == "sat" )
	{
		elem = getNextSibling(elem);
	}
	if ( elem && elem.nodeName == "hdop" )
	{
		elem = getNextSibling(elem);
	}
	if ( elem && elem.nodeName == "vdop" )
	{
		elem = getNextSibling(elem);
	}
	if ( elem && elem.nodeName == "pdop" )
	{
		elem = getNextSibling(elem);
	}
	if ( elem && elem.nodeName == "ageofdgpsdata" )
	{
		elem = getNextSibling(elem);
	}
	if ( elem && elem.nodeName == "dgpsid" )
	{
		elem = getNextSibling(elem);
	}
	if ( elem && elem.nodeName == "extensions" )
	{
		elem = getNextSibling(elem);
	}

	return wpt;
}


GPXParser.prototype.addTrackSegment = function(trc,trackSegment)
{
	var trackpoints = trackSegment.getElementsByTagName("trkpt");
	if (trackpoints.length == 0) return;

	// process first point
	var lastlon = parseFloat(trackpoints[0].getAttribute("lon"));
	var lastlat = parseFloat(trackpoints[0].getAttribute("lat"));
	var latlng = new GLatLng(lastlat,lastlon);
	trc.points.push(latlng);

	for (var i=1; i < trackpoints.length; i++)
	{
		var lon = parseFloat(trackpoints[i].getAttribute("lon"));
		var lat = parseFloat(trackpoints[i].getAttribute("lat"));

		// Verify that this is far enough away from the last point to be used.
		var latdiff = lat - lastlat;
		var londiff = lon - lastlon;
		if ( Math.abs(latdiff) > 0.0002 ||
		     Math.abs(londiff) > 0.0002 )
		{
			lastlon = lon;
			lastlat = lat;
			latlng = new GLatLng(lat,lon);
			trc.points.push(latlng);
		}
	}
}

GPXParser.prototype.createTrack = function(track)
{
	var trc = new GPXTrack();
	var trcname = track.getElementsByTagName("name"); // optional gps name string
	trc.name = trcname.length > 0 ? getTextContent(trcname[0]) : "???";

	//var trackCmt = track.getElementsByTagName("cmt"); // optional gps comment string
	var trcdesc = track.getElementsByTagName("desc"); // optional gps description string
	trc.desc = trcdesc.length > 0 ? getTextContent(trcdesc[0])  : "";
	//alert( "Track "+ trc.name + " " + trcdesc + " " + trc.desc );
	//var trackSrc = track.getElementsByTagName("src"); // optional source (reliability) string
	//var trackLink = track.getElementsByTagName("link"); // 0 or more links
	//var trackNumb = track.getElementsByTagName("number"); // optional gps number >=0
	//var trackType = track.getElementsByTagName("type"); // optional gps type string
	
	var segments = track.getElementsByTagName("trkseg");
	for (var i=0; i < segments.length; i++)
	{
		this.addTrackSegment(trc,segments[i]);
	}

	return trc;
}

GPXParser.prototype.SetCenterAndBounds = function(trackSegment)
{
	var pointlist = new Array("trkpt", "rtept", "wpt");
	var minlat = 0;
	var maxlat = 0;
	var minlon = 0;
	var maxlon = 0;

	for (var pointtype=0; pointtype < pointlist.length; pointtype++)
	{

		// Center the map and zoom on the given segment.
		var trackpoints = trackSegment.getElementsByTagName(pointlist[pointtype]);

		// If the min and max are uninitialized then initialize them.
		if ( (trackpoints.length > 0) && (minlat == maxlat) && (minlat == 0) )
		{
			minlat = parseFloat(trackpoints[0].getAttribute("lat"));
			maxlat = parseFloat(trackpoints[0].getAttribute("lat"));
			minlon = parseFloat(trackpoints[0].getAttribute("lon"));
			maxlon = parseFloat(trackpoints[0].getAttribute("lon"));
		}

		for (var i=1; i < trackpoints.length; i++)
		{
			var lon = parseFloat(trackpoints[i].getAttribute("lon"));
			var lat = parseFloat(trackpoints[i].getAttribute("lat"));

			if (lon < minlon) minlon = lon;
			if (lon > maxlon) maxlon = lon;
			if (lat < minlat) minlat = lat;
			if (lat > maxlat) maxlat = lat;
		}
	}

	if ( (minlat == maxlat) && (minlat == 0) ) return;

	// Center around the middle of the points
	var centerlon = (maxlon + minlon) / 2;
	var centerlat = (maxlat + minlat) / 2;

	this.metadata.bounds = new GLatLngBounds(new GLatLng(minlat, minlon), new GLatLng(maxlat, maxlon));
	this.metadata.center = new GLatLng(centerlat, centerlon);
}


