﻿

// Collections
var points = [];
var marker = null;
var highlighted_marker = null;
var point_markers = [];
var metricType='Kilometers';
var milestoneMarkers=[];

function processChanges( moveMap )
{
	// Reincode for saving
	createEncodings(moveMap);
	
	// Update UI - total distance
	displayTotalDistance( );	
}

// Add a point to the points list.
function addPoint( lat, lng, level ) {
  var pLat = parseFloat(lat);

  if (pLat.toString() != lat) {
    alert('Invalid latitude entered. Must be in range of -90 to 90');
    return;
  }

  if (pLat < -90 || pLat > 90) {
    alert('Invalid latitude entered. Must be in range of -90 to 90');
    return;
  }

  var pLong = parseFloat(lng);

  if (pLong.toString() != lng) {
    alert('Invalid longitude entered. Must be in range of -180 to 180');
    return;
  }

  if (pLong < -180 || pLong > 180) {
    alert('Invalid longitude entered. Must be in range of -180 to 180');
    return;
  }

  var pLevel = parseInt(level);

  if (pLevel.toString() != level) {
    alert('Invalid minimum level entered. Must be in range of 0 to 3');
    return;
  }

  if (pLevel < 0 || pLevel > 3) {
    alert('Invalid minimum level entered. Must be in range of 0 to 3');
    return;
  }

  createPoint(lat, lng, pLevel);
  processChanges(false);
  
}

function addExistingPoint( iIndex )
{
   // Find existing point details
   var pLat = points[ iIndex ].Latitude;
   var pLng = points[ iIndex ].Longitude;
   var pLevel = points[ iIndex ].Level;

   // Add point again
	createPoint( pLat, pLng, pLevel);
   processChanges(false);
}


// Returns the index of the marker in the polyline.
function findMarkerIndex(point_marker) {
  var index = -1;

  for (var  i = 0; i < point_markers.length; ++i) {
    if (point_markers[i] == point_marker) {
      index = i;
      break;
    }
  }

  return index;
}

// Creates a point and adds it to both the polyline and the list.
function createPoint(lat, lng, pLevel) {
  
  var newPoint = {
    Latitude: lat,
    Longitude: lng,
    Level: pLevel
  };

  points.push(newPoint);

  if (marker) {
    document.map.removeOverlay(marker);
    marker = null;
  }

  var point_marker = createPointMarker(new GLatLng(lat, lng), false);
  document.map.addOverlay(point_marker);
  point_markers.push(point_marker);
}

// Creates a marker representing a point in the polyline.
function createPointMarker(point, highlighted) {
  var clr = highlighted ? "yellow" : "blue";

  var point_marker = createMarker(point, clr);
  point_marker.enableDragging();

  GEvent.addListener(point_marker, "drag", function() {
    var index = findMarkerIndex(point_marker);

    if (index >= 0) {
      var nLat = point_marker.getPoint().lat();
      var nLng = point_marker.getPoint().lng();

      var pLevel = points[index].Level;

      var modifiedPoint = {
        Latitude: nLat,
        Longitude: nLng,
        Level: pLevel
      };

      points[index] = modifiedPoint;
      processChanges(false);
      
    }
  });

  GEvent.addListener(point_marker, "click", function() {
    highlight(findMarkerIndex(point_marker));
  });

  return point_marker;
}



// Encode a signed number in the encode format.
function encodeSignedNumber(num) {
  var sgn_num = num << 1;

  if (num < 0) {
    sgn_num = ~(sgn_num);
  }

  return(encodeNumber(sgn_num));
}

// Encode an unsigned number in the encode format.
function encodeNumber(num) {
  var encodeString = "";

  while (num >= 0x20) {
    encodeString += (String.fromCharCode((0x20 | (num & 0x1f)) + 63));
    num >>= 5;
  }

  encodeString += (String.fromCharCode(num + 63));
  return encodeString;
}



// Try to encode an unsigned number. Used by the documentation.
function tryEncode() {
  var txtValue = document.getElementById('txtNumber').value;
  if (parseInt(txtValue).toString() == txtValue) {
    document.getElementById('cdeValue').innerHTML = encodeNumber(parseInt(txtValue));
  }else{
    document.getElementById('cdeValue').innerHTML = '(None)';
  }
}

// Try to encode a signed number. Used by the documentation.
function trySignEncode() {
  var txtValue = document.getElementById('txtSignNumber').value;
  if (parseInt(txtValue).toString() == txtValue) {
    document.getElementById('cdeSignValue').innerHTML = encodeSignedNumber(parseInt(txtValue));
  }else{
    document.getElementById('cdeSignValue').innerHTML = '(None)';
  }
}

// Create the encoded polyline and level strings. If moveMap is true
// move the map to the location of the first point in the polyline.
function createEncodings(moveMap) {
  var i = 0;

  var plat = 0;
  var plng = 0;

  var encoded_points = "";
  var encoded_levels = "";

  for(i = 0; i < points.length; ++i) {
    var point = points[i];
    var lat = point.Latitude;
    var lng = point.Longitude;
    var level = point.Level;

    var late5 = Math.floor(lat * 1e5);
    var lnge5 = Math.floor(lng * 1e5);

    dlat = late5 - plat;
    dlng = lnge5 - plng;

    plat = late5;
    plng = lnge5;

    encoded_points += encodeSignedNumber(dlat) + encodeSignedNumber(dlng);
    encoded_levels += encodeNumber(level);
  }

  // move if moveMap is true.
  if (moveMap) {
    document.map.setCenter(
        new GLatLng(points[0].Latitude, points[0].Longitude),
        document.map.getZoom());
  }

  document.getElementById('encodedLevels').value = encoded_levels;
  document.getElementById('encodedPolyline').value = encoded_points;
  document.getElementById('totalDistance').value =  getTotalDistance('Meters');

  if (document.overlay) {
    document.map.removeOverlay(document.overlay);
  }
  
  if (points.length > 1) {
    document.overlay = GPolyline.fromEncoded({color: "#0000FF",
                                              weight: 10,
                                              points: encoded_points,
                                              zoomFactor: 32,
                                              levels: encoded_levels,
                                              numLevels: 4
                                             });

    document.map.addOverlay(document.overlay);
  }
}

// Decode an encoded polyline into a list of lat/lng tuples.
function decodeLine (encoded) {
  var len = encoded.length;
  var index = 0;
  var array = [];
  var lat = 0;
  var lng = 0;

  while (index < len) {
    var b;
    var shift = 0;
    var result = 0;
    do {
      b = encoded.charCodeAt(index++) - 63;
      result |= (b & 0x1f) << shift;
      shift += 5;
    } while (b >= 0x20);
    var dlat = ((result & 1) ? ~(result >> 1) : (result >> 1));
    lat += dlat;

    shift = 0;
    result = 0;
    do {
      b = encoded.charCodeAt(index++) - 63;
      result |= (b & 0x1f) << shift;
      shift += 5;
    } while (b >= 0x20);
    var dlng = ((result & 1) ? ~(result >> 1) : (result >> 1));
    lng += dlng;

    array.push([lat * 1e-5, lng * 1e-5]);
  }

  return array;
}

// Decode an encoded levels string into a list of levels.
function decodeLevels(encoded) {
  var levels = [];

  for (var pointIndex = 0; pointIndex < encoded.length; ++pointIndex) {
    var pointLevel = encoded.charCodeAt(pointIndex) - 63;
    levels.push(pointLevel);
  }

  return levels;
}

// Decode the supplied encoded polyline and levels.
function decode() {

  var encoded_points = document.getElementById('encodedPolyline').value;
  var encoded_levels = ''; // document.getElementById('encodedLevels').value;

  if (encoded_points.length==0 ) { // || encoded_levels.length==0) {
    return;
  }

var enc_points = decodeLine(encoded_points);
alert(enc_points);
alert(enc_points.length);
// Create fake encoded levels
for (var eachpnt = 0; eachpnt < enc_points.length; eachpnt++) {
    encoded_levels = encoded_levels + 'B';
}

document.getElementById('encodedLevels').value = encoded_levels;

alert(encoded_levels);

// NB: No longer stored in DB
  var enc_levels = decodeLevels(encoded_levels);

  if (enc_points.length==0 || enc_levels.length==0) {
    return;
}

//alert('pnts:' + enc_points.length);
//alert('lvls:' + enc_levels.length);
alert(encoded_points);
alert(enc_levels);
//alert('5');

document.overlay = GPolyline.fromEncoded({ color: "#0000FF",
    weight: 10,
    points: encoded_points,
    zoomFactor: 32,
    levels: encoded_levels,
    numLevels: 4
});

document.map.addOverlay(document.overlay);
return;

if (enc_points.length != enc_levels.length) {

    alert('Point count and level count do not match');
    return;
  }

  alert('6');
  
  points = [];

  for (var i = 0; i < enc_points.length; ++i) {
    createPoint(enc_points[i][0], enc_points[i][1], enc_levels[i]);
  }

  processChanges(true);
}

function createMarker(point, color) {
  var f = new GIcon();
  f.image = "http://labs.google.com/ridefinder/images/mm_20_" + color + ".png";
  f.shadow = "http://labs.google.com/ridefinder/images/mm_20_shadow.png";
  f.iconSize = new GSize(12,20);
  f.shadowSize = new GSize(22,20);
  f.iconAnchor = new GPoint(6,20);
  f.infoWindowAnchor = new GPoint(6,1);
  f.infoShadowAnchor = new GPoint(13,13);

  newMarker = new GMarker(point,
    {icon: f,
     draggable: true});

  return newMarker;
}


function displayTotalDistance( )
{
   // Get distance label
	var lDistance = document.getElementById('spDistance'); 
	
	// Update label
   lDistance.innerHTML = getTotalDistance( metricType ).toFixed( 2 );
 }
 
// Create the Google Map to be used.
 function createMap() {
     
  if (!GBrowserIsCompatible()) {
    alert('Your browser is not compatible with the Google Maps API');
    return;
  }

  document.map = new GMap2(document.getElementById("map_canvas"));
  document.map.setCenter(new GLatLng(37.4419, -122.1419), 13);
  document.map.addControl(new GLargeMapControl());
  //document.map.addControl(new GOverviewMapControl());
  document.map.enableScrollWheelZoom();


  /*if (document.overlay) {
      document.map.removeOverlay(document.overlay);
  }

  var mencodedlvl = document.getElementById('encodedLevels').value = encoded_levels;
  var mencodedpnts = document.getElementById('encodedPolyline').value = encoded_points;

  if (points.length > 1) {
      document.overlay = GPolyline.fromEncoded({ color: "#0000FF",
          weight: 10,
          points: mencodedpnts,
          zoomFactor: 32,
          //levels: encoded_levels,
          numLevels: 4
      });

      document.map.addOverlay(document.overlay);
  }

  return;*/
  // If the document has encoded markers, load them automatically
  var levelsCheck =  document.getElementById('encodedLevels').value;

  if (levelsCheck) {
      decode();
  }
  else {
      alert('Sorry, that route could not be loaded');
  }

}

// Move the map to the selected point in the point list.
function jumpToPoint() {
  var pointList = document.getElementById('pointList');
  if (pointList.selectedIndex >= 0) {
    var point = points[pointList.selectedIndex];
    document.map.setCenter(new GLatLng(point.Latitude, point.Longitude),
                           document.map.getZoom());
  }
}

function getTotalDistance(mt)
{
	var distance=0;
	var metricConversion=1;
	
	// Miles in a single meter
	if(mt=='Miles') { metricConversion=0.000621371192; }
	
	// Kilometers in a single meter 
	if(mt=='Kilometers')	{ metricConversion=0.001;}

   // Get distance between points in meters
	for(var i=1;i<points.length;i++)
	{
	   distance += distanceBetweenPoints( i, i - 1, 1, document.map.getZoom() );
	}
	
	// Convert to desired distance measurement system
	var totalDistance =(distance*metricConversion);
	return totalDistance;
}

function addDistanceMarker(i, segmentDistance, totalDistance, remainingDistance)
{
   // Number of additional markers we need in this segment
	remainingDistance=totalDistance-milestoneMarkers.length;
	
	// Create point using current lat and previous long
	var tempPoint=new GLatLng(points[i].Latitude,points[i-1].Longitude);

   // Determine points for percentage calc
	var xDist = points[i].Longitude - tempPoint.lng();
	var yDist = points[i-1].Latitude - tempPoint.lat();
	
	// Calc percent along line
	percentAlongLine=(1-(remainingDistance-segmentDistance))/segmentDistance;

	// New lat & lng
	var newLat = tempPoint.lat() + ((1-percentAlongLine)*yDist);
	var newLng = parseFloat( tempPoint.lng() ) + parseFloat(((percentAlongLine)*xDist ));
	
	// Milestone marker
	var distanceMarkerPosition = new GLatLng( newLat, newLng);
	var distanceMarker = createMarker(distanceMarkerPosition, "yellow");
	
	document.map.addOverlay(distanceMarker);  
	remainingDistance--;
	milestoneMarkers.push(distanceMarker);
	return(remainingDistance);
}

function clearMilestoneMarkers()
{
	for(var i=0;i<milestoneMarkers.length;i++)
	{
		document.map.removeOverlay(milestoneMarkers[i]);
	}
	
	milestoneMarkers=[];
	
	// Redisplay distance 
   displayTotalDistance( metricType );
}

function showMilestones()
{
   var segmentDistance = 0;
   var remainingDistance = 0;
   var distance = 0.0;
   var metricConversion = 1.0;
   if(metricType=='Miles'){metricConversion = 0.000621371192;}
   if(metricType=='Kilometers'){metricConversion = 0.001;}
   
   // Clear existing markers
   clearMilestoneMarkers();
   
   // Loop through each point in array starting at second point
   for(var i=1;i<points.length;i++)
   {
      // Measure distance between current point and previous, add to total
      segmentDistance = distanceBetweenPoints( i, i - 1, metricConversion, document.map.getZoom() );
      distance += segmentDistance;
   
      // While the number of markers we need is greater than the number of markers we have
      while(Math.floor(distance)>milestoneMarkers.length)
      {
         remainingDistance=addDistanceMarker(i, segmentDistance, distance, remainingDistance);
      }
   }
}

function distanceBetweenPoints( firstPoint, secondPoint, measurementSystem, dZoom )
{
   // Grab each point as lat/lng pairs
   var p1 = new GLatLng( points[ firstPoint ].Latitude, points[ firstPoint ].Longitude, dZoom );
	var p2 = new GLatLng( points[ secondPoint ].Latitude, points[ secondPoint].Longitude, dZoom );
	
	// Calculate distance 
	return measurementSystem * p2.distanceFrom( p1 );
}



// Set the mapping mode - normal, satellite, hybrid etc
function setMode( gType )
{
	document.map.setMapType( gType );
}

// Set metric to Miles or Kilometers
function setMetric( gMetric )
{
   // Get distance
	var lMetric = document.getElementById('spMetric'); 
	
	// Update label and variable
   lMetric.innerHTML = gMetric;
   metricType = gMetric;
  
   // Redisplay distance 
   displayTotalDistance();
}

// Set metric to Miles or Kilometers
function setMiles()
{
   // Get distance
	var lMetric = document.getElementById('spMetric'); 
	
	// Update label and variable
   lMetric.innerHTML = 'Miles';
   metricType = 'Miles';
  
   // Redisplay distance 
   displayTotalDistance();
}


// Set metric to Miles or Kilometers
function setKM()
{
   // Get distance
	var lMetric = document.getElementById('spMetric'); 
	
	// Update label and variable
   lMetric.innerHTML = 'Kilometers';
   metricType = 'Kilometers';
  
   // Redisplay distance 
   displayTotalDistance();
}
