/*
   map_river.js
   
   Javascript support for river controller.
   Implements showing of markers on the map, the interaction with the
   panelinfo div, and also editing functionality. 
 */

// River markers
var icons = {};                   // Markers (statically initialized in mapInit)
var markers = {};                 // [id,id... id]
var lineMarkers = [];             // [{marker:<GMarker>, point:<GLatLng>}...]
var linePoints = [];              // [<GLatLng>,<GLatLng>,...] for creating line
var line = null;                  // <GPolyline>
var bounds = null;                // <GBounds>  Bounds of the river points
var grades = null;                // [grade list]
var pointSelected = 0;            

// Editing state
var editing = false;              // True if we are editing
var pointsEdited = false;         // True if user changed any points during editing
var nextIndex = -1;               // Indexes of new river points (used to keep track of them)
var iconSelected = '';            // Selected icon in edit menu
var editmarker = null; // hack

// Data passed in from page
var river_id = 0;                 // The river we are viewing/editing
var playspot = 0;                 // True if playspot (necessary for new rivers where id=0)
var user_location = null;         // Used for directions
var user_id = null;               // Only used to see if we are logged in


function mapInit(params) {
  // Initialize globals with data from page load
  river_id = params.river_id;
  playspot = params.playspot;
  user_location = params.user_location;
  user_id = params.user_id;
  
  // Initialize map
  initGMap(params.mapstate);
    
  // Prepare edit menu (and make sure it is hidden)
  var pos = new GControlPosition(G_ANCHOR_TOP_RIGHT, new GSize(10,50));
  pos.apply(document.getElementById("menu"));
  map.getContainer().appendChild(document.getElementById("menu"));
  $('menu').hide();
  
  // Create icons
  initIcons();
  
  // Load the data 
  if (river_id == 0) {
    editStart();
  } else {
    loadData(true, params.edit);
  }
  
  initListeners();
}


function initIcons() {
  // Set up common parameters
  var gicon = new GIcon();
  gicon.image = "/images/large/r.png";
  gicon.maxHeight = 0.1;    // disable drag floating
  gicon.dragCrossImage = "";    // hide drag cross
  gicon.dragCrossSize = new GSize(0,0); // hide drag cross

  // Create river icon
  gicon.iconSize = new GSize(20, 20);
  gicon.iconAnchor = new GPoint(10, 10);
  gicon.infoWindowAnchor = new GPoint(10, 10);      
  icons['river'] = new GIcon(gicon, "/images/river.png");
  
  // Create the other ones
  gicon.iconSize = new GSize(30, 30);
  gicon.iconAnchor = new GPoint(15, 15);
  gicon.infoWindowAnchor = new GPoint(15, 15);      
  icons['rapid'] = new GIcon(gicon, "/images/rapid.png");
  icons['takeout'] = new GIcon(gicon, "/images/takeout.png");
  icons['putin'] = new GIcon(gicon, "/images/putin.png");
  icons['portage'] = new GIcon(gicon, "/images/portage.png");
  icons['camping'] = new GIcon(gicon, "/images/camping.png");
  icons['playspot'] = new GIcon(gicon, "/images/playspot.png");
  icons['other'] = new GIcon(gicon, "/images/other.png");
}

// Set up Listeners
function initListeners() {
  // maptypechanged- notifies controller so we can save the state
  GEvent.addListener(map, "maptypechanged", function() {
    var maptype = maptypeEnumToString(map.getCurrentMapType());
    new Ajax.Request("/river/ajax_maptype_changed", {parameters: {maptype: maptype}});
  });

  // click- in case of editing clicking means adding a new marker
  GEvent.addListener(map, "click", function(marker, latlng) {
    if (marker) {
      // Ignore event, pass on to marker listeners
    } else 
    if (editing && iconSelected != '') {
      closeEdit(); // wrap up in case we are editing something
      pointsEdited = true;
      if (iconSelected == 'river') {
        newLineMarker(latlng);
      } else {
        var marker = createMarker(latlng, iconSelected);
        marker.enableDragging();
        marker.openInfoWindowHtml(formMarker(marker));
      }
    }
  });
}


// Get the river markers and display them when they arrive
function loadData(reposition, startedit) {
  // Clear all existing points and tracking arrays
  map.clearOverlays();
  markers = {};  
  lineMarkers = [];
  linePoints = []; 
  line = null;
  
  // Request the points from the controller
  new Ajax.Request("/river/river_markers/" + river_id, {
    method: 'get',
    asynchronous: true,
    onSuccess: function(transport) {
      var response = eval( "(" + transport.responseText + ")" );
      var points = response.points;
      grades = response.grades;     // These are saved for rapid infowindows 
      bounds = new GLatLngBounds();
      if (points.length > 0) {
        for (var i = 0; i<points.length; i++) {
          var p = points[i];
          if (p.ptype == 'river')  {
            initLine(p);
          } else {
            initMarker(p);
          }
          
          var point = new GLatLng(p.lat, p.lng);
          bounds.extend(point);
        }
        redrawRiverline();
        
        // Position map to fit the data
        if (reposition) {
          setBounds();
        }
      }
      if (startedit) {
        editStart();
      }
    }
  });
}

function resetLineIndex() {
  for (k=0; k<lineMarkers.length; k++)  {
    lineMarkers[k].pindex = k;
  }
}

// Create a new point based on a latlng location
function createMarker(latlng, ptype) {
  var point = {};
  point.name = '';
  point.notes = '';
  point.grade_id = 0;
  point.pindex = 0;
  point.ptype = ptype;
  point.lat = latlng.lat();
  point.lng = latlng.lng();
  point.id = (nextIndex--);
  
  // Put it on the map and in the data structures
  var marker = initMarker(point);
  extendBounds(latlng);
  return marker;
}


function newLineMarker(latlng) {
  var marker = createLineMarker(latlng);
  extendBounds(latlng);
  marker.point_id = (nextIndex--);
  
  // Add it in the correct position in the arrays
  var done = false;
  if (line && line.getBounds().contains(latlng)) {
    // This strange bit of code makes sure that if you click between two existing points
    // then the points is inserted in the middle of the line as opposed to the end of it.
    // TODO could be improved! (eg u shaped rivers etc)
    for (var i=0; !done && i<lineMarkers.length-1; i++) {
      p1 = linePoints[i];   // use the lat/lng of the marker, since it may be moved
      p2 = linePoints[i+1];
      if (latlng.lat() > min(p1.lat(),p2.lat()) && latlng.lat() < max(p1.lat(),p2.lat()) &&
          latlng.lng() > min(p1.lng(),p2.lng()) && latlng.lng() < max(p1.lat(),p2.lat()) ) {

          // Found a place to insert the marker
          marker.pindex = i+1;
          lineMarkers.splice(i+1, 0, marker);
          linePoints.splice(i+1, 0, latlng);

          // Reset indexes
          resetLineIndex();
          done = true;
      }
    }
  }
  if (!done) {
    marker.pindex = lineMarkers.length;
    lineMarkers.push(marker);
    linePoints.push(latlng);
  }
  
  redrawRiverline();
  return marker;
}

function createLineMarker(latlng) {
  // Create map marker
  var marker = new GMarker(latlng, {icon:icons['river'], draggable:true});
  map.addOverlay(marker);
  addLineListeners(marker);
  return marker;
}

function initLine(point) {
  var latlng = new GLatLng(point.lat, point.lng);
  var marker = createLineMarker(latlng);
  marker.point_id = point.id;
  marker.pindex = point.pindex; 
  marker.disableDragging();
  lineMarkers.push(marker);
  linePoints.push(latlng);
  marker.hide();
}

function initMarker(point) {
  // Create map marker
  var marker = new GMarker(new GLatLng(point.lat, point.lng), {icon:icons[point.ptype], draggable:true});
  marker.disableDragging();
  marker.point_id = point.id; 
  map.addOverlay(marker);

  markers[point.id] = {point:point, marker:marker};
  addMarkerListeners(marker);
  return marker;
}

// Called by infowindow event or if user clicks point in panel
function selectPoint(pointid) {
  if (pointSelected && tabCurrent(tab) == 'points') {
    $('point-' + pointSelected).removeClassName("pointselect");
  }

  if (pointid == pointSelected) {
    map.closeInfoWindow();
    pointSelected = null;
  } else {
    var marker = markers[pointid].marker;
    if (editing) {
      marker.openInfoWindowHtml(formMarker(marker));
    } else {
      marker.openInfoWindowHtml(infoMarker(marker));
    }
    if (tabCurrent(tab) == 'points') {
      $('point-' + pointid).addClassName("pointselect");
    }
    pointSelected = pointid;
  }
}

function addMarkerListeners(marker) {
  // Click events: Bring up form or info
  GEvent.addListener(marker, "click", function() {
    selectPoint(marker.point_id);
  });

  GEvent.addListener(marker, "dragstart", function() {
    map.closeInfoWindow();
  });
  
  GEvent.addListener(marker, "dragend", function() {
    latlng = marker.getLatLng();
    markers[marker.point_id].point.lat = latlng.lat();
    markers[marker.point_id].point.lng = latlng.lng();
    pointsEdited = true;
  });
  
  // Save information when window closes
  GEvent.addListener(marker, "infowindowbeforeclose", function() {
    if (editing) {
      var id = marker.point_id;
      markers[id].point.name = $("name").value;
      markers[id].point.notes = $("notes").value;
      if (markers[id].point.ptype == 'rapid') {
        markers[id].point.grade_id = $("grade").value;
      }
      pointsEdited = true;
    }
  });
}

function addLineListeners(marker) {
  GEvent.addListener(marker, "click", function() {
    // Click means delete river marker
    map.removeOverlay(marker);            
    lineMarkers.splice(marker.pindex, 1);
    linePoints.splice(marker.pindex, 1);
    resetLineIndex();
    redrawRiverline();
    pointsEdited = true;
  });
  
  GEvent.addListener(marker, "drag", function()  {
    linePoints[marker.pindex] = marker.getLatLng();
    redrawRiverline();
    pointsEdited = true;
  });
}
  

function setBounds() {
  if (bounds) {
    map.setZoom(min(17,map.getBoundsZoomLevel(bounds)));
    map.setCenter(bounds.getCenter());
  }
}  

function extendBounds(latlng) {
  if (!bounds) {
    bounds = new GLatLngBounds();
  }
  bounds.extend(latlng);
}

function redrawRiverline() {
  if (line) {
    map.removeOverlay(line);
  }
  if (linePoints.length > 0) {
    line = new GPolyline(linePoints);
    map.addOverlay(line);
  }
}


function enableDrag() {
  for (var id in markers) {
    markers[id].marker.enableDragging();
  }
  for (var i=0; i < lineMarkers.length; i++) {
    lineMarkers[i].enableDragging();
  }
}

function disableDrag() {
  for (var id in markers) {
    markers[id].marker.disableDragging();
  }
  for (var i=0; i < lineMarkers.length; i++) {
    lineMarkers[i].disableDragging();
  }
}

// Called from view when user presses edit button
function editStart() {
  enableDrag();
  pointsEdited = false;
  map.closeInfoWindow();
  
  disableDiv('panelinfo', "<img src='/images/progress.gif'> Loading...");
  new Ajax.Request("/river/ajax_edit_start", {
    asynchronous: true,
    parameters: {id: river_id, playspot: playspot},
    evalScripts: true,
    onSuccess: function(transport) {
      editing = true;
    }
  });
}

function editSave() {
  disableDiv('panelinfo', "<img src='/images/progress.gif'> Saving...");

  var params = $('riverform').serialize(true);
  params['river[description]'] = tinyMCE.get('river_description').getContent();
  params.id = river_id;
  
  if (pointsEdited) {
    params.points_edited = 1;
    for (var id in markers) {
      point = markers[id].point;
      p = 'point[' + id + ']'
      params[p + '[lat]'] = point.lat;
      params[p + '[lng]'] = point.lng;
      params[p + '[ptype]'] = point.ptype;
      params[p + '[name]'] = point.name;
      params[p + '[notes]'] = point.notes;
      params[p + '[grade_id]'] = point.grade_id;
      if (id > 0) {
        params[p + '[id]'] = id;
      }
    }
  
    for (var i=0; i<linePoints.length; i++) {
      var id = lineMarkers[i].point_id;
      var p = 'point[' + id + ']'
      params[p + '[lat]'] = linePoints[i].lat();
      params[p + '[lng]'] = linePoints[i].lng();
      params[p + '[pindex]'] = i;
      params[p + '[ptype]'] = 'river';
      if (id > 0) {
        params[p + '[id]'] = id;
      }
    }
  } else {
    params.points_edited = 0;
  }
  
  new Ajax.Request("/river/ajax_edit_save", {
    method: 'post',
    parameters: params,
    evalScripts: true     // editStop() called from here if successful
  });
}

function editCancel() {
  // Reload old river data
  loadData(true, false);
  editStop();
  
  // Reset menus etc
  $('panelinfo').update("<img src='/images/progress.gif'> Loading...");
  new Ajax.Request("/river/ajax_edit_cancel", {
    parameters: {id: river_id},
    evalScripts: true
  });
}

function editStop() {
  closeEdit();
  disableDrag();
  editing = false;
  iconSelect(iconSelected);
}  


function iconSelect(iconType)  {
  // Wrap up if we are editing something
  if (iconSelected == 'river') {
    // Hide all river markers (leaving the polyline), show other markers when river is deselected)
    for (i=0; i<lineMarkers.length; i++) {
      lineMarkers[i].hide();
    } 
  } else if (iconSelected != '') {
    closeEdit();
  } 
  if (iconSelected != '') {
    $(iconSelected).removeClassName('menuSelected');
  }
  
  // Change selection
  if (iconSelected == iconType) {
    iconSelected = '';
  } else {
    $(iconType).addClassName('menuSelected');
    iconSelected = iconType;
    
    if (iconSelected == 'river') {
      // Add all river markers if river is selected
      for (i=0; i<lineMarkers.length; i++) {
        lineMarkers[i].show();
      } 
    }
  }
}

function closeEdit(save) {
  if (editmarker) {
    editmarker.closeInfoWindow();
  }
  editmarker = null;
}

function deleteMarker() {
  if (editmarker)  {
    delete markers[editmarker.point_id];
    GEvent.clearListeners(editmarker, "infowindowbeforeclose");
    map.removeOverlay(editmarker);
    editmarker = null;
    pointsEdited = true;
  }
}


function formMarker(marker) {
  var gradelist = '';
  var point = markers[marker.point_id].point;
  if (point.ptype == 'rapid') {
    grades = {'-Not set-': 0, 'II': 1, 'II+':2, 'II-III':3, 'III-':4, 'III':5,
      'III+': 6, 'III-IV':7, 'IV-':8, 'IV':9, 'IV+':10,
      'IV-V': 11, 'V-':12, 'V':13, 'V+':14, 'VI':21};
    gradelist = "Grade<select id='grade' name='m[grade]'>";
    for (var i in grades) {
      selected = grades[i] == point.grade_id? " selected": "";
      gradelist += "<option value='" + grades[i] + "'" + selected + ">" + i + "</option>";
    }
    gradelist += "</select><br/>";
  }
  editmarker = marker;
  string = 
    "<form action=''>"
    //+ "<img src='/images/" + marker.my_ptype + ".png' width=30 height==30>&nbsp;&nbsp;<b>Edit information</b><br/>"
    + "Name<br><input type='text' id='name' name='m[name]' value='" + htmlEscape(point.name) + "'/><br/>"
    + gradelist
    + "Notes<br/><textarea id='notes' style='height:100px;width:300px'>" + htmlEscape(point.notes) + "</textarea>"
    + "<br><img src='/images/icons/accept.png'><a href='javascript: closeEdit(1);'>Save</a> "
    + "&nbsp;&nbsp; <img src='/images/icons/cancel.png'><a href='javascript: closeEdit(0);'>Cancel</a> "
    + "&nbsp;&nbsp; <img src='/images/icons/cross.png'><a href='javascript: deleteMarker();'>Delete</a>"
    + "</form>";
//    + "<script>tinyMCE.execCommand('mceAddControl', false, 'notes');</script>";
  return string;
}



function infoMarker(marker) {
  var grade = '';
  var menu = '';
  var point = markers[marker.point_id].point;
  if (point.grade_id > 0) {
    grade = 'Class '+ grades[point.grade_id].grade + '<br>';
  }
  if (point.ptype == 'putin' || point.ptype == 'takeout' || (playspot && point.ptype == 'playspot')) {
    if (user_location) {
      menu = "<br/><a href='javascript:homeDirections("+ marker.point_id +");'>Get directions</a>";
    } else if (user_id) {
      menu = "<br><small>To enable directions, you need to <a href='/settings'>set your location</a>.</small>";
    } else {
      menu = "<br/><small>Log in to get directions</small>";
    }
  }
  string = 
    "<div style='width:240px'><img src='/images/" + point.ptype + ".png' width=30 height=30>"
    + "<b>" + htmlEscape(point.name) + "</b><br/>"
    + grade
    + htmlEscape(point.notes) + menu + "</div>";
  return string;
}


// Form helpers
/*
function gaugeLoad() {
  <% if !@river.gauge %>
    gaugeGet();
  <% end %>
}
*/

function gaugeGet() {
  $('gaugeGet').update('<img src="/images/progress.gif" border=0>');

  b = map.getBounds();
  min = b.getSouthWest();
  max = b.getNorthEast();
  parms = 'bounds[minlat]='+min.lat()+'&bounds[maxlat]='+max.lat()+'&bounds[minlng]='+min.lng()+'&bounds[maxlng]='+max.lng();
  new Ajax.Request('/river/get_gauges_select/', {
    asynchronous:true,
    parameters:parms,
    onSuccess: function(transport) {
      gauges = eval( "(" + transport.responseText + ")" );
      g_select = $F('river[gauge_id]');
      $('river[gauge_id]').update(gauges['options']);
      $('river[gauge_id]').value = g_select;
      $('gaugeGet').update('get');
    }
  });
}

function getRegion() {
      if ($('river_region_id').value == '') {
        var p = map.getCenter();
        new Ajax.Request("/river/ajax_get_region", {
          asynchronous:true,
          parameters: {lat: p.lat(), lng: p.lng()},
          onSuccess: function(transport) {
            region = transport.responseText;
            $('river_region_id').value  = region;
          }
        });
      }
}


function mapPan() {
  // Callback when user resizes windows- NOOP
}


function shuttleDirections() {
  // Find putin and takeout
  var putin = null;
  var takeout = null;
  for (var i in markers) {
    if (markers[i].point.ptype == 'putin') {
      putin = markers[i].marker;
    } else if (markers[i].point.ptype == '') {
      takeout = markers[i].marker;
    } 
  }
  if (putin && takeout) {
    // Create directions object
    var points = [putin.getLatLng(), takeout.getLatLng()];
    showDirections(points, takeout);
  }
}

function homeDirections(marker_id) {
  if (user_location) {
    var marker = markers[marker_id].marker;
    var points = [new GLatLng(user_location.lat, user_location.lng), marker.getLatLng()];
    showDirections(points, marker);
  }
}

// Global directions object
var gDir = null;

function showDirections(points, marker) {
  if (gDir) {
    gDir.clear();
  } else {
    gDir = new GDirections(map);
  }
  gDir.loadFromWaypoints(points);
  
  // Prepare infowindow contents
  var query = "http://maps.google.com/maps?q=" +
    "from:+" + points[0].toUrlValue(4) +
    "+to:+" + points[1].toUrlValue(4);

  // Wait for result
  GEvent.addListener(gDir, "load", function() {
    var html =
      "<b>Distance</b>:<br>" +
      gDir.getSummaryHtml() +
      "<br><br><a href='" + query + "' target='_blank'>Detailed directions on Google Maps";
    marker.openInfoWindowHtml(html);
  });
}
