// global variables and constants
var map;
var route, featurePoints;
var profile;
var minZoom = 8;
var maxZoom = 10;
var scaleSize = 2150; // meters
var earthRadius = 6378137; // meters
var currentZoom, targetZoom;
var allMarkers, markerMgr;
var lstnr;
var mapCentre = new GLatLng(33.963295, 77.525711);

// constructor for an "anchor" point of the route
function FeaturePoint(latlng, elevation, title) {
  this.ll = latlng;
  this.elevation = elevation; // in meters above the lowest point
  this.title = title + ", " + (3200 + elevation) + "m";
}

// continuous zoom will use this function
GPolygon.prototype.hide = function() {
  map.removeOverlay(this);
};

// map setup
function load(lang) {
  if (GBrowserIsCompatible()) {
    // create new Map object
    map = new GMap2(document.getElementById("map"), {mapTypes:[G_HYBRID_MAP]});
    map.enableDoubleClickZoom();
    map.enableContinuousZoom();
    map.addControl(new GSmallMapControl());
    map.addControl(new GScaleControl());
    map.addControl(new GOverviewMapControl());
    if (document.getElementById("map_overview")) {
      // adjust overview map
      var omDiv = document.getElementById("map_overview").firstChild;
      omDiv.style.backgroundColor = "#dadada";
      omDiv.style.bottom = "0px";
      omDiv.style.right = "0px";
    }
    // position the map
    map.setCenter(mapCentre, 11, G_HYBRID_MAP);
    map.savePosition();
    map.profile = false;
    // in case profile is visible, it should be redrawn after each zoom
    GEvent.addListener(map, "zoomend", function(oldlevel, newlevel) {
      if (map.profile && (newlevel < oldlevel && newlevel < minZoom || newlevel > oldlevel && newlevel > maxZoom))
        toggleProfile();
      else if (profile)
        redrawProfile(newlevel);
    });

    if (navigator.appName.indexOf("Microsoft") < 0 || document.compatMode) {
      // add polyline in all browsers but IE 6-
      var encodedPoints = "qvhoEsb`xMeMl}AhTjnAuAfiBcFb^eElM?zKhFpJlGfJx@?Lz@v@xJxFx`@vX`OzGj_A{OtWdUnc@zOnFpBb^hTc@hL?~FzKhl@eQb^{h@x_@{KtYxJpJmE`VhCfU{YrQcAnSjSz_@kDzO}LlcAnFhl@lb@|OxeAv@pd@iDnUoSf`@xHtcApRbyAtQxtApR`{@hD~[bNha@~V|sBhD|ZxPdn@vPvX~VhCjLxYv@zKbV~\\fMgCtYpVbVhCvHeBxHhDvHvIxHtHlC`Of]lb@pRpVxp@rGqB_\\x@uWpRqVzGcO|W}i@zOia@nK_N~^mTnKmShLwJ_Gka@jDuu@tQoc@bn@iR`OsVvHip@rZ{Kl[ucAxPqGpBwXrb@en@jd@mEjTor@ld@ePnBoaAxy@iR~NqaAfM}vAfv@mnAn[uaBv@ia@nKe_@f]mExP}gAdVwIxiAqnBdV?lmAijBn[?df@yv@h]e_@vYen@h]ia@|Gc^pByYyHyYaGc{@vIyh@uA_k@tj@kp@rZyh@vYia@qBg}@}G}x@aWal@sJmq@wi@go@}Og`@[{w@cg@ePoK{ZmC}ZwYal@uImTnSscAcFsVyHQyDae@mGePuv@oUsN}b@yDiRwHwBeJi`@cN}LmG`@gMyCgAyRqBuWkL{KyiA|[m\\_NcNdBy`@uf@yiAoU{p@cm@eVgQ";
      var encodedLevels = "PFFFFFFFFFFFFFFFJFFFFFFFFFFFFFFFJFFFFFFHFFFFFFFHFFFFFFFFFFFFFFFJFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFHFFFHFFJFFFFFFFFFFFHFFFFFFFFFFFFFFFJHFFFFFP";
      route = GPolyline.fromEncoded({
        color: "#0000ff",
        weight: 10,
        points: encodedPoints,
        levels: encodedLevels,
        zoomFactor: 2,
        numLevels: 18
      });
      map.addOverlay(route);
    }

    // define icons
    var baseIcon = new GIcon();
    baseIcon.shadow = "shadow.png";
    baseIcon.iconSize = new GSize(23, 23);
    baseIcon.shadowSize = new GSize(41, 23);
    baseIcon.iconAnchor = new GPoint(12, 23);
    var startIcon = new GIcon(baseIcon, "start.png");
    startIcon.printImage = "start.gif";
    startIcon.mozPrintImage = "start.gif";
    var stopIcon = new GIcon(baseIcon, "stop.png");
    stopIcon.printImage = "stop.gif";
    stopIcon.mozPrintImage = "stop.gif";
    var passIcon = new GIcon(baseIcon, "pass.png");
    passIcon.printImage = "pass.gif";
    passIcon.mozPrintImage = "pass.gif";
    var pauseIcon = new GIcon(baseIcon, "pause.png");
    pauseIcon.printImage = "pause.gif";
    pauseIcon.mozPrintImage = "pause.gif";
    // create markers
    var markers = new Array();
    var startMarker = new GMarker(new GLatLng(34.125359, 77.50612), {icon:startIcon, clickable:false, title:"Spituk"});
    markers.push(startMarker);
    var stopMarker = new GMarker(new GLatLng(33.906896, 77.732906), {icon:stopIcon, clickable:false, title:"Martselang"});
    markers.push(stopMarker);
    var passPoints = [
      new FeaturePoint(new GLatLng(34.04185,  77.361774), 1720, "Ganda La"),
      new FeaturePoint(new GLatLng(33.811244, 77.623686), 1950, "Kongmaru La")
    ];
    for (var id in passPoints)
      markers.push(new GMarker(passPoints[id].ll, {icon:passIcon, clickable:false, title:passPoints[id].title}));
    var campMarkers = new Array();
    var pausePoints = [
      new FeaturePoint(new GLatLng(34.085934, 77.416534),  300, "Jingchan"),
      new FeaturePoint(new GLatLng(34.045832, 77.379456), 1300, "Yurutse"),
      new FeaturePoint(new GLatLng(33.970837, 77.284698),  200, "Skiu"),
      new FeaturePoint(new GLatLng(33.891507, 77.420654),  600, "Markha"),
      new FeaturePoint(new GLatLng(33.813756, 77.557648), 1050, "Tahungste"),
      new FeaturePoint(new GLatLng(33.8034,   77.609138), 1530, "Nimaling"),
      new FeaturePoint(new GLatLng(33.851742, 77.706612),  500, "Shang Sumdo")
    ];
    for (var id in pausePoints)
      campMarkers.push(new GMarker(pausePoints[id].ll, {icon:pauseIcon, clickable:false, title:pausePoints[id].title}));
    if (route) {
      // initialize profile's anchor points from points with known elevation
      featurePoints = new Array();
      featurePoints.push(new FeaturePoint(route.getVertex(0), 0));
      for (var id in pausePoints) {
        featurePoints.push(id == 2 ? new FeaturePoint(new GLatLng(33.979382, 77.261009), 200) : pausePoints[id]);
        if (id == 1)
          featurePoints.push(passPoints[0]);
        else if (id == 5)
          featurePoints.push(passPoints[1]);
      }
      featurePoints.push(new FeaturePoint(route.getVertex(route.getVertexCount() - 1), 250));
    }
    // save references to all markers
    allMarkers = markers.concat(campMarkers);
    // add markers to the map
    markerMgr = new GMarkerManager(map);
    markerMgr.addMarkers(markers, 7);
    markerMgr.addMarkers(campMarkers, 10);
    markerMgr.refresh();
    markerMgr.isActive = true;
  } else {
    var warntext;
    if (lang && lang == "en")
      warnText = "Unfortunately, your browser isn't supported by Google Maps. A list of <a href='http://maps.google.com/support/bin/answer.py?answer=16532&amp;topic=1499'>compatible browser versions</a> is available from a Google site.";
    else
      warnText = "Leider wird Ihr Browser von Google Maps nicht unterst&uuml;tzt. Eine <a href='http://maps.google.com/support/bin/answer.py?answer=16532&amp;topic=1499'>Liste unterst&uuml;tzter Browserversionen</a> ist direkt bei Google einsehbar.";
    document.getElementById("map").innerHTML = warnText;
  }
}

function hideMarkers() {
  for (var id in allMarkers)
    try {
      allMarkers[id].savedPoint = allMarkers[id].getPoint();
      allMarkers[id].setPoint(new GLatLng(91, 0));
    } catch (e) {
    }
  markerMgr.refresh();
  markerMgr.isActive = false;
}

function showMarkers() {
  for (var id in allMarkers)
    try {
      allMarkers[id].setPoint(allMarkers[id].savedPoint);
    } catch (e) {
    }
  markerMgr.refresh();
  markerMgr.isActive = true;
}

// calculates and returns an "elevated" point as moved north from its real map position
function getElevatedPoint(latlng, dh, zoomFactor) {
  var newLat = latlng.lat() + dh * zoomFactor * 180 / (earthRadius * Math.PI);
  return new GLatLng(newLat, latlng.lng());
}

// creates profile overlays and adds them to the map
function createProfile(startFeature, endFeature, zoom) {
  var zoomFactor = Math.pow(2, 13 - zoom); // to use in getElevatedPoint()
  var profilePoints = new Array();
  var bgPoints = new Array();
  for (var id = endFeature; id >= startFeature; id--) {
    profilePoints.push(featurePoints[id].ll);
    bgPoints.push(featurePoints[id].ll);
  }
  for (var id = startFeature; id <= endFeature; id++) {
    profilePoints.push(getElevatedPoint(featurePoints[id].ll, featurePoints[id].elevation, zoomFactor));
    bgPoints.push(getElevatedPoint(featurePoints[id].ll, scaleSize, zoomFactor));
  }
  // return an object with two GPolygons as properties
  var result = new Object();
  result.bg = new GPolygon(bgPoints, null, 0, 0, "#3333cc", 0.5, {outline:false});
  map.addOverlay(result.bg);
  result.profile = new GPolygon(profilePoints, null, 0, 0, "#ccccff", 0.75, {outline:false});
  map.addOverlay(result.profile);
  return result;
}

// returns a GLatLngBounds enclosing profile for the given zoom level
function getProfileBounds(zoom) {
  return new GLatLngBounds(
    new GLatLng(featurePoints[7].ll.lat(), featurePoints[4].ll.lng()),
    new GLatLng(getElevatedPoint(featurePoints[0].ll, scaleSize, Math.pow(2, 13 - zoom)).lat(), featurePoints[10].ll.lng())
  );
}

// removes profile overlays
function removeProfile() {
  if (profile) {
    if (profile.part2) {
      map.removeOverlay(profile.part2.profile);
      map.removeOverlay(profile.part2.bg);
    }
    if (profile.part1) {
      map.removeOverlay(profile.part1.profile);
      map.removeOverlay(profile.part1.bg);
    }
    profile = null;
  }
}

// draws route profile for the given zoom level
function redrawProfile(zoom) {
  removeProfile();
  profile = new Object();
  profile.part1 = createProfile(0, 4, zoom);
  profile.part2 = createProfile(4, featurePoints.length - 1, zoom);
}

// calculates map bounds at the given zoom level
function getMapBounds(centre, zoom) {
  var proj = map.getCurrentMapType().getProjection();
  var c = proj.fromLatLngToPixel(centre, zoom);
  var size = map.getSize();
  var sw = proj.fromPixelToLatLng(new GPoint(c.x - size.width/2, c.y + size.height/2), zoom);
  var ne = proj.fromPixelToLatLng(new GPoint(c.x + size.width/2, c.y - size.height/2), zoom);
  return new GLatLngBounds(sw, ne);
}

// zoom if necessary
function checkZoom() {
  if (currentZoom > targetZoom) {
    do
      map.zoomOut(null, true);
    while (--currentZoom > targetZoom);
  } else if (currentZoom < targetZoom) {
    do
      map.zoomIn(null, false, true);
    while (++currentZoom < targetZoom);
  }
}

// main profile function
function toggleProfile() {
  map.profile = route && !map.profile;
  if (map.profile) {
    // draw profile
    hideMarkers();
    currentZoom = map.getZoom();
    targetZoom = currentZoom < minZoom ? minZoom : (currentZoom > maxZoom ? maxZoom : currentZoom);
    if (!getMapBounds(map.getCenter(), targetZoom).containsBounds(getProfileBounds(targetZoom))) {
      // pan to include the whole profile in the viewport
      lstnr = GEvent.addListener(map, "moveend", function() {
        GEvent.removeListener(lstnr);
        checkZoom();
      });
      map.panTo(mapCentre);
    } else
      checkZoom();
    redrawProfile(targetZoom);
  } else {
    // remove profile
    removeProfile();
    if (!markerMgr.isActive)
      showMarkers();
  }
}
