import React from 'react';
export class PathSimulator extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      simStatus: {},
      simOptions: undefined,
      path: undefined,
      timer: undefined
    };
    this.updateUserLocation = props.updateUserLocation;
    this.onSimStatusChanged = props.onSimStatusChanged;
    this.updatePathLocation = this.updatePathLocation.bind(this);
  }
  startPathSimulation(path, options) {
    this.setState(prevState => {
      if (prevState.timer) clearInterval(prevState.timer);
      return {
        ...prevState,
        simOptions: options,
        path: path,
        simStatus: {
          status: 'ACTIVE',
          details: {
            timeRemaining: path.distance / options.movementSpeed,
            currLoc: this.getPoint(path.steps[0]),
            currRndmLoc: this.randomizeLocation(this.getPoint(path.steps[0]), options.variance_m),
            pathLeg: 0
          }
        },
        timer: setInterval(this.updatePathLocation, options.updateRate * 1000)
      };
    });
    this.updatePathLocation();
  }
  controlSimulation(command) {
    switch (command) {
      case 'PLAY':
        this.setState(prevState => {
          var _this$onSimStatusChan;
          const updatesStatus = {
            ...prevState.simStatus,
            status: 'ACTIVE'
          };
          (_this$onSimStatusChan = this.onSimStatusChanged) === null || _this$onSimStatusChan === void 0 || _this$onSimStatusChan.call(this, updatesStatus);
          return {
            ...prevState,
            simStatus: updatesStatus
          };
        });
        break;
      case 'PAUSE':
        this.setState(prevState => {
          var _this$onSimStatusChan2;
          const updatesStatus = {
            ...prevState.simStatus,
            status: 'PAUSED'
          };
          (_this$onSimStatusChan2 = this.onSimStatusChanged) === null || _this$onSimStatusChan2 === void 0 || _this$onSimStatusChan2.call(this, updatesStatus);
          return {
            ...prevState,
            simStatus: updatesStatus
          };
        });
        break;
      case 'STOP':
        this.setState(prevState => {
          var _this$onSimStatusChan3;
          const updatesStatus = {};
          (_this$onSimStatusChan3 = this.onSimStatusChanged) === null || _this$onSimStatusChan3 === void 0 || _this$onSimStatusChan3.call(this, updatesStatus);
          return {
            ...prevState,
            simStatus: updatesStatus,
            simOptions: undefined,
            path: undefined
          };
        });
        this.setState(prevState => ({
          ...prevState,
          simStatus: {},
          simOptions: undefined,
          path: undefined
        }));
        break;
    }
    return this.state.simStatus;
  }
  getSimulationStatus() {
    return this.state.simStatus;
  }
  getPoint(p) {
    return [p.lat, p.lon];
  }
  updatePathLocation() {
    this.setState(prevState => {
      var _updatedState$simStat, _updatedState$simStat6;
      const updatedState = {
        ...prevState,
        simStatus: {
          ...prevState.simStatus,
          details: {
            ...prevState.simStatus.details
          }
        }
      };
      if (((_updatedState$simStat = updatedState.simStatus) === null || _updatedState$simStat === void 0 ? void 0 : _updatedState$simStat.status) == 'ACTIVE' && updatedState.simOptions && updatedState.path && updatedState.simStatus.details) {
        var _updatedState$simStat2, _updatedState$simStat3, _prevState$simStatus$, _updatedState$simStat4, _updatedState$simStat5, _this$onSimStatusChan4;
        const currLocation = (_updatedState$simStat2 = updatedState.simStatus.details) === null || _updatedState$simStat2 === void 0 ? void 0 : _updatedState$simStat2.currLoc;
        const currSegment = (_updatedState$simStat3 = updatedState.simStatus.details) === null || _updatedState$simStat3 === void 0 ? void 0 : _updatedState$simStat3.pathLeg;
        const speed = updatedState.simOptions.movementSpeed;
        const time = updatedState.simOptions.updateRate;
        const distance = speed * time;
        const {
          loc,
          leg
        } = this.moveDistanceAlongPath(updatedState.path, currLocation, currSegment, distance);
        updatedState.simStatus.details.currLoc = loc;
        updatedState.simStatus.details.pathLeg = leg;
        if (leg == updatedState.path.steps.length) {
          return {
            ...prevState,
            simStatus: {},
            simOptions: undefined,
            path: undefined
          };
        }
        updatedState.simStatus.details.currRndmLoc = this.randomizeLocation(loc, updatedState.simOptions.variance_m);
        const bearing = this.getBearingBetweenPoints((_prevState$simStatus$ = prevState.simStatus.details) === null || _prevState$simStatus$ === void 0 ? void 0 : _prevState$simStatus$.currLoc, (_updatedState$simStat4 = updatedState.simStatus.details) === null || _updatedState$simStat4 === void 0 ? void 0 : _updatedState$simStat4.currLoc);
        this.updateUserLocation((_updatedState$simStat5 = updatedState.simStatus.details) === null || _updatedState$simStat5 === void 0 ? void 0 : _updatedState$simStat5.currRndmLoc, bearing);
        (_this$onSimStatusChan4 = this.onSimStatusChanged) === null || _this$onSimStatusChan4 === void 0 || _this$onSimStatusChan4.call(this, {
          status: 'ACTIVE'
        });
      } else if (((_updatedState$simStat6 = updatedState.simStatus) === null || _updatedState$simStat6 === void 0 ? void 0 : _updatedState$simStat6.status) == 'PAUSED') {
        var _this$onSimStatusChan5;
        (_this$onSimStatusChan5 = this.onSimStatusChanged) === null || _this$onSimStatusChan5 === void 0 || _this$onSimStatusChan5.call(this, {
          status: 'PAUSED'
        });
        return updatedState;
      } else {
        var _this$onSimStatusChan6;
        (_this$onSimStatusChan6 = this.onSimStatusChanged) === null || _this$onSimStatusChan6 === void 0 || _this$onSimStatusChan6.call(this, {
          status: undefined
        });
        return updatedState;
      }
      return updatedState;
    });
  }

  // -------------------------- UTILITY METHODS --------------------------
  moveDistanceAlongPath(path, startLocation, startSegment, distance) {
    let start = startLocation;
    for (let i = startSegment; i < path.steps.length - 1; i++) {
      const end = this.getPoint(path.steps[i + 1]);
      const {
        endLoc,
        remainingDist
      } = this.getPointDistanceAlongPath(start, end, distance);
      if (endLoc) {
        return {
          loc: endLoc,
          leg: i
        };
      } else {
        if (i === path.steps.length - 1) {
          return {
            loc: end,
            leg: i
          };
        }
        distance = remainingDist;
        start = end;
      }
    }
    // Should never get here, but just to make the compiler happy...
    return {
      loc: this.getPoint(path.steps[path.steps.length - 1]),
      leg: path.steps.length
    };
  }
  getRandomNumber(start, end) {
    return Math.random() * (end - start) + start;
  }

  // get random location within specified distance of specified location
  randomizeLocation(loc, radius) {
    const bearing = this.getRandomNumber(0, 360);
    const distance = this.getRandomNumber(0, radius);
    return this.getDestinationPoint(loc, bearing, distance);
  }

  // project at specified distance between two lat/long points
  getPointDistanceAlongPath(start, end, distance) {
    const d = this.getDistanceBetweenPoints(start, end);
    if (distance > d) {
      return {
        endLoc: undefined,
        remainingDist: distance - d
      };
    } else {
      const bearing = this.getBearingBetweenPoints(start, end);
      const endLoc = this.getDestinationPoint(start, bearing, distance);
      return {
        endLoc,
        remainingDist: 0
      };
    }
  }

  // get lat/long point at specified distance and bearing from start point
  getDestinationPoint(start, bearing, distance) {
    const lat1 = this.deg2rad(start[0]);
    const lon1 = this.deg2rad(start[1]);
    const R = 6371; // Radius of the earth in km
    const d = distance / 1000; // Distance in km
    const brng = this.deg2rad(bearing);
    const lat2 = Math.asin(Math.sin(lat1) * Math.cos(d / R) + Math.cos(lat1) * Math.sin(d / R) * Math.cos(brng));
    const lon2 = lon1 + Math.atan2(Math.sin(brng) * Math.sin(d / R) * Math.cos(lat1), Math.cos(d / R) - Math.sin(lat1) * Math.sin(lat2));
    return [this.rad2deg(lat2), this.rad2deg(lon2)];
  }

  // get bearing between two lat/long points
  getBearingBetweenPoints(start, end) {
    const lat1 = this.deg2rad(start[0]);
    const lon1 = this.deg2rad(start[1]);
    const lat2 = this.deg2rad(end[0]);
    const lon2 = this.deg2rad(end[1]);
    const dLon = lon2 - lon1;
    const y = Math.sin(dLon) * Math.cos(lat2);
    const x = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLon);
    const brng = Math.atan2(y, x);
    return this.rad2deg(brng);
  }

  // get distance between two lat/long points, in meters
  getDistanceBetweenPoints(start, end) {
    const lat1 = this.deg2rad(start[0]);
    const lon1 = this.deg2rad(start[1]);
    const lat2 = this.deg2rad(end[0]);
    const lon2 = this.deg2rad(end[1]);
    const R = 6371; // Radius of the earth in km
    const dLat = lat2 - lat1; // deg2rad below
    const dLon = lon2 - lon1;
    const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(lat1) * Math.cos(lat2) * Math.sin(dLon / 2) * Math.sin(dLon / 2);
    const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    return R * c * 1000; // Distance in meters
  }

  deg2rad(deg) {
    return deg * (Math.PI / 180);
  }
  rad2deg(rad) {
    return rad * (180 / Math.PI);
  }
  render() {
    return /*#__PURE__*/React.createElement(React.Fragment, null);
  }
}
//# sourceMappingURL=PathSimulator.js.map