
class GPS {
  parse(trips) {    
    this.trips = trips
    this.regions = this.getRegions()
    this.data = this.trips.map(t => t.path).reduce((a, b) => a.concat(b), [])
  }

  getRegions() {
    var trips = [];
    var s = 0;
    for(let i=0; i<=this.trips.length; i++) {
      while(i < this.trips.length - 2) {
        const cur = this.trips[i];
        const nxt = this.trips[i+1];
        if((nxt.startTime - cur.endTime) > 3600 * 24 * 7 || distance(cur.center, nxt.center) > 100000) {
          break;
        }
        i++;
      }
      
      let trip = this.trips.slice(s, i);
      let dist = trip.reduce((sum, p) => p.distance + sum, 0);
      if (dist > 30000) {
        trips.push({
          lat: median(trip.map(v => v.center.lat)),
          lng: median(trip.map(v => v.center.lng)),
          timestamp: median(trip.map(v => v.startTime)),
          distance: dist,
          name: median(trip.map(t => t.name))
        });
      }
      s = i;
    }
    return trips;
  }
  getInfo() {
    let i = {
      points: this.trips.reduce((sum, p) => p.path.length + sum, 0),
      distance: this.trips.reduce((sum, p) => p.distance + sum, 0),
      topSpeed: this.trips.reduce((max, p) => p.topSpeed > max ? p.topSpeed : max, 0),
      ascendHeight: this.trips.reduce((sum, p) => p.ascendHeight + sum, 0),
      descendHeight: this.trips.reduce((sum, p) => p.descendHeight + sum, 0),
      movementTime: this.trips.reduce((sum, p) => p.movementTime + sum, 0),
      totalTime: this.trips.reduce((sum, p) => p.totalTime + sum, 0)
    };
    i.avgSpeed = i.distance / i.movementTime * 3.6;
    return i;
  }
}
function distance (a, b) {
 
  // Convert degrees to radians
  var lat1 = a.lat * Math.PI / 180.0;
  var lon1 = a.lng * Math.PI / 180.0;
 
  var lat2 = b.lat * Math.PI / 180.0;
  var lon2 = b.lng * Math.PI / 180.0;
 
  // radius of earth in metres
  var r = 6378100;
 
  // P
  var rho1 = r * Math.cos(lat1);
  var z1 = r * Math.sin(lat1);
  var x1 = rho1 * Math.cos(lon1);
  var y1 = rho1 * Math.sin(lon1);
 
  // Q
  var rho2 = r * Math.cos(lat2);
  var z2 = r * Math.sin(lat2);
  var x2 = rho2 * Math.cos(lon2);
  var y2 = rho2 * Math.sin(lon2);
 
  // Dot product
  var dot = (x1 * x2 + y1 * y2 + z1 * z2);
  var cos_theta = dot / (r * r);
 
  var theta = Math.acos(cos_theta);
 
  // Distance in Metres
  return r * theta;
}

function timeFormat(sec, x = "time") {
  if(isNaN(sec))
    return "NaN";

  let d = new Date(sec*1000);
  let t = new Date(null,null,null,null,null,sec).toTimeString().match(/\d{2}:\d{2}:\d{2}/)[0];
  
  switch(x) {
    case "time":
      if(sec < 3600*24)
        return t;
      else
        return Math.floor(sec/3600/24) + "d " + t;
    case "datetime":
      return d.toLocaleDateString('de-DE', {
        year: "numeric",
        month: "2-digit",
        day: "2-digit",
        hour: "2-digit",
        minute: "2-digit",
        second: "2-digit",
      });
    case "date":
      return d.toLocaleDateString('de-DE', {
        year: "numeric",
        month: "2-digit",
        day: "2-digit"
      }); 
    case "short":
      return d.toLocaleDateString('de-DE', {
        year: "numeric",
        month: "2-digit"
      }); 
  }
}
function median(values) {

  values.sort( function(a,b) {return a - b;} );

  var half = Math.floor(values.length/2);

  return values[half];
}
function avg(v) {
  return v.reduce((a,b) => a+b, 0)/v.length;
}

function smoothOut(vector, variance) {
  var t_avg = avg(vector)*variance;
  var ret = Array(vector.length);
  for (var i = 0; i < vector.length; i++) {
    (function () {
      var prev = i>0 ? ret[i-1] : vector[i];
      var next = i<vector.length ? vector[i] : vector[i-1];
      ret[i] = avg([t_avg, avg([prev, vector[i], next])]);
    })();
  }
  return ret;
}