// TODO(bolinfest): load page starting on current date
// TODO(bolinfest): add controls to make it easier to navigate dates
// TODO(bolinfest): Escape HTML and JS arguments where appropriate
// TODO(bolinfest): preload image markers
// TODO(bolinfest): Load more data if the initial load does not fill the agenda
// TODO(bolinfest): use reqid parameter when loading feeds
// TODO(bolinfest): use the maps API to get the bounding box for the map

/** The Google Map on which events are displayed */
var gMap;

var IMAGE_PATH = "http://calmap.bolinfest.com/images/";

/**
 * Keys are location names that have been plotted, such as "Shea Stadium"
 * Values are objects with the following keys:
 * <li><b>image</b> URL to marker image displayed in the agenda
 * <li><b>marker</b> GMarker for the location, plotted on the map
 * <li><b>events</b> Array of JSON CalendarEventEntries at the location
 */
var locationCache = {};

function getEntryLocation(entry) {
  return entry && entry.gd$where && entry.gd$where.length &&
      entry.gd$where[0].valueString;
}

function getEventsAt(location) {
  var html = [];
  var entries = locationCache[location].events;
  for (var i = 0; i < entries.length; ++i) {
    var entry = entries[i];    
    var linkText = getStartDateTime(entry) + ' ' +  getTitle(entry);
    var alternate = getAlternate(entry);
    if (alternate) {
      linkText = '<a href="' + alternate + '" target="_BLANK">' +
                 linkText + '</a>';
    }
    html.push('<div style="clear:both">' + getAddEventHtml(entry) + ' ' +
              linkText + '</div>');
  }
  var div = '<div><div class="infoWindowScrollbox">' +
            html.join("") + '</div></div>';
  // TODO(bolinfest): this width style is only here because of IE6
  // In the absence of the width style, Firefox sizes the infowindow naturally
  return '<div style="width: 20em">' +
    '<b>Events at ' + location + '</b><hr>' + div +
    '</div>';
}

/** Map of id of GData Entry to the GData Entry object itself */
var gEntries = {};
function getEntryById(id) {
  return gEntries[id];
}

/** Return HTML for button image that adds to Google Calendar when clicked */
function getAddEventHtml(entry) {
  var location = (getEntryLocation(entry)) 
               ? "&location=" + encodeURIComponent(getEntryLocation(entry))
               : "";
  var start = entry.gd$when[0].startTime;
  var end = entry.gd$when[0].endTime;
  var startDate = rfc3339StringToDate(start);
  var endDate = rfc3339StringToDate(end);
  var isAllDay = (startDate.length == 10);
  var dates = dateToEventPageDate(startDate, isAllDay) + '/' +
              dateToEventPageDate(endDate, isAllDay);
  // From http://www.google.com/googlecalendar/event_publisher_guide.html:
  // event page expects something like 20070205T071500Z/20070203T083000Z
  var url = "http://www.google.com/calendar/event?action=TEMPLATE" +
            "&text=" + encodeURIComponent(getTitle(entry)) + location +
            "&dates=" + dates;
  return '<a href="' + url + '" target="_BLANK" ' +
         'title="Add this event to Google Calendar">' +
         '<img src="' + IMAGE_PATH + 'add.gif" class="addButton">' + '</a>';
}

/** Get the start date in "4/19 7:05pm" format */
function getStartDateTime(entry) {
  var date = rfc3339StringToDate(entry.gd$when[0].startTime);
  var isAllDay = (date.length == 10);
  var dateStr = (date.getMonth() + 1) + '/' + date.getDate();
  if (isAllDay) {
    return dateStr;
  } else {
    var tm = date.getHours() + ":" + date.getMinutes();
    return dateStr + ' ' + formatTime(date);
  }
}

/** Get the title from a GData Entry */
function getTitle(entry) {
  return entry.title.$t;
}

/**
 * For a GData entry, return the alternate link as a string, if available.
 * Otherwise, return null;
 */
function getAlternate(entry) {
  var links = entry.link;
  if (!links) return null;
  for (var i = 0; i < links.length; ++i) {
    var link = links[i];
    if (link.rel != 'alternate') continue;
    return link.href;
  }
  return null;
}

function _showLocation(location) {
  var point = LOCATIONS[location];
  if (!point) return;
  point = new GLatLng(point.lat, point.lng);
  gMap.setMapType(G_SATELLITE_MAP);
  gMap.setCenter(point, 17);
  var marker = locationCache[location].marker;
  var html = getEventsAt(location);
  marker.openInfoWindowHtml(html);
}

function _onload() {
  var scrollList = new ScrollList();
  var content = $('content');
  scrollList.render(content);

  var url = window["feedUrl"];
  var loader = new EventLoader(url);
  window._callback = null;
  window._parseJson = function(json) {
    var callback = window._callback;
    var entries = json.feed.entry;
    if (!entries || !entries.length) {
      callback(null);
    } else {
      // sort the entries
      function entryComparator(a, b) {
        var aDate = rfc3339StringToDate(a.gd$when[0].startTime);
        var bDate = rfc3339StringToDate(b.gd$when[0].startTime);
        var cmp = aDate.getTime() - bDate.getTime();
        if (cmp) return cmp;
        var aTitle = getTitle(a);
        var bTitle = getTitle(b);
        if (String.prototype.getLocaleCompare) {
          return aTitle.localeCompare(bTitle);
        } else {
          var at = aTitle.toLowerCase();
          var bt = bTitle.toLowerCase();
          return ((at < bt)? -1 : ((at == bt) ? 0 : 1));
        }
      }
      entries.sort(entryComparator);      
      var contents = [];
      for (var i = 0; i < entries.length; ++i) {
        var entry = entries[i];
        gEntries[entry.id] = entry;
        var html = formatEntry(entry);
        if (html) contents.push(html);
      }
      callback(contents);
    }
  }

  /**
   * Generate the HTML to display the entry in the agenda
   * @return String<HTML>
   */
  function formatEntry(entry) {
    var datetime = getStartDateTime(entry);
    var title = getTitle(entry);
    var alternate = getAlternate(entry);
    if (alternate) {
      title = '<a href="' + alternate + '" target="_BLANK">' + title + '</a>';
    }
    // This seems a bit much
    // title = getAddEventHtml(entry) + ' ' + title;
    var location = getEntryLocation(entry);
    var info = "";
    var img = getLocationMarkerImage(location);
    if (img) {
      if (location) {
        locationCache[location].events.push(entry);
      }
      var onmouseover = "var s = this.style; s.textDecoration = 'underline';" +
        "s.cursor = 'pointer'; s.color = 'green';";
      var onmouseout = "var s = this.style; s.textDecoration = 'none';" +
        "s.cursor = 'default'; s.color = 'black';";
      var onmousedown = "_showLocation('" + location + "')";
      info = '<div class="info">' +
          '<div class="location" onmousedown="' + onmousedown + '"' +
                               ' onmouseover="' + onmouseover + '"' +
                               ' onmouseout="'  + onmouseout + '">' +
          '<img src="' + img + '"> ' + location +
          '</div></div>';
    } else {
      info = '<div class="info"><div class="location">' +
             location + '</div></div>'; 
    }
    return '<div class="event">' +
      '<div class="time">' + datetime + '</div>' +
      '<div class="title">' + title + '</div>' + info +
    '</div>';
  }

  var locationMarkers = [
    IMAGE_PATH + 'mm_12_red.png',
    IMAGE_PATH + 'mm_12_yellow.png',
    IMAGE_PATH + 'mm_12_green.png',
    IMAGE_PATH + 'mm_12_gray.png',
    IMAGE_PATH + 'mm_12_orange.png',
    IMAGE_PATH + 'mm_12_purple.png',
    IMAGE_PATH + 'mm_12_white.png'
  ];
  var nextMark = 0;
  function addMark(lat, lng, location, img) {
    var point = new GLatLng(lat, lng);
    img = img.replace('12', '26');
    var opts = {};
    opts.icon = new GIcon(G_DEFAULT_ICON, img);
    opts.icon.iconSize = new GSize(15, 26);
    var marker = new GMarker(point, opts);
    GEvent.addListener(marker, "click", createMarkerCallback(marker,location));
    gMap.addOverlay(marker);
    return marker;
  }
  /** @return {String} */
  function getLocationMarkerImage(/*String*/ location) {
    if (location in locationCache) return locationCache[location].image;
    var info = LOCATIONS[location];
    // TODO(bolinfest): try to geocode when no info present
    if (!info) return null;
    var img;
    if (info && info.img) {
      img = info.img;
    } else {
      img = locationMarkers[nextMark++];
      nextMark %= locationMarkers.length;
    }
    var marker = addMark(info.lat, info.lng, location, img);
    locationCache[location] = {
      "image" : img,
      "marker" : marker,
      "events" : []
    };
    return img;
  }
  function createMarkerCallback(marker, location) {
    return function() {
      var html = getEventsAt(location);
      marker.openInfoWindowHtml(html);
    };
  }

  gMap = new GMap2($('map'));
  // somewhere around wichita, KS
  gMap.setCenter(new GLatLng(37.6864, -97.3349), 4);
  gMap.setMapType(G_NORMAL_MAP);

  // start in April 2007
  var lastMonth = 3;
  var lastYear = (new Date()).getFullYear();

  function contentRequestHandler(callback) {
    ++lastMonth;
    if (lastMonth >= 12) {
      lastMonth = 0;
      ++lastYear;
    }
    var start = new Date(lastYear, lastMonth, 1);
    var end = new Date(lastYear, lastMonth,
        getNumberOfDaysInMonth(lastYear, lastMonth));
    window._callback = callback;
    loader.loadDates(start, end, "_parseJson");
  }
  scrollList.setContentRequestHandler(contentRequestHandler);

  // populate the agenda with April's data
  window._callback = function(entries) {
    if (entries) scrollList.addEntries(entries);
  };
  var start = new Date(lastYear, lastMonth, 1);
  var end = new Date(lastYear, lastMonth, 31);  
  loader.loadDates(start, end, "_parseJson");
}

