// (C) Copyright 2006 Mario Wolczko mario@wolczko.com
// except in the parts lifted from http://www.google.com/apis/maps/documentation/.

var hikerName, datafile, markerData;
var map, bounds, points, lineSoFar, lastReal; 
var GPSicon, cellIcon, targetIcon, markers, lineOverlay;
var redrawInterval = 20 * 60 * 1000; // 20 mins
var pause = 100; // ms
var zoomPause = 2000;

function OtherControls() {
}
OtherControls.prototype = new GControl();

function latest() {
  map.panTo(points[points.length - 1]);
  var lastN = 5;
  var newBounds = computeBoundsOf(points.length < lastN ? points : points.slice(-lastN));
  var level= map.getBoundsZoomLevel(newBounds)-1;
  setTimeout(function(){ animateZoomTo(level, ""); }, zoomPause);
}

OtherControls.prototype.initialize = function(map) {
  var container = document.createElement("div");

  var nowDiv = document.createElement("div");
  this.setButtonStyle_(nowDiv);
  container.appendChild(nowDiv);
  nowDiv.appendChild(document.createTextNode("latest"));
  GEvent.addDomListener(nowDiv, "click", function() {
    reload("latest()");
  });

  var allDiv = document.createElement("div");
  this.setButtonStyle_(allDiv);
  container.appendChild(allDiv);
  allDiv.appendChild(document.createTextNode("all"));
  GEvent.addDomListener(allDiv, "click", function() {
    map.setCenter(bounds.getCenter(), map.getBoundsZoomLevel(bounds));
  });

  var tourDiv = document.createElement("div");
  this.setButtonStyle_(tourDiv);
  container.appendChild(tourDiv);
  tourDiv.appendChild(document.createTextNode("animate"));
  GEvent.addDomListener(tourDiv, "click", function() {
    map.removeOverlay(lineOverlay);
    for (var i = 0; i < markers.length; i++)
      map.removeOverlay(markers[i]);
    map.panTo(points[0]);
    lineSoFar = [];
    setTimeout(function(){
      animateZoomTo(map.getBoundsZoomLevel(bounds)+1, "panNext(1,1)");
    },pause);  
  });

  map.getContainer().appendChild(container);
  return container;
}

OtherControls.prototype.getDefaultPosition = function() {
  return new GControlPosition(G_ANCHOR_BOTTOM_RIGHT, new GSize(5, 10));
}

// Sets the proper CSS for the given button element.
OtherControls.prototype.setButtonStyle_ = function(button) {
  //button.style.textDecoration = "underline";
  button.style.color = "#0000cc";
  button.style.backgroundColor = "white";
  button.style.font = "small Arial";
  button.style.border = "1px solid black";
  button.style.padding = "2px";
  button.style.marginBottom = "3px";
  button.style.textAlign = "center";
  button.style.width = "6em";
  button.style.cursor = "pointer";
}

function animateZoomTo(newLevel, expr) {
  var currZoom = map.getZoom();
  if (currZoom == newLevel) 
    setTimeout(expr, pause);
  else
    setTimeout(function(){ 
      if (currZoom < newLevel)
	map.zoomIn();
      else
	map.zoomOut();
      animateZoomTo(newLevel, expr); 
    }, zoomPause);
}

function animatePanTo(point) {
  setTimeout(function(){ map.panTo(point); }, pause);
}

function panNext(i,j) {
/*
  var lastN = 15;
  if (i >= lastN) {
    var newBounds = computeBoundsOf(lineSoFar.slice(-lastN));
    var level= map.getBoundsZoomLevel(newBounds);
    animateZoomTo(level, "");
  }
*/
  map.panTo(points[i]);
  lineSoFar.push(points[i]);
  map.removeOverlay(lineOverlay);
  while (parseInt(markerData[j].getAttribute("cell")) == 2)
    j++;
  map.addOverlay(markers[j]);
  lineOverlay = new GPolyline(lineSoFar);
  map.addOverlay(lineOverlay);
  if (i < points.length - 1)
    setTimeout(function() {
      panNext(i+1,j+1);
    }, pause);
}

function makeIcons(mapElement) {
  // Create two "tiny" marker icons, one for GPS points, and one for cell towers, 
  // and the special icon for the most recent point
  GPSicon = new GIcon();
  GPSicon.image = "http://labs.google.com/ridefinder/images/mm_20_red.png";
  GPSicon.shadow = "http://labs.google.com/ridefinder/images/mm_20_shadow.png";
  GPSicon.iconSize = new GSize(12, 20);
  GPSicon.shadowSize = new GSize(22, 20);
  GPSicon.iconAnchor = new GPoint(6, 20);
  GPSicon.infoWindowAnchor = new GPoint(5, 1);

  cellIcon = new GIcon(GPSicon);
  cellIcon.image = "http://labs.google.com/ridefinder/images/mm_20_white.png";

  // marker for last (most recent) point
  targetIcon = new GIcon();
  targetIcon.image = hikerName + ".gif";
  //targetIcon.shadow = "target_shadow.png";
  targetIcon.iconSize = new GSize(mapElement.getAttribute("iconWidth"), 100);
  //targetIcon.shadowSize = new GSize(46, 42);
  targetIcon.iconAnchor = new GPoint(0, 99);
  targetIcon.infoWindowAnchor = new GPoint(0, 99);
}

function createMarker(point, dateFrom, dateTo, isCell, isLast) {
  var icon = isLast ? targetIcon : isCell ? cellIcon : GPSicon;
  var newMarker = new GMarker(point, icon);
  GEvent.addListener(newMarker, "click", function() {
    newMarker.openInfoWindowHtml(
        ((dateTo == "" || dateFrom == dateTo) ? dateFrom : dateFrom + " to<br>" + dateTo)
      + (isCell ? "<br>(cell tower)" : "<br>(via GPS)")
    );
  });
  markers.push(newMarker);
  map.addOverlay(newMarker);
} 

// compute the bounds of a set of points
function computeBoundsOf(points) {
  var bounds = new GLatLngBounds();
  for (var i = 0; i < points.length; i++) {
    bounds.extend(points[i]);
  }
  return bounds;
}

function addMarkers(start) {
  for (var i = start; i < markerData.length; i++) {
    var dateFrom = markerData[i].getAttribute("dateFrom");
    var dateTo = markerData[i].getAttribute("dateTo");
    var isCell = parseInt(markerData[i].getAttribute("cell")) > 0;
    var isBogus = parseInt(markerData[i].getAttribute("cell")) == 2;
    var lat = parseFloat(markerData[i].getAttribute("lat"));
    var lng = parseFloat(markerData[i].getAttribute("lng"));
    var point = new GLatLng(lat, lng);
    createMarker(point, dateFrom, dateTo, isCell, i == lastReal);
    if (!isBogus) 
      points.push(point);
  }
  lineOverlay = new GPolyline(points);
  map.addOverlay(lineOverlay);
}

// add unique query string to ensure we don't get a cached copy
function datafile_dontcache() {
  return datafile + "?dontCache=" + new Date().getTime();
}

function load() {
  if (GBrowserIsCompatible()) {
    // Download the data in datafile and load it on the map. The format we
    // expect is:
    // <markers>
    //   <marker lat="37.441" lng="-122.141" dateFrom="datestring" dateTo="[datestring]" cell="0|1|2"/>
    // </markers>
    // cell value: 0=GPS, 1=cell, 2=bogus cell (do not include on line)
    var mapElement = document.getElementById("map");
    hikerName = mapElement.getAttribute("name");
    datafile = hikerName + ".xml";
    GDownloadUrl(datafile_dontcache(), function(data, responseCode) {
      var xml = GXml.parse(data);
      markerData = xml.documentElement.getElementsByTagName("marker");

      // convert the lat,long pairs in the XML file to an array of GLatLngs
      points = [];
      for (var i = 0; i < markerData.length; i++) {
        var lat = parseFloat(markerData[i].getAttribute("lat"));
        var lng = parseFloat(markerData[i].getAttribute("lng"));
        var isBogus = parseInt(markerData[i].getAttribute("cell"))==2;
        if (!isBogus) {
          lastReal = i;
          points.push(new GLatLng(lat, lng));
        }
      }

      // iterate over all locations to compute initial map bounds
      bounds = computeBoundsOf(points);

      // make the map
      map = new GMap2(mapElement);
      map.setCenter(bounds.getCenter(), map.getBoundsZoomLevel(bounds));
      map.addControl(new GLargeMapControl());
      map.addControl(new GMapTypeControl());
      map.addControl(new OtherControls());

      makeIcons(mapElement);

      // now add the markers and a line joining all the GPS locations
      points = [];
      markers = [];
      addMarkers(0);
    });	
    setTimeout("reload(null)", redrawInterval); // redraw map periodically
  }
}

function removeLastMarker() {
  var lastMarker= markers.pop(); 
  map.removeOverlay(lastMarker);
  return lastMarker;
}

function reload(continuation) {
  GDownloadUrl(datafile_dontcache(), function(data, responseCode) {
    var xml = GXml.parse(data);
    var oldMarkerData = markerData;
    var oldLength = oldMarkerData.length;
    markerData = xml.documentElement.getElementsByTagName("marker");
    var lastMarker = markers[markers.length - 1];
    if (markerData.length == oldLength) { // check for new times
      if (oldMarkerData[oldLength-1].getAttribute("dateTo")
          != markerData[oldLength-1].getAttribute("dateTo")) {
        var lastPoint = lastMarker.getPoint();
        removeLastMarker();
        var newMarkerInfo = markerData[oldLength - 1];
        var cellAttr = parseInt(newMarkerInfo.getAttribute("cell"));
        createMarker(lastPoint,
	  	     newMarkerInfo.getAttribute("dateFrom"),
		     newMarkerInfo.getAttribute("dateTo"),
		     cellAttr > 0,
		     cellAttr != 2);
      }
    } else if (markerData.length > oldLength) { // got new data  
      // replace last marker (target) with cell/GPS 
      var lastPoint = removeLastMarker().getPoint();
      var oldMarkerInfo = oldMarkerData[oldLength - 1];
      // which is the last real point?
      for (var i = oldLength - 1; i < markerData.length; i++) {
        var isBogus = parseInt(markerData[i].getAttribute("cell"))==2;
        if (!isBogus)
          lastReal = i;
      }
      createMarker(lastPoint,
		   oldMarkerInfo.getAttribute("dateFrom"),
		   oldMarkerInfo.getAttribute("dateTo"),
		   parseInt(oldMarkerInfo.getAttribute("cell")) > 0,
		   lastReal == oldLength - 1);
      map.removeOverlay(lineOverlay);
      // add new markers, extend line
      addMarkers(oldLength);
    }
    if (continuation != null) 
      eval(continuation);
    else
      setTimeout("reload(null)", redrawInterval);
  });	
}

