//
// Copyright (c) 2021 Clementine Computing LLC.
//
// This file is part of RideLogic.
//
// RideLogic is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// RideLogic is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with RideLogic.  If not, see <https://www.gnu.org/licenses/>.
//


var g_info = {
  "geojson" : {},
  "route" : {},
  "route_path": {},

  "active_route" : [],
  "ol_route_layer" : {}
};

var Feature = ol.Feature;
var Map = ol.Map;
var Overlay = ol.Overlay;
var Point = ol.geom.Point;
var TileJSON = ol.source.TileJSON;
var VectorSource = ol.source.Vector;
var View = ol.View;
var Icon = ol.style.Icon;
var Style = ol.style.Style;
var TileLayer = ol.layer.Tile;
var VectorLayer = ol.layer.Vector;
var OSM = ol.source.OSM;
var fromLonLat =  ol.proj.fromLonLat;

var Control = ol.control.Control;

var LineString = ol.geom.LineString;

//var defaultLonLat = [ -76.5019, 42.4440];
//var defaultLonLat = [ -76.1805, 42.6012 ];
var defaultLonLat = [ -76.2705, 42.5512 ];
var defaultWebMerc = fromLonLat(defaultLonLat);


function _rnd(a,b) {
  a = ((typeof a == "undefined") ? 1.0 : a);
  if (typeof b === "undefined") {
    return Math.random()*a;
  }
  return Math.random()*(b-a) + a;
}


function example_change_pos() {

  let w = 1/16.0;

  let lonlat = [
    defaultLonLat[0] + _rnd(-w,w),
    defaultLonLat[1] + _rnd(-w,w)];
  let merc = fromLonLat(lonlat);


  _icon[0].feature.getGeometry().setCoordinates(merc);
  busLayer.getSource().changed();
}

function example_change_opacity(p) {
  p = ((typeof p === "undefined") ? 0.0 : p);
  _icon[0].style.getImage().setOpacity(p);
  busLayer.getSource().changed();
}

// icon can't change image src dynamically,
// so have to replace.
// See https://stackoverflow.com/questions/57341190/how-can-i-change-icon-source-dynamically
//
function example_change_image() {
  let _nuimg = new Icon({
      anchor: [0.5, 46],
      anchorXUnits: 'fraction',
      anchorYUnits: 'pixels',
      src: 'data/bus_gw_90.png',
    });
  _icon[0].style.setImage(_nuimg);
  busLayer.getSource().changed();
}

//---

var vectorSource = new VectorSource();
var _icon = [];
for (let ii=0; ii<2; ii++) {

  let w = 1/32.0;
  let lonlat = [
    defaultLonLat[0] + _rnd(-w,w),
    defaultLonLat[1] + _rnd(-w,w)];
  let merc = fromLonLat(lonlat);

  let iconFeature = new Feature({
    geometry: new Point([merc[0], merc[1]]),
    name: 'bus'
  });

  let iconStyle = new Style({
    image: new Icon({
      anchor: [0.5, 46],
      anchorXUnits: 'fraction',
      anchorYUnits: 'pixels',
      src: 'data/icon.png',
    }),
  });

  iconStyle.getImage().setOpacity(0.0);

  _icon.push({ "style": iconStyle, "feature": iconFeature });
  iconFeature.setStyle(iconStyle);
  vectorSource.addFeature(iconFeature);

}

//---


const busLayer = new VectorLayer({
  source: vectorSource,
  zIndex: 2
});

//--

const rasterLayer = new TileLayer({
  //source: ol.source.OSM()
  source: new ol.source.OSM(),
});

//--


//-------------------
//-------------------
//-------------------


const g_map = new Map({
  //layers: [rasterLayer, vectorLayer, routeLayer],
  layers: [rasterLayer, busLayer ],
  target: document.getElementById('map'),

  controls:[],
  view: new View({
    center: defaultWebMerc,
    zoom: 11,
  }),
});

var g_side_toggle = true;

function toggle_sidebar(tf) {

  g_side_toggle = (g_side_toggle ? false : true);
  if (typeof tf !== "undefined") {
    g_side_toggle = tf;
  }

  // my perpetual fight with CSS.
  // I can't seem to get these values in when I put them in the
  // CSS, so we do it here programatically.
  //
  // Create a scrollable bar only for the list and not
  // for the map.
  // Hide the scroll bar when collapsing.
  // Hide the x scrollbar but dumping x values that overflow.
  //
  let ele = document.getElementById("sidebar-wrapper");
  if (g_side_toggle) {
    ele.style["overflow-y"] = "auto";
    ele.style["overflow-x"] = "hidden";
    ele.style["margin-left"] = 0;
    ele.style["min-width"] = "15rem";
    ele.style["max-height"] = "100vh";
  }
  else {
    ele.style["overflow-y"] = "hidden";
    ele.style["overflow-x"] = "hidden";
    ele.style["margin-left"] = "-15rem";
    ele.style["min-width"] = "0";
    ele.style["max-height"] = "100vh";
  }


}

class CustomToggle extends Control {
  constructor(opt) {
    opt = opt || {};
    let ele = document.createElement("button");
    ele.id = 'ui_route_toggle';
    ele.innerHTML = "Route";

    // class="btn" id="sidebarToggle" style='display:float;'
    ele.classList.add("custom-button");

    super({
      element: ele,
      target: opt.target
    });

    ele.addEventListener('click', this.fire.bind(this), false);
  }

  fire() {

    toggle_sidebar();

    // hacky, but functional
    //
    setTimeout(resize_map, 250);

  }
}

// button to toggle side bar route list
//
g_map.addControl(new  CustomToggle({ className: 'custom-button' }));

//-------------------
//-------------------
//-------------------


g_map.addControl(new ol.control.Zoom({
  className: 'custom-zoom'
}));

const element = document.getElementById('popup');

const popup = new Overlay({
  element: element,
  positioning: 'bottom-center',
  stopEvent: false,
});
g_map.addOverlay(popup);

// display popup on click
//
g_map.on('click', function (evt) {

  // not working (wip)
  //
  /*
  const feature = map.forEachFeatureAtPixel(evt.pixel, function (feature) {
    return feature;
  });
  */

});

// change mouse cursor when over marker
g_map.on('pointermove', function (e) {
  const pixel = g_map.getEventPixel(e.originalEvent);
  const hit = g_map.hasFeatureAtPixel(pixel);
  g_map.getTarget().style.cursor = hit ? 'pointer' : '';
});

// Close the popup when the map is moved
//
g_map.on('movestart', function () {
  //$(element).popover('dispose');
});

g_map.on("postrender", function(event) {
  //console.log("bang...");
});


//-----

function update_buses(json_gtfs) {

  let bearingIdxLookup = [ "0", "45", "90", "135", "180", "225", "270", "315" ];

  for (let ii=0; ii<json_gtfs.entityList.length; ii++) {
    let id = json_gtfs.entityList[ii].id;
    let p = json_gtfs.entityList[ii].vehicle.position;
    let lat = p.latitude;
    let lon = p.longitude;
    let bearing = p.bearing;


    let lonlat = [ lon , lat ];
    let merc = fromLonLat(lonlat);

    if (typeof _icon[ii] === "undefined") { continue; }

    _icon[ii].feature.getGeometry().setCoordinates(merc);

    bearing = (bearing % 360 );
    if (bearing < 0) { bearing += 360.0; }
    let iheading = Math.floor( (parseInt( bearing ) + 23) / 45 );
    if (iheading > 7) { iheading = 0; }

    let _nuimg = new Icon({
        anchor: [0.5, 46],
        anchorXUnits: 'fraction',
        anchorYUnits: 'pixels',
        src: 'data/bus_gw_' + bearingIdxLookup[iheading] + '.png',
      });
    _icon[ii].style.setImage(_nuimg);
    _icon[ii].style.getImage().setOpacity(1.0);

    //console.log(">>", id, lat, lon, bearing);
  }

  busLayer.getSource().changed();
}

function fetch_gtfs_vehicle_position() {
  let xhr = new XMLHttpRequest();
  xhr.onload = function() {
    let ab = xhr.response;
    let json_pb  = pb2json.pb2json(ab);
    //console.log(">>>", json_pb);

    update_buses(json_pb)
  };

  xhr.responseType = "arraybuffer";
  xhr.open("GET", "VehiclePosition.pb");
  xhr.send();
}

//--

function ui_show_single_route(route_id) {
  if (!(route_id in g_info.ol_route_layer)) {
    console.log("route", route_id, "not found in g_info.ol_route_layer");
    return;
  }

  for (let i=0; i<g_info.active_route; i++) {
    let route_id = g_info.active_route[i];
    let route_layer = g_info.ol_route_layer[route_id];
    g_map.removeLayer( route_layer );
  }
  g_info.active_route = [ route_id ];

  g_map.addLayer( g_info.ol_route_layer[route_id] );
}

function init_route_layer() {

  for (let route_id in g_info.route_path) {

    let route_points = g_info.route_path[route_id];
    let merc_points = [];
    for (let ii=0; ii<route_points.length; ii++) {
      merc_points.push(ol.proj.transform(route_points[ii], 'EPSG:4326', 'EPSG:3857'));
    }

    let routeLine = new ol.Feature({
      geometry: new ol.geom.LineString(merc_points)
    });

    let routeLineVector = new ol.source.Vector({});
    routeLineVector.addFeature(routeLine);

    let routeLayer = new VectorLayer({
      source: routeLineVector,
      style: new ol.style.Style({
        fill: new ol.style.Fill({ color: 'rgb(0,0,255,0.25)', weight: 4, opacity: 0.5 }),
        stroke: new ol.style.Stroke({ color: 'rgb(0,0,255,0.25)', width: 6, opacity: 0.5 })
      }),
      zIndex: 0
    });

    g_info.ol_route_layer[route_id] = routeLayer;
  }

}

function init_stop_layer(geojson) {

  //DEBUG
  return;

  if (!geojson) { return; }
  let stop_points = geojson.features[0].properties.route_stops;

  if (!stop_points) { return; }

  let merc_points = [];
  for (let ii=0; ii<stop_points.length; ii++) {
    let pnt = stop_points[ii].stop.geometry.coordinates;
    merc_points.push(ol.proj.transform(pnt, 'EPSG:4326', 'EPSG:3857'));
  }

  let stopVectorSource = new VectorSource();

  for (let ii=0; ii<merc_points.length; ii++) {
    let _stop = stop_points[ii].stop;

    let _stop_name = _stop.stop_name;

    let iconFeature = new Feature({
      geometry: new Point([merc_points[ii][0], merc_points[ii][1]]),
      name: _stop_name
    });

    let iconStyle = new Style({
      image: new Icon({
        anchor: [0.5, 46],
        anchorXUnits: 'fraction',
        anchorYUnits: 'pixels',
        src: 'data/star-3.png',
      }),
    });

    iconStyle.getImage().setOpacity(0.8);
    iconFeature.setStyle(iconStyle);
    stopVectorSource.addFeature(iconFeature);

  }

  let stopLayer = new VectorLayer({
    source: stopVectorSource,
    zIndex: 0
  });

  g_map.addLayer(stopLayer);
}

function fetch_geojson(url) {
  let xhr = new XMLHttpRequest();
  xhr.onload = function() {
    let geojson = xhr.response;

    g_info.geojson = geojson;

    //post_process_geojson();
    ui_add_route();

    init_route_layer(geojson);
    init_stop_layer(geojson);
  };

  xhr.responseType = "json";
  xhr.open("GET", url, true);
  xhr.send();

}

//---

function post_process_geojson() {
  let trip = g_info.geojson.features;
  for (let i=0; i<trip.length; i++) {
    if (trip[i].type != "trip") { continue; }
    let prop = trip[i].properties;
    let _route = prop.route;

    if (_route.route_id in g_info.route) { continue; }
    g_info.route[_route.route_id] = prop.route_stops;
    g_info.route_path[_route.route_id] = trip[i].geometry.coordinates;

  }


}

// add routes to side menu
//
function ui_add_route() {
  let ele = document.getElementById("ui_route_list");

  ele.innerHTML = '';

  let trip = g_info.geojson.features;
  for (let i=0; i<trip.length; i++) {
    if (trip[i].type != "trip") { continue; }
    let prop = trip[i].properties;
    let _route = prop.route;

    if (_route.route_id in g_info.route) { continue; }
    g_info.route[_route.route_id] = prop.route_stops;
    g_info.route_path[_route.route_id] = trip[i].geometry.coordinates;

    let _a = document.createElement("a");
    _a.classList.add("list-group-item");
    _a.classList.add("list-gorup-item-action");
    _a.classList.add("list-gorup-item-light");
    _a.classList.add("p-3");
    _a.innerText = prop.route.route_long_name;
    _a.onclick = (function(_r) {
      return function() {
        ui_show_single_route(_r);
      }
    })(_route.route_id);

    ele.appendChild(_a);

  }

  toggle_sidebar(g_side_toggle);
}

//---

// resize map
//
function resize_map() {

  let h = $(window).height();
  let m = document.getElementById("map");
  m.style.height = (h-10) + "px";
  g_map.updateSize();
}

function init() {
  setInterval(fetch_gtfs_vehicle_position, 1000);
  setTimeout( function() { fetch_geojson("geojson/test_route.geojson"); }, 100);

  $(window).on('resize', resize_map);
}


$(document).ready(function() {
  init();
  resize_map();
  toggle_sidebar(g_side_toggle);
});

