function loadMap(lat, lng) {
  if (GBrowserIsCompatible()) {
    var map = new GMap2(document.getElementById("detail-map"));
    map.setCenter(new GLatLng(lat, lng), 17);
    var marker = new GMarker(new GLatLng(lat, lng));
    map.addOverlay(marker);
    GEvent.addListener(map, "click", function() {
	window.location = "http://maps.google.com?q=" + lat + "," + lng;
    });
  }
}

function makeJourneyMapMarker(mapObj, checkinObj) {
    var lat = checkinObj['lat'];
    var lng = checkinObj['lng'];
    var note = checkinObj['note'];
    var thisPoint = new GLatLng(lat, lng);
    var markerArgs = {};
    if (note) {
      var icn = new GIcon();
      icn.image = "/i/marker-green.png";
      icn.iconSize = new GSize(20, 34);
      icn.iconAnchor = new GPoint(9, 34);
      markerArgs = { icon: icn };
    }
    var marker = new GMarker(thisPoint, markerArgs);
    
    var infoBubbleFn = function () {
      journeyDataGlobal['currentMarker'] = marker;
      mapObj.openInfoWindow(thisPoint, markerNoteStr(checkinObj));
    };
    GEvent.addListener(marker, "click", infoBubbleFn);
    return marker;
}

/* format of journeyDataGlobal is 
   { checkins:[ { lat:<lat>, lng:<lng>, when:<whenStr>, postId:<id>, [note:<noteStr] }, 
                ... ],
     bid: <bid>
     isActive: <yes-no>,
     title: <title-str>
     }
*/
var centerAndZoomerFn; // XXX ooooo global hack.
function paintFullJourneyMap(shouldAutoZoom) {
  if (!shouldAutoZoom) { shouldAutoZoom = false; }
  var map = new GMap2(document.getElementById("map"));
  map.addControl(new GLargeMapControl());
  map.addControl(new GMapTypeControl());
  journeyDataGlobal['map'] = map;

  var w = $(window).width();
  var h = $(window).height();
  var notesWidth = $('#notes').width();
  var jqMap = $('#map');
  jqMap.width(w - notesWidth - 40);
  jqMap.height(h - 90);

  centerAndZoomerFn = makeCenterAndZoomer(shouldAutoZoom, map);
  paintJourneyToIdx(journeyDataGlobal['checkins'].length);

  repeatedlyUpdateJourney();
}

// idx is exclusive
function paintJourneyToIdx(idx) {
  var checkins = journeyDataGlobal['checkins'];
  var map = journeyDataGlobal['map'];
  var prevPoint;
  for (var i = 0; (i < idx) && (i < checkins.length); i++) {
    var thisCheckin = checkins[i];
    var marker = makeJourneyMapMarker(map, checkins[i]);
    thisCheckin['marker'] = marker;
    var thisPoint = marker.getPoint();
    if (i == 0) {
      centerAndZoomerFn(thisCheckin);
    }
    else {
      paintLine(map, prevPoint, thisPoint);
    }
    map.addOverlay(marker);

    if (i == 0) { GEvent.trigger(marker, "click"); }
    
    prevPoint = thisPoint;
  }

  // add in the "connector" polyline in the case that this isn't the first:
  if ((idx > 0) && (idx < checkins.length)) {
    paintLine(map, 
	      checkins[idx - 1]['marker'].getPoint(),
	      checkins[idx]['marker'].getPoint());
  }

  // push notes in reverse order
  for (var i = (idx - 1); i >= 0; i--) {
    pushNoteOnJourney(i);
  }
}

function paintLine(map, fromPt, toPt) {
  var line = new GPolyline([fromPt, toPt], "#ff0000", 5);
  map.addOverlay(line);  
}

// does nothing if no note is available 
function pushNoteOnJourney(idx) {
  var checkins = journeyDataGlobal['checkins'];
  var checkin = checkins[idx];
  var note = checkin['note'];
  if (note) {
    var root = $('#notes-proper');
    var noNotes = $('#no-notes');
    if (noNotes) { noNotes.remove(); }
    var idxFromRight = checkins.length - idx - 1;
    root.prepend('<div class="note"><img src="/i/marker-green.png" /><p><a href="javascript:openCheckin(journeyDataGlobal, ' + idxFromRight + ');">' + note + '</a></p><p class="note-sig">' + timeSigStr(checkin) + '</p></div>');
  }
}

function openCheckin(data, idxFromRight) {
  var checkins = journeyDataGlobal['checkins'];
  var idx = checkins.length - idxFromRight - 1;
  var marker = checkins[idx]['marker'];
  GEvent.trigger(marker, "click");
  journeyDataGlobal['map'].setCenter(marker.getPoint());
}

function staticMapImgUrl(lat, lng) {
  var key = googleMapsKey();
  return "http://maps.google.com/staticmap?center=" +
    lat + "," + lng +
    "&zoom=16&size=230x195&maptype=mobile&key=" + key +
    "&markers=" + lat + "," + lng + ",midgreen";
}

// 60 seconds
var updateDelay = 60 * 1000;

/* returns an array in reverse chron (may be empty) of the latest posts to the given
   journey.  This array should be *pre*-pended to the current list. */
function repeatedlyUpdateJourney() {
  window.setInterval(function () { updateJourneyOnce(); }, updateDelay);
}

function updateJourneyOnce() {
  checkins = journeyDataGlobal['checkins'];
  if (checkins.length > 0) {
    var lastPostId = checkins[0]['postId'];
    var jid = journeyDataGlobal['bid'];
    $.get("/jcupdates/" + jid + "/" + lastPostId, handleJourneyUpdates, "json");
  }  
}

// this is called repeatedly
function handleJourneyUpdates(freshCheckinsArrayStr) {
  freshCheckinsArray = eval(freshCheckinsArrayStr);
  var len = freshCheckinsArray.length;
  if (len > 0) {
    // then we have updates...
    var existingCheckins = journeyDataGlobal['checkins'];
    journeyDataGlobal['checkins'] = freshCheckinsArray.concat(existingCheckins);
    paintJourneyToIdx(len);
    updateLastUpdated();
  }
  updateTimeStamps();
  updatePageTitle();
  updateInfoWindow();
}

function updateLastUpdated() {
  journeyDataGlobal['lastUpdatedSecs'] = (new Date().getTime() / 1000);
}

function updatePageTitle() {
  var lastUpdated = journeyDataGlobal['lastUpdatedSecs'];
  if (lastUpdated) {
    var titleStr = 'JourneyCast';
    var title = journeyDataGlobal['title'];
    if (title && (title.length > 0)) {
      titleStr = '"' + title + '"' + " on JourneyCast";
    }
    titleStr = "(updated " + deviceTimeToAgoStr(lastUpdated) + ") " + titleStr;
    document.title = titleStr;
  }
}

// notes only...so we skip non-noted checkins
function updateTimeStamps() {
  var checkins = journeyDataGlobal['checkins'];
  var notedCheckins = [];
  for (var i = 0; i < checkins.length; i++) {
    var cur = checkins[i];
    if (cur['note']) {
      notedCheckins.push(cur);
    }
  }
  var chillins = $('#notes-proper').children('div.note');
  for (var i = 0; (i < chillins.length); i++) {
    var singleton = $(chillins[i]).children('p.note-sig');
    var timeStampDiv = singleton[0];
    $(timeStampDiv).html(timeSigStr(notedCheckins[i]));
  }
}

function updateInfoWindow() {
  var m = journeyDataGlobal['map'];
  var info = m.getInfoWindow();
  if (!info.isHidden()) {
    GEvent.trigger(journeyDataGlobal['currentMarker'], "click");
  }
}

function timeSigStr(checkin) {
  var timeStamp = checkin['whenRaw'];
  return "&mdash;" + deviceTimeToAgoStr(timeStamp) + " (" + 
    deviceTimeToLocalTime(timeStamp) + ")";
}

function markerNoteStr(checkin) {
  var note = checkin['note'];
  var whenRaw = checkin['whenRaw'];
  var timeProse = deviceTimeToAgoStr(whenRaw);
  var noteProse;
  if (note) {
    noteProse = note + " (" + timeProse + ")";
  } else {
    noteProse = "Location posted " + timeProse;
  }
  return noteProse;
}

function deviceTimeToAgoStr(secs) {
  var nowSecs = (new Date().getTime()) / 1000;
  var passed = nowSecs - secs;
  var num;
  var units;
  if (passed < 60) {
    num = "a few";
    units = "second";
  } else if (passed < (60 * 60)) {
    num = Math.round(passed / 60);
    units = "minute";
  } else if (passed < (60 * 60 * 24)) {
    num = Math.round(passed / (60 * 60));
    units = "hour";
  } else {
    num = Math.round(passed / (60 * 60 * 24));
    units = "day"
  }
  return "" + num + " " + pluralize(units, num) + " ago";
}

function deviceTimeToLocalTime(secs) {
  var t = new Date(secs * 1000);
  var mins = t.getMinutes();
  var hours = t.getHours();
  var amPm = "AM";
  if (hours > 12) {
    hours = hours - 12;
    amPm = "PM";
  }
  if (hours == 0) {
    hours = 12;
  }
  if (mins < 10) {
    mins = mins + "";
    mins = "0" + mins;
  }
  return hours + ":" + mins + " " + amPm;
}

function pluralize(str, count) {
  if (count == 1) {
    return str;
  }
  return str + "s";
}

// returns a function that, when called with the most recent checkin, centers and zooms
// the map appropriately.
function makeCenterAndZoomer(shouldAutoZoom, map) {
  // note: we do all this computation inside the function since the checkins data
  // may have changed (e.g., with ajax checkin updates)
  return function(mostRecentCheckin) {
    var checkins = journeyDataGlobal['checkins'];
    var zoomLevel = 16;
    var centerPt;
    if (shouldAutoZoom) {
      if (checkins.length > 1) {
	var latSorted = checkins.slice().sort(makePointSorter('lat')); // south to north
	// XXX lng has problems in certain areas of the world...you should fix this:
	var lngSorted = checkins.slice().sort(makePointSorter('lng')); // ~ west to east
	var bottom = latSorted[0]['lat'];
	var top = latSorted[latSorted.length - 1]['lat'];
	var left = lngSorted[0]['lng'];
	var right = lngSorted[lngSorted.length - 1]['lng'];
	centerPt = new GLatLng((bottom + top) / 2, (left + right) / 2);
	var bnds = new GLatLngBounds(new GLatLng(bottom, left), new GLatLng(top, right));
	// the pop ups screw stuff up a bit, so we go out one more level...
	zoomLevel = map.getBoundsZoomLevel(bnds) - 1;
	if (zoomLevel < 0) { zoomLevel = 0; }
      }
    }
    if (!shouldAutoZoom) {
      centerPt = mostRecentCheckin['marker'].getPoint();
    }
    map.setCenter(centerPt, zoomLevel);
  }
}

function makePointSorter(checkinProp) {
  return function(checkin1, checkin2) {
    return checkin1[checkinProp] - checkin2[checkinProp];
  }
}

