﻿var map;
var undo;

$(function () {
  google.load("maps", "2", { callback: initialize });

  // point description dialog			
  $('#dialog').dialog({
    autoOpen: false,
    width: 400,
    modal: true,
    buttons: {
      "OK": function () {
        $(this).dialog("close");
      }
    },
    open: function (event, ui) {
      $('#pointDescription').val("");
      $('#pointDescription').focus();
    }
  });
  $('#dialog').keyup(function (e) {
    if (e.keyCode == 13) {
      $('#dialog').dialog("close");
    }
  });

  // goto location dialog
  $('#locationDialog').dialog({
    autoOpen: false,
    width: 400,
    modal: true,
    buttons: {
      "OK": function () { gotoLocationOK(); }
    },
    open: function (event, ui) {
      // clear all fields
      $('#location').val("");
      $('#latInput').val("");
      $('#lngInput').val("");
      $('#eastingInput').val("");
      $('#northingInput').val("");

      $('#location').focus();
    }
  });
  $('#locationDialog').keyup(function (e) {
    if (e.keyCode == 13) {
      gotoLocationOK();
    }
  });

  function gotoLocationOK() {
    $('#locationDialog').dialog("close");
    if ($('#location').val() != "") {
      // location name
      var geocoder = new GClientGeocoder();
      geocoder.getLatLng($('#location').val(), function (latLng) {
        map.setCenter(latLng);
        addPointToMap(latLng, $('#location').val());
      });
    }
    else if ($('#latInput').val() != "" && $('#lngInput').val() != "") {
      // lat/long
      var latlng = new GLatLng($('#latInput').val(), $('#lngInput').val());
      map.setCenter(latlng);
      addPointToMap(latlng);
    }
    else if ($('#eastingInput').val() != "" && $('#northingInput').val() != "") {
      // easting/northing
      var os1w = new OSRef($('#eastingInput').val(), $('#northingInput').val());
      var ll1w = os1w.toLatLng(os1w);
      ll1w.OSGB36ToWGS84();
      var latlng = new GLatLng(ll1w.lat, ll1w.lng);
      map.setCenter(latlng);
      addPointToMap(latlng);
    }
  }

  // load KML dialog
  $('#loadKmlDialog').dialog({
    autoOpen: false,
    width: 400,
    modal: true,
    buttons: {
      "OK": function () { loadKmlOK(); }
    },
    open: function (event, ui) {
      $('#predefinedLayer').val("");
      $('#kmlUrl').val("");
      $('#kmlUrl').focus();
      $('#kmlLoading').hide();
      $('#kmlLoading').html("Loading...");
    }
  });

  $('#loadKmlDialog').keyup(function (e) {
    if (e.keyCode == 13) {
      loadKmlOK();
    }
  });

  function loadKmlOK() {
    // load KML
    $('#kmlLoading').show();
    loadKml($('#kmlUrl').val());
  }

  // get directions dialog
  $('#getDirectionsDialog').dialog({
    width: 600,
    resizable: false,
    autoOpen: false,
    modal: false
  });
  $('#getDirectionsDialog').keyup(function (e) {
    if (e.keyCode == 13) {
      goGetDirections();
    }
  });

  undo = new undoStack();
});

function initialize() {
  if (GBrowserIsCompatible()) {
    map = new GMap2($("#map")[0]);
    // need to set the centre before resizing the map for the first time
    map.setCenter(new GLatLng(54.5, -2.5), 6);
    resizeMap();
    if (google.loader.ClientLocation == null) {
      map.setCenter(new GLatLng(54.5, -2.5), 6);
    }
    else {
      var latLong = new GLatLng(google.loader.ClientLocation.latitude,
            google.loader.ClientLocation.longitude);
      map.setCenter(latLong, 12);
    }
    map.addControl(new GLargeMapControl());
    map.addControl(new GMapTypeControl());
    map.addControl(new GNavLabelControl(), new GControlPosition(G_ANCHOR_BOTTOM_LEFT));
    map.enableDoubleClickZoom();
    map.enableScrollWheelZoom();
    window.onresize = resizeMap;
    setCurrentTool(null);

    GEvent.bind(map, "mousemove", this, function(latlng) {
      $('#lat').html(latlng.lat());
      $('#lng').html(latlng.lng());
    });

    // ads
    var adsManagerOptions = {
      maxAdsOnMap: 4,
      style: G_ADSMANAGER_STYLE_ADUNIT
    };

    adsManager = new GAdsManager(map, "pub-3374089704310222", adsManagerOptions);
    adsManager.enable();
  }
}

function resizeMap() {
  var width, height;

  // all except Explorer
  if (window.innerHeight) {
    width = window.innerWidth;
    height = window.innerHeight;
  }
  // Explorer 6 Strict Mode
  else {
    width = document.documentElement.clientWidth;
    height = document.documentElement.clientHeight;
  }

  height -= 50;
  width -= 200;
  var mapDiv = $("#map")[0];
  if (width > 0)
    mapDiv.style.width = width + "px";
  if (height > 0)
    mapDiv.style.height = height + "px";

  map.checkResize();
}

var currentTool;
var clickListener;
function setCurrentTool(tool) {
  if (currentTool != null) {
    currentTool.end();
    // reset the cursor
    map.getDragObject().setDraggableCursor("url(http://maps.google.com/intl/en_us/mapfiles/openhand.cur)");
    GEvent.removeListener(clickListener);
  }
  currentTool = tool;
    
  if (tool == null)
    $('#currentTool').html("[none]");
  else {
    $('#currentTool').html(tool.getName());
    // all tools currently use a crosshair cursor
    map.getDragObject().setDraggableCursor("crosshair");
    clickListener = GEvent.bind(map, "click", currentTool, currentTool.clickHandler);
  }
}

function clearAll() {
  undo = new undoStack();
  map.clearOverlays();
  clearDirections();
  // clear layer checkboxes
  $('#layers div input').attr('checked', false);
}

function gotoLocation() {
  $('#locationDialog').dialog('open');
}

function loadKmlDialog() {
  $('#loadKmlDialog').dialog('open');
}

// predefined layers
var wikipediaArticlesLayer;
var trafficLayer;
var postcodeDistrictsLayer;
var webcamsLayer;
var photosLayer;
var videosLayer;
var stationsLayer;
var publicTransportLayer;

function toggleTileLayer(layer, tileUrlTemplate) {
  if (layer == null) {
    layer = new GTileLayerOverlay(
      new GTileLayer(null, null, null, {
        tileUrlTemplate: tileUrlTemplate,
        isPng: true,
        opacity: 0.8
      })
    );

    map.addOverlay(layer);
    return layer;
  }
  else {
    return removeLayer(layer);
  }
}

function toggleTraffic(layer) {
  if (layer == null) {
    layer = new GTrafficOverlay();
    map.addOverlay(layer);
    return layer;
  }
  else {
    return removeLayer(layer);
  }
}

function removeLayer(layer) {
  map.removeOverlay(layer);
  return null;
}

function toggleKml(layer, kmlUrl, zoomId) {
  if (layer == null) {
    layer = new GGeoXml(kmlUrl);
    map.addOverlay(layer);
    var loadEvent = GEvent.bind(layer, "load", this, function() {
      if (!layer.hasLoaded()) {
        alert("Failed to load layer");
      }
      GEvent.removeListener(loadEvent);
    });
    $('#' + zoomId).show();
    return layer;
  }
  else {
    $('#' + zoomId).hide();
    return removeLayer(layer);
  }
}

function toggleGLayer(layer, layerUrl) {
  if (layer == null) {
    layer = new GLayer(layerUrl);
    map.addOverlay(layer);
    return layer;
  }
  else {
    return removeLayer(layer);
  }
}

function zoomToLayer(kmlLayer) {
  if (kmlLayer != null) {
    // move the map to a sensible location
    var center = kmlLayer.getDefaultCenter();
    map.setCenter(center);

    var span = kmlLayer.getDefaultSpan();
    if (span != null) {
      var sw = new GLatLng(center.lat() - span.lat() / 2,
                       center.lng() - span.lng() / 2);
      var ne = new GLatLng(center.lat() + span.lat() / 2,
                       center.lng() + span.lng() / 2);
      var bounds = new GLatLngBounds(sw, ne);

      map.setZoom(map.getBoundsZoomLevel(bounds));
    }
  }
}

function loadKml(kmlUrl) {
  var kmlLayer = new GGeoXml(kmlUrl);
  map.addOverlay(kmlLayer);
  var loadEvent = GEvent.bind(kmlLayer, "load", this, function() {
    if (kmlLayer.hasLoaded()) {
      $('#loadKmlDialog').dialog("close");
      zoomToLayer(kmlLayer);
      undo.add(kmlLayer);
    }
    else {
      $('#kmlLoading').html("Failed to load");
    }
    GEvent.removeListener(loadEvent);
  });
}

function getDirections() {
  $('#getDirectionsDialog').dialog('open');
}

var directions;
function goGetDirections() {
  if (directions == null)
    directions = new GDirections(map, $('#directions')[0]);
  directions.clear();
  $('#distance').html("Loading...");

  GEvent.addListener(directions, "load", function() {
    $('#directions').html("").css('height', '600px');
    $("#distance").html("Distance: " +
            directions.getDistance().html + ", Time: " +
            directions.getDuration().html);

    // if directions are already on the undo stack, remove
    undo.remove(directions);
    undo.add(directions);
  });

  function getErrorMessage(code) {
    switch (code) {
      case G_GEO_SUCCESS: return "no errors occurred";
      case G_GEO_BAD_REQUEST: return "the directions request could not be successfully parsed";
      case G_GEO_UNKNOWN_ADDRESS: return "no corresponding geographic location could be found for the specified address";
      case G_GEO_UNKNOWN_DIRECTIONS: return "could not compute directions";
      // TODO - handle other error codes
      default: return "error code " + code;
    }
  }

  GEvent.addListener(directions, "error", function() {
    $('#distance').html("An error occurred, " + getErrorMessage(directions.getStatus().code));
    $('#directions').html("").css('height', '0px');
  });

  var directionsMode = G_TRAVEL_MODE_DRIVING;
  if ($("#directionsMode").val() == "Walking")
    directionsMode = G_TRAVEL_MODE_WALKING;
  
  directions.load("from: " + $("#directionsFrom").val() + " to: " + $("#directionsTo").val(),
    { travelMode: directionsMode });
}

function clearDirections() {
  $("#directionsFrom").val("");
  $("#directionsTo").val("");
  $('#directions').html("").css('height', '0px');
  $("#distance").html("");
  if (directions != null)
    directions.clear();
  undo.remove(directions);
}

function addPoint() {
  if (currentTool instanceof addPointTool) {
    setCurrentTool(null);
  }
  else {
    setCurrentTool(new addPointTool());
  }
}

function addLine() {
  if (currentTool instanceof addLineTool) {
    setCurrentTool(null);
  }
  else {
    setCurrentTool(new addLineTool());
  }
}

function streetView() {
  if (currentTool instanceof streetViewTool) {
    setCurrentTool(null);
  }
  else {
    setCurrentTool(new streetViewTool());
  }
}

function getPointHtml(latlng) {
  // get easting/northing
  var ll2w = new LatLng(latlng.lat(), latlng.lng());
  ll2w.WGS84ToOSGB36();
  var os2w = ll2w.toOSRef();

  return "Latitude: " + latlng.lat() + "<br/>" +
    "Longitude: " + latlng.lng() + "<br/><br/>" +
    "Easting: " + os2w.easting + "<br/>" +
    "Northing: " + os2w.northing + "<br/>";
}

function addPointToMap(latlng, description) {
  var marker = new GMarker(latlng, { title: description });
  map.addOverlay(marker);
  // TODO - get elevation of location (use Google Maps Javascript API version 3 when it's released)

  var html = "";
  if (description != null)
    html += "<b>" + description + "</b><br/>";
  marker.bindInfoWindowHtml(html + getPointHtml(latlng));
  undo.add(marker);
}

// classes ----------------------------------------------------------------------------------

var undoStack = function() {

  this.array = Array();

  this.add = function(item) {
    this.array.push(item);
  }

  this.undo = function() {
    var item = this.array.pop();

    if (item != null) {
      if (item instanceof GPolyline) {
        // remove overlay when two points
        if (item.getVertexCount() <= 2) {
          map.removeOverlay(item);
          setCurrentTool(null);
        }
        else {
          // put line back on the stack
          this.array.push(item);
          if (!(currentTool instanceof addLineTool)) {
            setCurrentTool(new addLineTool(item));
          }

          currentTool.undo();
        }
      }
      else if (item instanceof GDirections) {
        clearDirections();
      }
      else {
        // this is a point or KML layer - remove it
        map.removeOverlay(item);
      }
    }
  }

  this.remove = function(item) {
    for (var i = this.array.length - 1; i >= 0; i--) {
      if (this.array[i] == item) {
        this.array.splice(i, 1);
      }
    }
  }
}

// Tools ------------------------------------------------------------------------------------

// add point tool
var addPointTool = function () {

  this.getName = function () {
    return "Add point";
  }

  this.end = function () {
  }

  this.clickHandler = function (overlay, latlng) {
    $('#dialog').bind('dialogclose', function (event, ui) {
      var description = $('#pointDescription').val();
      addPointToMap(latlng, description);
      setCurrentTool(null);
      $('#dialog').unbind('dialogclose');
    });
    $('#dialog').dialog('open');
  }
}

// ------------------------------------------------------------------------------------------
// add line tool
var addLineTool = function(item) {
  var polyline = item;

  $('#lengthInfo').show();
  $('#measureUnits').bind('change', { tool: this }, function(event) {
    event.data.tool.showLineLength();
  });
  $('#addLine').attr("title", "End add line");
  var linePoints = new Array();
  if (polyline != null) {
    for (var i = 0; i < polyline.getVertexCount(); i++) {
      linePoints.push(polyline.getVertex(i));
    }
  }

  this.clickHandler = function(overlay, latlng) {
    // add point of line
    linePoints.push(latlng);
    this.rebuildPolyline();
  }

  this.rebuildPolyline = function() {
    if (polyline != null) {
      undo.remove(polyline);
      map.removeOverlay(polyline);
    }
    polyline = new GPolyline(linePoints);
    map.addOverlay(polyline);
    undo.add(polyline);
    this.showLineLength();
  }

  this.getName = function() {
    return "Add line";
  }

  this.end = function() {
    $('#addLine').attr("title", "Add line");
    polyline = null;
    linePoints = null;
    $('#lineLength').html("");
    $('#lengthInfo').hide();
  }

  this.showLineLength = function() {
    if (polyline != null) {
      var length = polyline.getLength();
      var lengthString = "";
      var units = $('#measureUnits').val();
      if (units == "metres") {
        lengthString = length.toFixed(0);
      }
      else if (units == "kilometres") {
        lengthString = (length / 1000).toFixed(1);
      }
      else if (units == "feet") {
        lengthString = (length * 3.2808399).toFixed(0);
      }
      else if (units == "yards") {
        lengthString = (length * 1.0936133).toFixed(0);
      }
      else if (units == "miles") {
        lengthString = (length * 0.000621371192).toFixed(1);
      }
      $('#lineLength').html(lengthString);
    }
  }

  this.undo = function() {
    linePoints.pop();
    this.rebuildPolyline();
  }
}

// ------------------------------------------------------------------------------------------
// street view tool
// ideally this would be part of the streetViewTool class but can't I can't get it to work
var myPano;
var streetViewTool = function() {
  var marker;
  var errorListener;
  var svMoveListener;

  // add street view overlay
  var svOverlay = new GStreetviewOverlay();
  map.addOverlay(svOverlay);
  $('#streetView').attr("title", "End street view");

  // street view dialog
  $('#streetViewDialog').dialog({
    autoOpen: false,
    width: 480,
    height: 420,
    modal: false,
    resizable: true,
    open: function() {
      // set up street view
      var panoCtr = $("#pano")[0];
      myPano = new GStreetviewPanorama(panoCtr);
      myPano.setContainer(panoCtr);
      errorListener = GEvent.addListener(myPano, "error", function(errorCode) {
        var error = "An error occurred";
        if (errorCode == 600) {
          error = "No street view available";
        }
        else if (errorCode == 603) {
          error = "The Flash plugin is not available";
        }
        $("#panoError").html(error);
      });

      // move marker
      svMoveListener = GEvent.addListener(myPano, "initialized", function(location) {
        addMarker(location.latlng);
      });
    },
    close: function() {
      if (errorListener != null)
        GEvent.removeListener(errorListener);
      if (svMoveListener != null)
        GEvent.removeListener(svMoveListener);
      if (myPano != null)
        myPano.remove();
      myPano = null;
    },
    resize: function(event, ui) {
      $('#pano').css('height', ui.size.height - 70);
      myPano.checkResize();
    }
  });

  var addMarker = function(latlng) {
    if (marker != null) {
      map.removeOverlay(marker);
    }

    var houseIcon = new GIcon(G_DEFAULT_ICON);
    houseIcon.image = 'images/houses.png';

    // Set up our GMarkerOptions object
    var markerOptions = { icon: houseIcon };

    marker = new GMarker(latlng, markerOptions);
    marker.bindInfoWindowHtml(getPointHtml(latlng));
    map.addOverlay(marker);
  }

  this.clickHandler = function(overlay, latlng) {
    $('#streetViewDialog').dialog('open');
    $("#panoError").html("");
    if (myPano != null)
      myPano.setLocationAndPOV(latlng);

    addMarker(latlng);
  };

  this.getName = function() {
    return "Street view";
  };

  this.end = function() {
    map.removeOverlay(svOverlay);
    if (marker != null)
      map.removeOverlay(marker);
    $('#streetView').attr("title", "Street view");
    $('#streetViewDialog').dialog('close');
  }
}
