"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = exports.VehicleShape = void 0;

var _gtfsRealtimeBindings = require("gtfs-realtime-bindings");

var _leaflet = _interopRequireDefault(require("leaflet"));

var _transitVehicleOverlay = _interopRequireDefault(require("@opentripplanner/transit-vehicle-overlay"));

var _propTypes = _interopRequireDefault(require("prop-types"));

var _react = _interopRequireDefault(require("react"));

var _server = _interopRequireDefault(require("react-dom/server"));

var _reactLeaflet = require("react-leaflet");

var _styledComponents = _interopRequireDefault(require("styled-components"));

var _viewer = require("../../util/viewer");

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }

const VehicleShape = _styledComponents.default.span`
  background-color: #${props => {
  var _props$route;

  return ((_props$route = props.route) === null || _props$route === void 0 ? void 0 : _props$route.route_color) || '000000';
}};
  border: 1px solid #000000;
  border-radius: 50%;
  color: #${props => {
  var _props$route2;

  return ((_props$route2 = props.route) === null || _props$route2 === void 0 ? void 0 : _props$route2.route_text_color) || 'ffffff';
}};
  display: block;
  height: 24px;
  left: -6px;
  line-height: 24px;
  margin-left: auto;
  margin-right: auto;
  position: relative;
  text-align: center;
  top: -6px;
  width: 24px;
`;
/**
 * Convert GTFS-rt entities to vehicle location from OTP-UI.
 * @param vehicle The GTFS-rt vehicle position entity to convert.
 * @param routes Optional GTFS-like routes list.
 */

exports.VehicleShape = VehicleShape;

function gtfsRtToTransitVehicle(gtfsRtVehicle, routes) {
  const {
    position,
    stopId,
    timestamp,
    trip,
    vehicle
  } = gtfsRtVehicle.vehicle;
  const route = routes.find(r => r.route_id === trip.routeId);
  let routeShortName;
  let routeType = 'BUS';

  if (route) {
    routeShortName = route.route_short_name; // Obtain the OTP route type from the GTFS route_type enum value.

    routeType = (0, _viewer.getModeFromRoute)({
      type: route.route_type
    });
  }

  return {
    heading: position.bearing,
    id: vehicle.id,
    lat: position.latitude,
    lon: position.longitude,
    reportDate: new Date(parseInt(timestamp, 10)).toLocaleString(),
    routeShortName,
    routeType,
    stopId,
    tripId: trip.tripId,
    vehicleId: vehicle.id
  };
}
/**
 * Generate a vehicle shape component (a colored dot) based on the provided routes list.
 */


function makeVehicleShape(routes) {
  return props => {
    const {
      lat,
      lon,
      routeShortName
    } = props.vehicle;
    const route = routes.find(r => r.route_short_name === routeShortName) || {};

    const iconHtml = _server.default.renderToStaticMarkup( /*#__PURE__*/_react.default.createElement(VehicleShape, {
      route: route
    }, route.route_short_name));

    return /*#__PURE__*/_react.default.createElement(_reactLeaflet.Marker, {
      icon: _leaflet.default.divIcon({
        html: iconHtml
      }),
      position: [lat, lon]
    });
  };
}
/**
 * This component is a composite overlay that renders GTFS-rt vehicle positions
 * that it downloads from the liveFeedUrl prop, using the
 * @opentripplanner/transit-vehicle-overlay package.
 *
 * For transit routes not defined in a known GTFS feed, this component also
 * renders transit route shapes if available from the routeDefinitionUrl prop.
 */


class GtfsRtVehicleOverlay extends _reactLeaflet.MapLayer {
  constructor(props) {
    super(props);

    _defineProperty(this, "onOverlayAdded", () => {
      this.setState({
        visible: true
      });

      this._startRefreshing();
    });

    _defineProperty(this, "onOverlayRemoved", () => {
      this.setState({
        visible: false
      });

      this._stopRefreshing();
    });

    _defineProperty(this, "_fetchVehiclePositions", async () => {
      const {
        liveFeedUrl
      } = this.props;

      if (liveFeedUrl) {
        try {
          const response = await fetch(liveFeedUrl);

          if (response.status >= 400) {
            const error = new Error('Received error from server');
            error.response = response;
            throw error;
          }

          const buffer = await response.arrayBuffer();
          const view = new Uint8Array(buffer);

          const feed = _gtfsRealtimeBindings.transit_realtime.FeedMessage.decode(view);

          const vehicleLocations = feed.entity.map(vehicle => gtfsRtToTransitVehicle(vehicle, this.state.routes));
          this.setState({
            vehicleLocations
          });
        } catch (err) {
          console.log(err);
        }
      }
    });

    _defineProperty(this, "_fetchRoutes", async () => {
      const {
        routeDefinitionUrl
      } = this.props;

      if (routeDefinitionUrl) {
        try {
          const response = await fetch(routeDefinitionUrl);

          if (response.status >= 400) {
            const error = new Error('Received error from server');
            error.response = response;
            throw error;
          }

          const routes = await response.json(); // Generate the symbols for the overlay at this time
          // so it renders the route colors for each vehicle.

          const vehicleSymbols = [{
            minZoom: 0,
            symbol: makeVehicleShape(routes)
          }];
          this.setState({
            routes,
            vehicleSymbols
          });
        } catch (err) {
          console.log(err);
        }
      }
    });

    this.state = {
      routes: [],
      vehicleLocations: [],
      // Set to undefined to fall back on the default symbols,
      // unless a route definition is provided.
      vehicleSymbols: undefined,
      visible: props.visible
    };
  }

  async componentDidMount() {
    const {
      liveFeedUrl,
      name,
      registerOverlay,
      routeDefinitionUrl
    } = this.props;
    registerOverlay(this);

    if (!routeDefinitionUrl) {
      console.warn(`routeDefinitionUrl prop is missing for overlay '${name}'.`);
    } else {
      // If route definitions are provided, wait until they are
      // fetched so they can be used rendering vehicle shapes.
      await this._fetchRoutes();
    }

    if (!liveFeedUrl) {
      console.warn(`liveFeedUrl prop is missing for overlay '${name}'.`);
    } else if (this.state.visible) {
      // If layer is initially visible, start getting the vehicle positions.
      this._startRefreshing();
    }
  }

  componentWillUnmount() {
    this._stopRefreshing();
  }

  createLeafletElement() {}

  updateLeafletElement() {}
  /**
   * Fetches GTFS-rt vehicle positions (protocol buffer) and converts to
   * OTP-UI transitVehicleType before saving to component state.
   */


  _startRefreshing() {
    // initial vehicle retrieval
    this._fetchVehiclePositions(); // set up timer to refresh vehicle positions periodically.
    // defaults to every 30 sec.


    this._refreshTimer = setInterval(this._fetchVehiclePositions, 30000);
  }

  _stopRefreshing() {
    if (this._refreshTimer) clearInterval(this._refreshTimer);
  }

  render() {
    const {
      routes,
      vehicleLocations,
      vehicleSymbols,
      visible
    } = this.state;
    return /*#__PURE__*/_react.default.createElement(_reactLeaflet.FeatureGroup, null, routes.map(r => /*#__PURE__*/_react.default.createElement(_reactLeaflet.Polyline, {
      color: `#${r.route_color}`,
      key: r.route_id,
      opacity: 0.6,
      positions: r.shape
    })), /*#__PURE__*/_react.default.createElement(_transitVehicleOverlay.default, {
      symbols: vehicleSymbols,
      vehicleList: visible ? vehicleLocations : null
    }));
  }

}

_defineProperty(GtfsRtVehicleOverlay, "propTypes", {
  /** URL to GTFS-rt feed in protocol buffer format. */
  liveFeedUrl: _propTypes.default.string,

  /** URL to GTFS-like route list in JSON format. */
  routeDefinitionUrl: _propTypes.default.string,

  /** Sets whether the layer is initially visible. */
  visible: _propTypes.default.bool
});

var _default = GtfsRtVehicleOverlay;
exports.default = _default;

//# sourceMappingURL=gtfs-rt-vehicle-overlay.js