// this is a bad way...
var svgNS="http://www.w3.org/2000/svg";

var vml=window.external ? true : false;
var startTime=new Date();
var graphPoints=[];
var startEle=0;
var timeOffset=0;
var previousEndTime=0;
var totalDistance=0;
var previousPoint;
var SPEED=750;
var minEle=10000;
var maxEle=0;
var polylines=[];
var trackXMLdocs={files:[],processed:[]};
var map;

function getDragParent(el) {
	var oldEl=el;
	while (el) {
		el=el.parentNode;
		if (el.id=="maptable" || el.nodeName.toUpperCase()=='BODY') {
			return oldEl;
		}
		oldEl=el;
	}
}
draggingThing=null;
function startDrag(e) {
	draggingThing=getDragParent(e.srcElement || e.target);
	offsetX=e.clientX-draggingThing.offsetLeft;
	offsetY=e.clientY-draggingThing.offsetTop;
	document.body.onmousemove=moveDrag;
	document.body.onmouseup=endDrag;
	document.onselectstart=nullFunc;
}

function nullFunc(e) {
	return false;
}
function moveDrag(e) {
	e=e || event;
	if (draggingThing) {
		draggingThing.style.top=(e.clientY-offsetY)+'px';
		draggingThing.style.left=(e.clientX-offsetX)+'px';
		return true;
	}
}
function endDrag(e) {
	draggingThing=null;
	document.body.onmousemove=null;
	document.body.onmouseend=null;
	document.onselectstart=null;
}

function getHeightWidth() {
	var winWidth=800;
	var winHeight=600;
	var	d=document;
	if (typeof window.innerWidth!='undefined') {
		var winWidth = window.innerWidth;
		var winHeight = window.innerHeight;
	} else {
		if (d.documentElement &&
				typeof d.documentElement.clientWidth!='undefined' &&
				d.documentElement.clientWidth!=0) {
			var winWidth = d.documentElement.clientWidth;
			var winHeight = d.documentElement.clientHeight;
		} else {
			if (d.body &&
					typeof d.body.clientWidth!='undefined') {
				var winHeight = d.body.clientHeight;
				var winWidth = d.body.clientWidth;
			}
		}
	}
	return {width:winWidth,height:winHeight};
}
function sizer() {
	var size=getHeightWidth();
			mapWidth=size.width-25;
			mapHeight=size.height-25;
			var mt=document.getElementById('map').style;
			mt.height=mapHeight+"px";
			mt.width=mapWidth+"px";
}



function HTTP() {
 var xmlhttp
   try {
   xmlhttp=new ActiveXObject("Msxml2.XMLHTTP")
  } catch (e) {
   try {
     xmlhttp=new ActiveXObject("Microsoft.XMLHTTP")
   } catch (E) {
    xmlhttp=false
   }
  }
 if (!xmlhttp) {
  try {
   xmlhttp = new XMLHttpRequest();
  } catch (e) {
		xmlhttp=false;
  }
 }
 return xmlhttp;
}

function getGPX(file) {
    document.getElementById('info').innerHTML='<center><span style="font-weight:bold">Loading, please wait...</span>';
	var xmlhttp=HTTP();
	xmlhttp.open("GET", file, true);
	xmlhttp.onreadystatechange=function() {
		if (xmlhttp.readyState==4) {
			var doc=xmlhttp.responseXML;
			if (!doc) {
    			document.getElementById('info').innerHTML='<center><span style="font-weight:bold">hmm, cannot load<br>' + file +' </span>';
    			return;
			}

			if (doc.documentElement) {
				var trks=doc.getElementsByTagName('trk');
				if (trks.length>0) {
					trackXMLdocs.files.push(doc);
					var trkRef=trackXMLdocs.files.length-1;
					var	output=[];
					for (var i=0; i< trks.length; i++) {
						// setTimeout(new Function("trackProcess("+trkRef+","+i+")"),10);
						var trk=trks[i];
						var name=trk.getElementsByTagName('name');
						if (name[0]) {
							name=name[0].firstChild.nodeValue;
						} else {
							name="unnammed";
						}
						var trkpt=trk.getElementsByTagName('trkpt');
						var tracklength=trkpt.length;
						if (!previousPoint) {
							try {
								pt=trkpt.item(0);
								previousPoint=new GPoint(pt.getAttribute('lon'),pt.getAttribute('lat'));
								try {
									startTime=new Date(parseDate(pt.getElementsByTagName('time')[0].firstChild.nodeValue));
								} catch (e) {}
								try {
									startEle=Number(pt.getElementsByTagName('ele')[0].firstChild.nodeValue);
								} catch (e) {}
							}catch (e) {}
						}
						// output.push('<span onclick="showTrack('+trkRef+','+i+')">'+name+'('+tracklength+' points)</span><br>');
                        document.getElementById('info').innerHTML='<center><span style="font-weight:bold"> loaded' + i +' points</span>';

					}
					trackProcess(0,0);
					TP.onLoaded(trackXMLdocs.processed[0][0]);
				} else {
					document.getElementById('info').innerHTML="No track logs found";
				}
			} else {
				document.getElementById('info').innerHTML="No track logs found";
			}
		}
	}
	xmlhttp.send(null)
}

function trackProcess(file,trackID) {
	if (!trackXMLdocs.processed[file]) {
		trackXMLdocs.processed[file]=[];
	}
	var trk=[];
	try {
	  var trackName = trackXMLdocs.files[file].getElementsByTagName('trk')[0].getElementsByTagName('name')[0].firstChild.nodeValue;
      TP.trackName = trackName;
	} catch (e) {
	  TP.trackName = 'unnamed track';
	}

	try {
	var	track=trackXMLdocs.files[file].getElementsByTagName('trk').item(trackID);

	var trackPts=track.getElementsByTagName('trkpt');
	if (trackPts.length>0) {
		if (previousEndTime!=0) {
			try {
				var pt=trackPts.item(i);
				var time=new Date(parseDate(pt.getElementsByTagName('time')[0].firstChild.nodeValue));
				timeOffset+=time.valueOf()-previousEndTime;
			} catch (e) {}
		}

	    document.getElementById('info').innerHTML='Processing ' + trackPts.length + ' points...';
		for (var i=0;i<trackPts.length;i++) {
			var pt=trackPts.item(i);

			try {
				var ele=Number(pt.getElementsByTagName('ele')[0].firstChild.nodeValue);
			} catch (e) {
				var ele=0;
			}

			try {
				var time=new Date(parseDate(pt.getElementsByTagName('time')[0].firstChild.nodeValue));
			} catch (e) {
				var time=new Date();
			}

			var pt = new GPoint(pt.getAttribute('lon'), pt.getAttribute('lat'));
			var distance = GCdistanceBetween(pt,previousPoint);

			// Just: prevent NaN ??
			if (!isNaN(distance)) {
				totalDistance += distance;
				previousPoint = pt;
			}
			pt.ele=ele;
			if (ele>maxEle) maxEle=ele;
			if (ele<minEle) minEle=ele;
			pt.time=time;
			pt.elapsedTime = new Date(time.valueOf()-timeOffset);
			pt.distance = totalDistance;
			TV.calcSpeed(pt);
			trk.push(pt);
		}
		trackXMLdocs.processed[file][trackID]=trk;
		previousEndTime=pt.time.valueOf();
	}
	} catch (e) {
		trackXMLdocs.processed[file][trackID]=false;
	}
}

function showTrack(file,track) {
	if (!trackXMLdocs.processed[file] || !trackXMLdocs.processed[file][track]) {
		trackProcess(file,track);
	}
	if (document.getElementById('timeSequence').checked) {
		setTimeout(function() {drawTrackSeqUpd(file,track,1,10) },SPEED)
	} else {
		drawTrackSeq(file,track,1,trackXMLdocs.processed[file][track].length-1);
	}
}



function drawTracks(file,track) {
	if (!trackXMLdocs.processed[file] || !trackXMLdocs.processed[file][track]) {
		trackProcess(file,track);
	}
	if (trackXMLdocs.processed[file][track]!=false) {
		setTimeout(function() { drawTrackSeqUpd(file,track,1,10,function()
		{			betweenTrackLines(file,track+1);drawTracks(file,track+1)})},100);
	} else {
		document.getElementById('info').innerHTML=	document.getElementById('info').innerHTML+'<br><center><span onclick="PlayAll()" style="font-weight:bold">[View Route]</span></center>';
	}
}

function drawTrackSeqUpd(file,track,start,end,callbackEnd) {
	var tl=trackXMLdocs.processed[file][track].length;
	if (start < tl) {
		drawTrackSeq(file,track,start,(end>=tl ? tl-1 : end));
		setTimeout(function() {
		  drawTrackSeqUpd(file, track, end, end+20, callbackEnd)
		},SPEED);
	} else {
		if (callbackEnd) {
			callbackEnd();
		}
	}
}

function drawTrackSeq(file,track,start,end) {
	var	track=trackXMLdocs.processed[file][track];
	var colour=getColour(track[end].ele);
//	addToGraph(track[end].ele);
	var arr=track.slice(start-1,end);
	var pt=drawPolyline(arr, new GPolyline(arr,colour,4,0.5),arr[arr.length-1]);
//	document.getElementById('info').innerHTML="<center>"+(pt.distance.toFixed(2))+"miles "+FmtTime(pt.time.valueOf()-startTime.valueOf())+"</center>"+"<center>"+(pt.distance.toFixed(2))+"miles "+FmtTime(pt.elapsedTime.valueOf()-startTime.valueOf())+"</center>";
    var timeLapse = pt.time.valueOf()-startTime.valueOf();
    var speed = pt.speed; // TV.calcSpeed(pt);
    var maxSpeed = TV.calcMaxSpeed(arr);
	document.getElementById('info').innerHTML='totaldist ' + totalDistance.toFixed(2) + ' km<br>dist ' + pt.distance.toFixed(2) + ' km' + '<br>speed ' + speed.toFixed(2) +' km/h' + '<br>maxspeed ' + maxSpeed.toFixed(2) + ' km/h<br>time ' + TV.FmtTime2(pt.time.valueOf()) + '<br>elapsed ' + FmtTime(timeLapse);
}

function betweenTrackLines(file,track) {
	try {
		var previous=trackXMLdocs.processed[file][track-1];
		var next=trackXMLdocs.processed[file][track];
		var arr=[previous[previous.length-1],next[0]];
		// drawPolyline(new GPolyline(arr,"#000000",4,0.5),next[0]);
	} catch (e) {}
}

function drawPolyline(ptarr, poly,pt) {
	polylines.push(poly);
	if (polylines.length>5) {
//		map.removeOverlay(polylines[0]);
		polylines.splice(0,1);
	}

    // Recenter when map edge reached
    if (!TV.inside(pt, map.getBoundsLatLng())) {
  	   map.recenterOrPanToLatLng(pt);
    }

	for (var i=0; i < ptarr.length; i++) {
       TV.moveLabel(ptarr[i]);
    }

	map.addOverlay(polylines[polylines.length-1]);

	return pt;
}

function FmtTime(n) {
	seconds=n/1000;
	minutes=seconds/60;
	hours=minutes/60;
	return Math.floor(hours)+':'+pad(Math.floor(minutes)%60)+':'+pad(Math.floor(seconds)%60);
}


function getColour(ele) {
	var elevation=(170-Math.floor(ele))%170+85;
//	return "#"+pad(elevation.toString(16))+"0000";
	return "#CC33CC";
}

function pad(num) {
	var num=num+'';
	if (num.length==1) return "0"+num;
	return num;
}



function GCdistanceBetween(a,b) {
	var rad=0.01745566;
	Lo=Math.abs(a.x-b.x);
	Drad=Math.acos((Math.sin(a.y*rad)*Math.sin(b.y*rad))+(Math.abs(Math.cos(a.y*rad))*Math.abs(Math.cos(b.y*rad))*Math.cos(Lo*rad)));

	Crad=Math.acos((Math.sin(b.y*rad)-(Math.sin(a.y*rad)*Math.cos(Drad)))/(Math.cos(a.y*rad)*Math.sin(Drad)));
	var Cdeg = Crad * 57.295779;
	return Drad * 57.295779 * 69.06 * 1.6094;  // KIlometers Just
}

function parseDate(str) {
	str=str.split(/[-:TZ]/gim);
	return new Date(str[0],str[1]-1,str[2],str[3],str[4],str[5])
}

function PlayAll() {
   TP.play(trackXMLdocs.processed[0][0]);
}

// Added functions/classes by Just
var TP = {
   points: [],
   index: 0,
   lastIndex: 0,
   timeout: 30,
   lineDistance: 0,
   prevPoint: null,
   polyLineColour: '#FF0000',
   maxSpeed: 0,
   trackName: 'unnamed track',

   onLoaded: function(points) {
     TP.points = points;
	 if (map) {
       var pt = TP.points[0];
       TV.moveLabel(pt);
	   map.recenterOrPanToLatLng(pt);
	   document.getElementById('info').innerHTML='<center><span style="font-weight:bold">LOADED ' + TP.points.length + ' points -<br>Now zooming (wait)...</span>';
       setTimeout('TP.zoomToBegin()', 4000);
     }
   },

   zoomToBegin: function() {
     map.centerAndZoom(TP.points[0], 5);
	 document.getElementById('info').innerHTML='<center><span onclick="PlayAll()" style="font-weight:bold">READY - [Play Route]</span>';
   },

   init: function(points) {
     TP.index = 0;
     TP.lastIndex = 0;
     TP.lineDistance = 0;
     TP.prevPoint = null;
     TP.maxSpeed = 0;
	 if (map) {
       var pt = TP.points[0];
       // map.centerAndZoom(pt, 5);
	   map.clearOverlays();
 	   map.recenterOrPanToLatLng(pt);
       TV.moveLabel(pt);
	 }

   },

   play: function(points) {
     // First time
     if (points) {
       TP.init(points);
     }

     // Point to process
     var pt = TP.points[TP.index];

     // Recenter when map edge reached
     if (!TV.inside(pt, map.getBoundsLatLng())) {
  	   map.recenterOrPanToLatLng(pt);
     }

     // Move player
     TV.moveLabel(pt);

     // Set max speed
	 if (pt.speed && pt.speed < 45 && pt.speed > TP.maxSpeed) {
	   TP.maxSpeed = pt.speed;
	 }

     // Check if we should draw polyline
	 if (TP.prevPoint != null) {
        TP.lineDistance = TP.lineDistance + (pt.distance - TP.prevPoint.distance);

        if (TP.lineDistance > 4 || TP.index == (TP.points.length -1) ) {
          TP.drawPolyline();
          TP.lineDistance = 0;
          TP.lastIndex = TP.index;
        }

        if (TP.index % 20 == 0) {
          TP.showStatus(TP.points[TP.index]);
        }
	 }

     if (++TP.index < TP.points.length) {
       TP.prevPoint = pt;
       setTimeout('TP.play()', TP.timeout);
     } else {
       TP.showStatus(TP.points[TP.points.length-1]);
 	   document.getElementById('info').innerHTML = document.getElementById('info').innerHTML+'<br><center><span onclick="PlayAll()" style="font-weight:bold">[Play Route Again]</span></center>';
       TP.index = 0;
     }
   },

  drawPolyline: function() {
	var arr = TP.points.slice(TP.lastIndex, TP.index);
    var pl = new GPolyline(arr, TP.polyLineColour, 3, 0.80);
	map.addOverlay(pl);
  },

  showStatus: function(pt) {
    var timeLapse = pt.time.valueOf() - startTime.valueOf();
 	document.getElementById('info').innerHTML=TP.trackName + '<br>totaldist ' + totalDistance.toFixed(2) + ' km<br>dist ' + pt.distance.toFixed(2) + ' km' + '<br>speed ' + pt.speed.toFixed(2) +' km/h' + '<br>maxspeed ' + TP.maxSpeed.toFixed(2) + ' km/h<br>time ' + TV.FmtTime2(pt.time.valueOf()) + '<br>elapsed ' + FmtTime(timeLapse) + '<br>elevation ' + pt.ele + ' m';
  }

}


var TV = {
  lastPT : null,
  maxSpeed: 0,
  label: null,

  // Move skater
  moveLabel: function(pt) {
     if (TV.label == null) {
       TV.label = new TLabel();
       TV.label.id = 'mover';
       TV.label.anchorLatLng = pt;
       TV.label.anchorPoint = 'topCenter';
       //TV.label.markerOffset = new GSize (10,10);
       //TV.label.percentOpacity = [percent];
       TV.label.content = '<img src="blueball.gif" border="0" />';

       map.addTLabel(TV.label);
       return;
     }

     TV.label.setPosition(pt);
  },

  inside: function(pt, bounds) {
//       return false;
    return (pt.x > bounds.minX && pt.y > bounds.minY && pt.x < bounds.maxX && pt.y < bounds.maxY);
  },

  // Crude speed calc
  calcMaxSpeed: function(ptarr) {
	    var speed = 0;
		for (var i=0; i< ptarr.length; i++) {
		  if (ptarr[i].speed) {
		    speed = ptarr[i].speed;
		    if (speed < 45 && speed > TV.maxSpeed) {
	          TV.maxSpeed = speed;
	   	    }
	   	  }
        }

		return TV.maxSpeed;
	},

	// Crude speed calc
	calcSpeed: function(pt) {
		if (!pt.time) {
		  return -1;
		}

		if (TV.lastPT == null) {
		  TV.lastPT = pt;
		  return -2;
		}


		// Calc distance
		distance = GCdistanceBetween(pt, TV.lastPT);
		deltaTime = pt.time - TV.lastPT.time;
        seconds=deltaTime/1000;
	    minutes=seconds/60;
	    hours=minutes/60;
	    TV.lastPT = pt;
	    speed = (distance/hours);
		pt.speed = speed;
        if (speed > 100) {
           pt.speed = 0;
        }

		// Keep reasonable (may have GPS errors)
	    if (speed < 45 && speed > TV.maxSpeed) {
	       // TV.maxSpeed = speed;
	    }

		return distance / hours;
	},

  FmtTime2: function (n) {
	seconds=n/1000;
	minutes=seconds/60;
	hours=(minutes/60%24)+2; // Not nice, but GPS 2 hours behind ...
	return Math.floor(hours)+':'+pad(Math.floor(minutes)%60)+':'+pad(Math.floor(seconds)%60);
   },

	// Parse parameter out of a string.
	getParameter: function(string, parm, delim) {
		 // returns value of parm from string
		 if (string.length == 0) {return '';}
		 var sPos = string.indexOf(parm + "=");
		 if (sPos == -1) {return '';}
		 sPos = sPos + parm.length + 1;
		 var ePos = string.indexOf(delim, sPos);
		 if (ePos == -1) {ePos = string.length;}
		 return unescape(string.substring(sPos, ePos));
	},

	// Get parameter from query string passed to my page
	getPageParameter: function(parameterName, defaultValue) {
		var s = self.location.search;
		if ((s == null) || (s.length < 1)) {
			return defaultValue;
		}
		s = TV.getParameter(s, parameterName, '&');
		if ((s == null) || (s.length < 1)) {
			s = defaultValue;
		}
		return s;
	},

  // Cross-browser add event listener to element
  addEvent: function(elm, evType, callback, useCapture) {
    if (elm.addEventListener) {
      elm.addEventListener(evType, callback, useCapture);
      return true;
    } else if (elm.attachEvent) {
      var r = elm.attachEvent('on' + evType, callback);
      return r;
    } else {
      elm['on' + evType] = callback;
    }
  },

  init: function() {
    TV.addEvent(window, 'resize', sizer, false);
    sizer();
    var url = TV.getPageParameter('url', 'gpx/catalunya-2005/19-JUL-05-MTB-track-4.xml');
    getGPX(url);

    map = new GMap(document.getElementById("map"));
    map.setMapType(G_SATELLITE_MAP);
    // map.zoomTo(5);
    map.addControl(new GMapTypeControl());
    map.addControl(new GLargeMapControl());
    map.addControl(new GScaleControl());

    map.centerAndZoom(new GPoint(4.9, 52.35), 9);
  }

}

TV.addEvent(window, 'load', TV.init, false);
