/**
 * @file
 * Contains javascript libs for gpx_trackelevator module.
 *
 * When the DOM is ready initialize function is launched to prepare
 * data for rendering. Then plot_track is launched to create the map
 * and then google core_cart is load. Finally, the elevation graph
 * is created by plot_elevation_graph function.
 *
/* Expected Drupal.settings.gpx_track_elevation properties:
 * - points: an object with field instance & formatter data. See below for details.
 * - trColor: track color.
 * - epColor: alevation profile color.
 * - trstroke: track stroke.
 * - maptype: map type to use as default.
 * - enableBilink: if bidirectional link is enabled.
 * - trStartPoint: start points marker image.
 * - trEndPoint: end points marker image.
 * - trLastPoint: last points marker image.
 * - parDistance: "Distance" translation.
 * - parElevation: "Elevation" translation.
 * - parAsl: "asl" (above see level) translation.
 * - parLengthUnit: "m" (meter) translation.
 * - wpt_types: object with one method for each recognized waypoint type.
 *    Method name is the type and its value is the associated image url.
 *
 * points object has one method for each node to show. Metod name is id-nid, where nid is the node id
 * points[id-nid] return an array whose elements are:
 *  - gpx_start_point (int: how to show track start point for this formatter)
 *  - gpx_start_point (int: how to show track start point for this formatter)
 *  - array with the following elements:
 *   - array with an element for each <trk> tag:
 *    - <trk> name
 *    - array with an element for each <trkpt>
 *     - latitude
 *     - longitude
 *     - elevation
 *   - array with an element for each waypoint
 *    - waypoint name
 *    - waypoint latitude
 *    - waypoint longitude
 *    - waypoint description
 *    - waypoint elevation
 *    - waypoint type
 */

(function($) {

  var points = [];
  var bounds;
  var map = {};
  var elevations = {};
  var markerPoint = {};
  var trackNumber = {};
  var chart = {};
  var trackName = {};
  var StartPoints = [];
  var EndPoints  = [];
  var markers_type;
  var trackNids;
  var track_points_marker = {};
  var DEFAULT_MARKER = "http://www.google.com/mapfiles/marker.png";
  var DEFAULT_START_MARKER = "http://www.google.com/mapfiles/dd-start.png";
  var DEFAULT_END_MARKER = "http://www.google.com/mapfiles/dd-end.png";
  var DEFAULT_MARKER_SIZE = 30;
  var GOOGLE_CHART_LOADED = false;

  /**
   * Function plot_track creates the map and the listeners and plots the track.
   */
  function plot_track(){
    rawWayPoints = {};
    rawWayPoints[nIndex] = trackNids['id-' + nIndex][2][1];
    var myTrack = [];
    var wayPoints = [];

    // Show the map.
    $('.gpxtrele-container .map-canvas').height('20em');
    theMap  = document.getElementById('gpxtrele-container-' + nIndex).getElementsByClassName('map-canvas');

    map[nIndex] = new google.maps.Map(theMap[0],
    {
      draggable: true,
      keyboardShortcuts: false,
      mapTypeId: google.maps.MapTypeId[Drupal.settings.gpx_track_elevation.maptype],
      overviewMapControl: true,
      overviewMapControlOptions: false,
      streetViewControl: false,
      mapTypeControl: true,
      zomcontrol: true,
    });

    // Prepare the mouse move marker.
    markerPoint[nIndex] = new google.maps.Marker({
      position: new google.maps.LatLng(0,0),
      map:map[nIndex],
      visible: true,
      opacity: 0,
      icon: {
        path: google.maps.SymbolPath.CIRCLE,
        scale: 3,
        strokeColor: Drupal.settings.gpx_track_elevation.epColor
      },
    });

    // Prepare waypoints infowindow.
    infoBox = new google.maps.InfoWindow({});

    // Show waypoints.
    for (var i = 0; i < rawWayPoints[nIndex].length; i += 1) {
      var point = new google.maps.LatLng(rawWayPoints[nIndex][i][1], rawWayPoints[nIndex][i][2]);
      bounds.extend(point);
      wayPoints.push(new google.maps.Marker({
        map:map[nIndex],
        position: point,
        visible: true,
        title: rawWayPoints[nIndex][i][0],
        opacity: 1,
        icon: setMarkerImage(rawWayPoints[nIndex][i][5]),
        zIndex: 2

      }));
      google.maps.event.addListener(wayPoints[i], 'click', bindIndexWaypoint(rawWayPoints, nIndex, i));
    }

    map[nIndex].fitBounds(bounds);

    // Show last track point.
    for (i = 0; i < trackNumber[nIndex]; i += 1) {
      if (trackNids['id-' + nIndex][1] > 0) {
        if ((trackNids['id-' + nIndex][1] > 1) || (i == (trackNumber[nIndex] - 1))) {
          EndPoints.push(new google.maps.Marker({
            map:map[nIndex],
            position: points[nIndex][i][points[nIndex][i].length - 1],
            icon: setTrackImage((i == (trackNumber[nIndex] - 1)) ? 'last' : 'end', DEFAULT_END_MARKER),
            title: trackName[nIndex][i],
            zIndex: 1
          }));
        }
      }

      // Show first track point.
      if (trackNids['id-' + nIndex][0] > 0) {
        if ((trackNids['id-' + nIndex][0] > 1) || (i == 0)) {
          StartPoints.push(new google.maps.Marker({
            map:map[nIndex],
            position: points[nIndex][i][0],
            icon: setTrackImage('start', DEFAULT_START_MARKER),
            title: trackName[nIndex][i],
            zIndex: 3
          }));
        }
      }

      // Show tracks.
      myTrack.push(new google.maps.Polyline({
        path: points[nIndex][i],
        strokeColor: Drupal.settings.gpx_track_elevation.trColor,
        strokeOpacity: .7,
        strokeWeight: Drupal.settings.gpx_track_elevation.trstroke,
        clickable: false,
      }));

      myTrack[i].setMap(map[nIndex]);
    }

    if (Drupal.settings.gpx_track_elevation.enableBilink) {
      // We have to catch mousemove of the map to emulate that of the track
      // mousemove of the polyline would be better, but it does not return
      // point info for not editable polylines.
      google.maps.event.addListener(map[nIndex], 'mousemove', marker_on_map.bind(nIndex));

      function marker_on_map (evt){
        var nIndex = this;
        var minDist = 1000000000;
        var dist;
        var thePoint;

        for (var j = 0; j < trackNumber[nIndex]; j += 1) {
          for (var i = 0; i < points[nIndex][j].length; i += 1) {
            dist = google.maps.geometry.spherical.computeDistanceBetween(points[nIndex][j][i], evt.latLng);
            if (dist < minDist) {
              minDist = dist;
              thePoint = [j,i];
            }
          }
        }

        // In order to emulate the "track mousemove" we need to be close
        // to the track. Unfortunately the term "close to" depends on the
        // zoom level, so we have to check the zoom based distance.
        var zoom = 591658 / Math.pow(2, map[nIndex].getZoom() - 1);
        if (zoom / minDist > 1) {
          markerPoint[nIndex].setPosition(points[nIndex][thePoint[0]][thePoint[1]]);
          markerPoint[nIndex].setOpacity(1);
          chart[nIndex][thePoint[0]].setSelection([{row:thePoint[1],column:1}]);
        }
        else {
          for (var k = 0; k < trackNumber[nIndex]; k += 1) {
            chart[nIndex][k].setSelection(null);
          }
          markerPoint[nIndex].setOpacity(0);
        }
      }
    }

    /**
     * Function bindIndexWaypoint shows the waypoints InfoWindow.
     *
     * @param i
     *   index of the waypoint to be shown.
     */
    function bindIndexWaypoint(rawWayPoints, nIndex, i) {
      return (function(){
        var inboxInfo = '';
        if (this.title != '') {
          inboxInfo = '<b>' + this.title + '</b><br>';
        }
        if (rawWayPoints[nIndex][i][4] != '') {
          inboxInfo += Drupal.settings.gpx_track_elevation.parElevation + ': ' + round10(rawWayPoints[nIndex][i][4]) + '<br>';
        }
        if (rawWayPoints[nIndex][i][3] != '') {
          inboxInfo += rawWayPoints[nIndex][i][3] + '<br>';
        }
        infoBox.setContent(inboxInfo);
        infoBox.open(map[nIndex],this);
      });
    }
  }

  /**
   * Function plot_elevation_graph creates elevation profile(s).
   */
  function plot_elevation_graph() {
    nIndex = this;
    var data = [];
    chart[nIndex] = [];
    for (var j = 0; j < trackNumber[nIndex]; j += 1) {
      var divId = 'elevation_chart' + j;
      $('#gpxtrele-container-' + nIndex + ' .elevation_chart').append('<div id="elevation_chart-' + nIndex + '-' + j + '"></div>');
      data.push(new google.visualization.DataTable());
      data[j].addColumn('string', Drupal.settings.gpx_track_elevation.parDistance);
      data[j].addColumn('number', Drupal.settings.gpx_track_elevation.parElevation);
      data[j].addColumn({'type': 'string', 'role': 'tooltip', 'p': {'html': true}});
      for (var i = 1; i < elevations[nIndex][j].length; i++) {
        data[j].addRow([elevations[nIndex][j][i][0] + '', elevations[nIndex][j][i][1], Drupal.settings.gpx_track_elevation.parDistance + ': <b>' + elevations[nIndex][j][i][0] + '</b> ' + Drupal.settings.gpx_track_elevation.parLengthUnit + '<br> ' + Drupal.settings.gpx_track_elevation.parElevation + ': <b>' + elevations[nIndex][j][i][1] + '</b> ' + Drupal.settings.gpx_track_elevation.parLengthUnit + ' ' + Drupal.settings.gpx_track_elevation.parAsl]);
      }
      chart[nIndex].push(new google.visualization.AreaChart(document.getElementById('elevation_chart-' + nIndex + '-' + j)));
      document.getElementById('elevation_chart-' + nIndex + '-' + j).parentNode.style.display = 'block';
      document.getElementById('elevation_chart-' + nIndex + '-' + j).style.display = 'block';

      google.visualization.events.addListener(chart[nIndex][j], 'onmouseover', show_marker.bind([parseInt(nIndex), j]));

      google.visualization.events.addListener(chart[nIndex][j], 'onmouseout', hide_marker.bind([parseInt(nIndex), j]));
      chart[nIndex][j].draw(data[j],{
        height: 200,
        legend: 'none',
        title: trackName[nIndex][j],
        titleX: Drupal.settings.gpx_track_elevation.parDistance,
        titleY: Drupal.settings.gpx_track_elevation.parElevation,
        hAxis: {showTextEvery: Math.round(points[nIndex][j].length / 10)},
        colors: [Drupal.settings.gpx_track_elevation.epColor],
        tooltip: {isHtml: true, trigger: 'both'},
      });
    }

    /**
     * Function show_marker shows a marker on the map.
     *
     * @param pnt
     *   position where marker has to be shown.
     */
    function hide_marker (pnt) {
      markerPoint[this[0]].setOpacity(0);
    }

    /**
     * Function show_marker shows a marker on the map.
     *
     * @param pnt
     *   position where marker has to be shown.
     */
    function show_marker (pnt) {
      markerPoint[this[0]].setPosition(points[this[0]][this[1]][pnt.row]);
      markerPoint[this[0]].setOpacity(1);
    }
  }

  /**
   * Function round10 rounds a number to the nearest 10.
   *
   * @param number
   *   number to be rounded.
   *
   * @return
   *   rounded number.
   */
  function round10(number) {
    return Math.round(Math.round(number / 10) * 10);
  }

  function setMarkerImage (wpt) {
    var selected_marker;
    if ((wpt != '') && (markers_type) && (wpt in markers_type)) {
      selected_marker = {
        url: markers_type[wpt],
        scaledSize: new google.maps.Size(DEFAULT_MARKER_SIZE, DEFAULT_MARKER_SIZE),
        anchor: new google.maps.Point(DEFAULT_MARKER_SIZE / 2, DEFAULT_MARKER_SIZE / 2),
      }
    }
    else if ((markers_type) && ('default' in markers_type)) {
      selected_marker = {
        url: markers_type['default'],
        scaledSize: new google.maps.Size(DEFAULT_MARKER_SIZE, DEFAULT_MARKER_SIZE),
        anchor: new google.maps.Point(DEFAULT_MARKER_SIZE / 2, DEFAULT_MARKER_SIZE / 2),
      }
    }
    else {
      selected_marker = DEFAULT_MARKER;
    }
    return selected_marker;
  }

  function setTrackImage (pointType, defaultImage) {
    defaultImage = defaultImage || DEFAULT_MARKER;
    if (pointType in track_points_marker) {
      selected_marker = track_points_marker[pointType] || defaultImage;
    }
    else {
      selected_marker = defaultImage;
    }
    return selected_marker;
  }

  Drupal.behaviors.gpx_track_elevation = {
    attach: function (context, settings) {
      $('.gpxtrele-container', context).once('gpxtrele-container', function () {
        track_points_marker.start = Drupal.settings.gpx_track_elevation.trStartPoint;
        track_points_marker.end = Drupal.settings.gpx_track_elevation.trEndPoint;
        track_points_marker.last = Drupal.settings.gpx_track_elevation.trLastPoint;
        markers_type = Drupal.settings.gpx_track_elevation.wpt_types;
        bounds = new google.maps.LatLngBounds();
        trackNids = Drupal.settings.gpx_track_elevation.points;

        nIndex = this.id.substring(19);
        index = 'id-' + nIndex;
          trackPoints = trackNids[index][2][0];
          trackNumber[nIndex] = trackPoints.length;

          nIndex = index.substring(3);
          points[nIndex] = [];
          trackName[nIndex] = [];
          elevations[nIndex] = [];

          for (var j = 0; j < trackNumber[nIndex]; j += 1) {
            trackName[nIndex][j] = trackPoints[j][0];
            var totalDistance = 0;
            points[nIndex][j] = [];
            elevations[nIndex][j] = [];
            var point = new google.maps.LatLng(trackPoints[j][1][0][0], trackPoints[j][1][0][1]);
            elevations[nIndex][j].push(['0', trackPoints[j][1][0][2]]);
            points[nIndex][j].push(point);
            bounds.extend(point);
            for (var i = 1; i < trackPoints[j][1].length; i += 1) {
              point = new google.maps.LatLng(trackPoints[j][1][i][0], trackPoints[j][1][i][1]);
              points[nIndex][j].push(point);
              totalDistance += google.maps.geometry.spherical.computeDistanceBetween(points[nIndex][j][i - 1], points[nIndex][j][i]);
              bounds.extend(point);
              elevations[nIndex][j].push([round10(totalDistance) + '', Math.round(trackPoints[j][1][i][2])]);
            }
          }
          plot_track(nIndex);

          google.load('visualization', '1', {packages: ['corechart'], callback: plot_elevation_graph.bind(nIndex)});

      });
    }
  };
}(jQuery));
