
/* Depends on
 *
 * Google Maps API;
 * open-source marker manager;
 * LabeledMarker
 * mootools 1.2
 *
 */

// Globals
var map; // Google map
var mgr; // marker manager
var sizes = [ 12, 20, 32, 48 ]; // marker sizes; must match image sizes
var icon_names = [ 'S', 'C', 'D', 'SC', 'SD', 'CD', 'SCD' ]; // base names
// image paths
var images = { 'S': [],
	       'C': [],
	       'D': [],
               'SC': [],
               'SD': [],
               'CD': [],
               'SCD': [] };
var state_zoom = 3;
var report_zoom = 6;
var grid_prefix;

function initialize(agg_fn, state_agg_fn, timestamp, local_grid_prefix) {
  var i;
  var j;
  var icon;

  grid_prefix = local_grid_prefix;
  map = new GMap2(document.getElementById("map_canvas"));
  map.setMapType(G_PHYSICAL_MAP);
  map.addControl(new GLargeMapControl());
  map.addControl(new GMapTypeControl());
  map.setCenter(new GLatLng(0, 0), 2);

  mgr = new MarkerManager(map);

  for (i = 0; i < sizes.length; i++) {
    for (j = 0; j < icon_names.length; j++) {
      icon = new GIcon();
      icon.image = icon_names[j] + sizes[i] + 'a.png';
      icon.iconSize = new GSize(sizes[i], sizes[i]);
      icon.iconAnchor = new GPoint(sizes[i]/2, sizes[i]/2);
      icon.infoWindowAnchor = new GPoint(sizes[i]/2, 0);
      images[icon_names[j]][i] = icon;
    }
  }

  var state_data_loaded = false;
  function zoomHandler() {
    //alert('zoomHandler!');
    var filenames;
    var newzoom = map.getZoom();

    if (newzoom >= state_zoom && newzoom < report_zoom && !state_data_loaded) {
      state_data_loaded = true;
      show_loading();
      new Request.JSON({url: state_agg_fn,
			onComplete: function (x) {
			  handleReports(x, true);
			  hide_loading();
			}
		       }).get();
    }

    if (newzoom >= report_zoom) {
      filenames = get_filenames(timestamp);
      fetch_files(filenames);
    }
  }

  GEvent.addListener(map, 'zoomend', zoomHandler);
  GEvent.addListener(map, 'moveend', zoomHandler);

  show_loading();
  new Request.JSON({url: agg_fn,
		    onComplete: function (x) {
		      handleReports(x, true);
		      hide_loading();

		      var zoom_arg = gup('zoom');
		      var lat_arg = gup('lat');
		      var lon_arg = gup('lon');
		      if (zoom_arg && lat_arg && lon_arg) {
			map.setCenter(new GLatLng(parseFloat(lat_arg),
						  parseFloat(lon_arg)),
				      parseInt(zoom_arg));
		      }
		    }
		   }).get();

}

var load_count = 0;

function show_loading() {
  var x = $('load_indicator');
  var left = (800 - 100) / 2;
  var top = (550 - 100) / 2;
  x.setStyles({'display': 'block', 'position': 'absolute', 'top': top,
	       'left': left, 'z-index': 100000});
  load_count++;
}

function hide_loading() {
  load_count--;
  if (load_count == 0) {
    $('load_indicator').setStyle('display', 'none');
  }
}

function get_icon_name(report) {
  var name = '';
  if (report['Suspected'] > 0) {
    name += 'S';
  }
  if (report['Confirmed'] > 0) {
    name += 'C';
  }
  if (report['Fatal'] > 0) {
    name += 'D';
  }
  if (name === '') {
    name = 'S';
  }
  return name;
}

function get_size_idx(report) {
  if (report['cases'] > 99) {
    return 3;
  } else if (report['cases'] > 49) {
    return 2;
  } else if (report['cases'] > 5) {
    return 1;
  } else {
    return 0;
  }
}

function get_opts(report) {
  var icon_name = get_icon_name(report);
  var size_idx = get_size_idx(report);
  var opts = {
    icon: images[icon_name][size_idx],
    clickable: true,
    title: '',
    labelText: report['cases']
  };
  if (size_idx == 0) {
    opts['labelOffset'] = new GSize(-3, -6);
    opts['labelClass'] = 'small_label';
  } else if (size_idx == 1) {
    if (report['cases'] < 10) {
      opts['labelOffset'] = new GSize(-3, -6);
      opts['labelClass'] = 'small_label';
    } else {
      opts['labelOffset'] = new GSize(-7, -6);
      opts['labelClass'] = 'small_label';
    }
  } else if (size_idx == 2) {
    opts['labelOffset'] = new GSize(-8, -8);
    opts['labelClass'] = 'large_label';
  } else if (size_idx == 3) {
    if (report['cases'] < 1000) {
      opts['labelOffset'] = new GSize(-12, -6);
      opts['labelClass'] = 'large_label';
    } else if (report['cases'] < 10000) {
      opts['labelOffset'] = new GSize(-16, -8);
      opts['labelClass'] = 'large_label';
    } else {
      opts['labelOffset'] = new GSize(-15, -6);
      opts['labelClass'] = 'small_label';
    }
  }
  return opts;
}

function handleReports(reports, is_agg) {
  var i;
  var report;
  var markers = [];
  var us = null;
  var marker;
  var opts;
  var point;

  for (i = 0; i < reports.length; i++) {
    report = reports[i];

    point = new GLatLng(report['latitude'], report['longitude']);
    opts = get_opts(report);
    marker = new LabeledMarker(point, opts);
    marker.bindInfoWindowHtml(report['html']);

    if (is_agg && report['country'] == 'US' && !report['state']) {
      us = marker;
    } else {
      markers.push(marker);
    }
  }

  if (us) {
    mgr.addMarkers([us], 0, state_zoom-1);
  }
  if (is_agg) {
    mgr.addMarkers(markers, 0, report_zoom-1);
  } else {
    mgr.addMarkers(markers, report_zoom);
  }
  //alert('handleReports');
  mgr.refresh();
}

// from http://www.netlobo.com/url_query_string_javascript.html
function gup(name)
{
  name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
  var regexS = "[\\?&]"+name+"=([^&#]*)";
  var regex = new RegExp( regexS );
  var results = regex.exec( window.location.href );
  if( results == null )
    return "";
  else
    return results[1];
}

function embed_instructions() {
  var zoom = map.getZoom();
  var point = map.getCenter();
  var lat = point.y;
  var lon = point.x;

  var url = 'http://' + window.location.host + window.location.pathname + '?lat=' + lat + '&lon=' + lon + '&zoom=' + zoom;

  var html = 'Embed FluTracker (zoomed to the current location) in your page by cutting and pasting the following code: <br /><textarea cols="50" rows="5"><iframe src="' + url + '" width="850" height="750" style="border:none;" frameborder="0"</iframe></textarea>';

  $('embed_id').innerHTML = html;
}

/*
 * This code is prototypey.  It tries to compute a set of grid coordinates
 * based on the current state of the map, and turn those into filenames that
 * can be downloaded from the server.  The point is to try to decrease the
 * amount of data that needs to be shipped around.
 *
 * The way files are named is:
 *
 * timestamp/x_y.js
 *
 * where x and y range from 0 to 17, establishing 20 degree grid cells
 * across the longitudes and latitudes of the world.
 */

var lat_divisor = 10.0;
var lng_divisor = 20.0;
var lat_modulus = Math.round(180.0 / lat_divisor);
var lng_modulus = Math.round(360.0 / lng_divisor);
var cache = {};

function drop(x, addend, divisor) {
  x += addend;
  x = Math.floor(x);
  var s = x % divisor;
  x -= s;
  return Math.round(x / divisor);
}

function get_filenames(timestamp) {
  if (map.getZoom() < report_zoom) {
    return [];
  }

  var ne = map.getBounds().getNorthEast();
  var sw = map.getBounds().getSouthWest();
  var sx = drop(sw.lng(), 180.0, lng_divisor);
  var sy = drop(sw.lat(), 90.0, lat_divisor);
  var ex = drop(ne.lng(), 180.0, lng_divisor);
  var ey = drop(ne.lat(), 90.0, lat_divisor);
  var x, y;
  var fns = [];

  //console.log('sx: ' + sx + ' ex: ' + ex + ' sy: ' + sy + ' ey: ' + ey);

  i = 0;
  for (x = sx; x != ex+1; x++) {
    if (i >= 10)  break;
    for (y = sy; y != ey+1; y++) {
      // console.log(x + ', ' + y);
      i++;
      if (i >= 10)  break; // escape hatch if my math is wrong!
      x = x % lng_modulus;
      y = y % lat_modulus;
      fns.push(timestamp + '/' + grid_prefix + x + '_' + y + '.js');
    }
  }

  // console.log('Filenames: ' + fns);

  return fns;
}

function fetch_files(filenames) {
  var i = 0;
  var fn;
  for (i = 0; i < filenames.length; i++) {
    fn = filenames[i];
    if (cache[fn]) continue;
    cache[fn] = 1;
    show_loading();
    new Request.JSON({url: fn,
		      onSuccess: function (x) {
			handleReports(x, false);
			hide_loading();
		      },
		      onFailure: function(x) {
			hide_loading();
		      }
		     }).get();

  }
}