/**
 * Map Manager - Owns the map and provides
 *  a map UI API specific to runnerroot.
 *  The map manager is a shim between the
 *  Google API and runner root API. 
 */
MapManager.prototype.map;
MapManager.prototype.markers;
MapManager.prototype.lines;
MapManager.prototype.numbers;

MapManager.initialized = false;
MapManager.last_line;
MapManager.runner_icon;
MapManager.runner_multi_icon;
MapManager.point_icon;
MapManager.start_icon;
MapManager.finish_icon;
MapManager.start_finish_icon;


function MapManager(map) {
  this.markers = new Array();
  this.lines = new Array();
  this.numbers = new Array();

  this.map = map;
  this.map.setUIToDefault();

  if (!MapManager.initialized) {
    MapManager.init();
    MapManager.initialized = true;
  }
}

// init icons...
MapManager.init = function() {
  var runner_icon = new GIcon();
  runner_icon.image = '/images/icons/jogging.png';
  runner_icon.shadow = '/images/icons/activity-shadow.png';
  runner_icon.iconSize = new GSize(32, 37);
  runner_icon.shadowSize = new GSize(51, 37);
  runner_icon.iconAnchor = new GPoint(15, 35);
  runner_icon.infoWindowAnchor = new GPoint(15, 10);
  MapManager.runner_icon = runner_icon;

  var runner_multi_icon = new GIcon();
  runner_multi_icon.image = '/images/icons/jogging-multiple.png';
  runner_multi_icon.shadow = '/images/icons/activity-shadow.png';
  runner_multi_icon.iconSize = new GSize(32, 37);
  runner_multi_icon.shadowSize = new GSize(51, 37);
  runner_multi_icon.iconAnchor = new GPoint(15, 35);
  runner_multi_icon.infoWindowAnchor = new GPoint(15, 10);
  MapManager.runner_multi_icon = runner_multi_icon;

  var point_icon = new GIcon();
  point_icon.image = '/images/icons/route-marker.png';
  point_icon.iconSize = new GSize(8, 8);
  point_icon.iconAnchor = new GPoint(4, 4);
  MapManager.point_icon = point_icon;

  var start_icon = new GIcon();
  start_icon.image = '/images/icons/start.png';
  start_icon.iconSize = new GSize(17, 17);
  start_icon.iconAnchor = new GPoint(8, 17);
  MapManager.start_icon = start_icon;

  var finish_icon = new GIcon();
  finish_icon.image = '/images/icons/finish.png';
  finish_icon.iconSize = new GSize(17, 17);
  finish_icon.iconAnchor = new GPoint(8, 17);
  MapManager.finish_icon = finish_icon;  

  var start_finish_icon = new GIcon();
  start_finish_icon.image = '/images/icons/start_finish.png';
  start_finish_icon.iconSize = new GSize(17, 17);
  start_finish_icon.iconAnchor = new GPoint(8, 17);
  MapManager.start_finish_icon = start_finish_icon;  
}

MapManager.prototype.clear_markers = function() {
  for (var x = 0; x < this.markers.length; ++x) {
    this.map.removeOverlay(this.markers[x]);
  }
  this.markers = new Array();
}

MapManager.prototype.clear_lines = function() {
  for (var x = 0; x < this.lines.length; ++x) {
    this.map.removeOverlay(this.lines[x]);
  }
  this.lines = new Array();
}

MapManager.prototype.is_really_close = function(a, b) {
  var distance = 0;

  // since we're just look for really close things, we can treat this as a plane
  distance  = Math.pow(a.start_lat - b.start_lat, 2);
  distance += Math.pow(a.start_lng - b.start_lng, 2);
  distance  = Math.sqrt(distance);
  return (distance < 0.001);
}

MapManager.prototype.display_search_results = function(results) {
  if (!results) { return; }

  this.clear_markers();
  this.clear_lines();

  for (var x = 0; x < results.length; ++x) {
    results[x].displayed = false;
  }

  for (var x = 0; x < results.length; ++x) { 
    var result = results[x];
    if (result.displayed) continue;

    // sanity check
    if (!result || !result.start_lat || !result.start_lng) { continue; }

    // new current route
    var routes = new Array();
    routes.push(result);
    result.displayed = true;

    // check for other routes very close to this one
    for (var y = x + 1; y < results.length; ++y) {
      if (!routes.displayed && this.is_really_close(result, results[y])) {
	routes.push(results[y]);
	results[y].displayed = true;
      }
    }

    this.mark_routes(routes);
  }
}

MapManager.prototype.mark_routes = function(routes) {
  if (routes.length == 0) return;
  
  var options = (routes.length == 1) ? {icon : MapManager.runner_icon} : {icon : MapManager.runner_multi_icon};
  var marker = new GMarker(new GLatLng(routes[0].start_lat, routes[0].start_lng), options);
  this.markers.push(marker);
  this.map.addOverlay(marker);
  GEvent.addListener(marker, "click", 
		     function() {
		       // code to display the nice "popup" to select the route
		       var html = "<div class=\"root_marker\">";
		       var style = " style=\"padding: 4px 4px 4px 4px\"";
		       html += "<table>";
		       html += "<tr>\n";
                       html += "  <td class=\"header\"" + style + ">Name</td><td class=\"header\"" + style + ">Distance</td>\n";
		       html += "  <td class=\"header\"" + style + ">Owner</td><td class=\"header\"" + style + ">[Delete]</td>\n";
                       html += "</tr>\n";
		       for (var x = 0; x < routes.length; ++x) {
			 var short_name = (routes[x].name.length > 50) ? (routes[x].name.substr(0, 50) + "...") : routes[x].name;
			 html += "<tr>";
			 html += "  <td" + style + "><a href=\"javascript: load_root(" + routes[x].id + ")\">" + short_name + "</a></td>";
			 html += "  <td" + style + ">" + routes[x].distance + "</td>";
			 html += "  <td" + style + ">" + (routes[x].username ? routes[x].username : "public") + "</td>";
			 html += "  <td" + style + ">[<a href=\"javascript: delete_root(" + routes[x].id + ")\">Delete</a>]</td>";
			 html += "</tr>";
		       }
		       html += "</table>";
		       html += "</div>";
		       marker.openInfoWindow(html);
		     });
}

MapManager.prototype.gather_glatlng = function(x) {
  if (!x.glatlng) {
    x.glatlng = new GLatLng(x.lat, x.lng);
  }

  return x.glatlng;
}

MapManager.prototype.gather_gmarkers = function(x) {
  if (!x.glatlng) {
    x.glatlng = new GLatLng(x.lat, x.lng);
  }

  if (!x.g_marker) {
    x.g_marker = new GMarker(x.glatlng, {icon : MapManager.point_icon, draggable : true});
    GEvent.addListener(x.g_marker, "click", function() { process_point(x, x.glatlng); });
    GEvent.addListener(x.g_marker, "dragend", 
		       function(latlng) { 
			 x.lat = latlng.lat(); 
			 x.lng = latlng.lng(); 
			 x.glatlng = new GLatLng(x.lat, x.lng);
			 update_root(); });
  }

  return x.g_marker;
}

MapManager.prototype.add_distance_marker = function(number, distance, last_point, current_point) {
  // sanity check
  if (number >= 100) return;

  var current_distance = Root.calculate_distance(last_point, current_point);
  var relative_distance = (number - distance) / current_distance;
  var new_lat = parseFloat(last_point.lat) + (current_point.lat - last_point.lat) * relative_distance;
  var new_lng = parseFloat(last_point.lng) + (current_point.lng - last_point.lng) * relative_distance;

  if (!this.numbers[number]) {
    var number_icon = new GIcon();
    number_icon.image = '/images/numbers/' + number + '.png';
    number_icon.iconSize = new GSize(17, 17);
    number_icon.iconAnchor = new GPoint(8, 17);
    this.numbers[number] = number_icon;
  }

  var distance_marker = new GMarker(new GLatLng(new_lat, new_lng), this.numbers[number]);
  this.markers.push(distance_marker);
  this.map.addOverlay(distance_marker);
}
    
MapManager.prototype.display_route = function(route) {
  this.clear_markers();
  this.clear_lines();

  if (route.get_points().length > 0) {
    // add line
    var points = route.get_points().map(this.gather_glatlng);
    var new_line = new GPolyline(points, "#6666ee", 5, 0.6);
    var last_point = false;
    this.map.addOverlay(new_line);
    this.lines.push(new_line);

    // add points
    points = route.get_points().map(this.gather_gmarkers);
    for (var x = 0; x < points.length; ++x) {
      // add the point to the map
      this.markers.push(points[x]);
      this.map.addOverlay(points[x]);
    }

    // add the distance markers
    points = route.get_points();
    var total_distance = 0;
    for (var x = 0; x < points.length; ++x) {
      // calculate distance to add mile/km markers
      if (!last_point) last_point = points[x];
      current_distance = Root.calculate_distance(last_point, points[x]);

      // if we make it into the loop we need to add one or more distance markers
      for (var y = Math.floor(total_distance + 1); y < total_distance + current_distance; ++y) {
	this.add_distance_marker(y, total_distance, last_point, points[x]);
      }

      total_distance += current_distance;
      last_point = points[x];
      if (total_distance > 100) { break; }
    }

    // start and finish markers...
    if (points[0].equals(points[points.length - 1])) {
      // same point, just one start/finish marker
      var marker = new GMarker(points[0].glatlng, {icon : MapManager.start_finish_icon});
      this.markers.push(marker);
      this.map.addOverlay(marker);
    } else {
      // start and finish marker
      var marker = new GMarker(points[0].glatlng, {icon : MapManager.start_icon});
      this.markers.push(marker);
      this.map.addOverlay(marker);
      marker = new GMarker(points[points.length - 1].glatlng, {icon : MapManager.finish_icon});
      this.markers.push(marker);
      this.map.addOverlay(marker);
    }

  }
}

// setters
MapManager.prototype.set_center = function(latlng, zoom) { this.map.setCenter(latlng, zoom); }
MapManager.prototype.set_zoom = function(zoom) { this.map.setZoom(zoom); }

// getters
MapManager.prototype.get_center = function() { return this.map.getCenter(); }
MapManager.prototype.get_zoom = function() { return this.map.getZoom(); }
MapManager.prototype.get_bounds = function() { return this.map.getBounds(); }


