"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
Object.defineProperty(exports, "itineraryToTransitive", {
  enumerable: true,
  get: function () {
    return _util.itineraryToTransitive;
  }
});
var _baseMap = require("@opentripplanner/base-map");
var _react = _interopRequireWildcard(require("react"));
var _maplibre = require("react-map-gl/maplibre");
var _polyline = _interopRequireDefault(require("@mapbox/polyline"));
var _bbox = _interopRequireDefault(require("@turf/bbox"));
var _routeLayers = require("./route-layers");
var _util = require("./util");
var _route_arrow = _interopRequireDefault(require("./images/route_arrow.png"));
var _2 = _interopRequireDefault(require("./images/01.png"));
var _3 = _interopRequireDefault(require("./images/03.png"));
var _4 = _interopRequireDefault(require("./images/04.png"));
var _5 = _interopRequireDefault(require("./images/05.png"));
var _6 = _interopRequireDefault(require("./images/06.png"));
var _7 = _interopRequireDefault(require("./images/07.png"));
var _8 = _interopRequireDefault(require("./images/08.png"));
var _9 = _interopRequireDefault(require("./images/09.png"));
var _0 = _interopRequireDefault(require("./images/10.png"));
var _1 = _interopRequireDefault(require("./images/11.png"));
var _10 = _interopRequireDefault(require("./images/12.png"));
var _11 = _interopRequireDefault(require("./images/13.png"));
var _12 = _interopRequireDefault(require("./images/14.png"));
var _13 = _interopRequireDefault(require("./images/15.png"));
var _14 = _interopRequireDefault(require("./images/16.png"));
var _15 = _interopRequireDefault(require("./images/17.png"));
var _square = _interopRequireDefault(require("./images/square.png"));
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
const CAPSULES = {
  3: _3.default,
  4: _4.default,
  5: _5.default,
  6: _6.default,
  7: _7.default,
  8: _8.default,
  9: _9.default,
  10: _0.default,
  11: _1.default,
  12: _10.default,
  13: _11.default,
  14: _12.default,
  15: _13.default,
  16: _14.default,
  17: _15.default
};

// These are based on the sprites in the image folder
const WIDTH_IMAGE_SIZES = {
  6: 1283,
  7: 1450,
  8: 1617,
  9: 1783,
  10: 1950,
  11: 2117,
  12: 2283,
  13: 2450,
  14: 2617,
  15: 2783,
  16: 2950,
  17: 3117
};
const HEIGHT_IMAGE_SIZE = 533;
// TODO: BETTER COLORS
const modeColorMap = {
  CAR: "#888",
  BICYCLE: "#f00",
  SCOOTER: "#f5a729",
  MICROMOBILITY: "#f5a729",
  MICROMOBILITY_RENT: "#f5a729",
  WALK: "#86cdf9"
};

/**
 * Apply a thin, white halo around the (black) text.
 */
const defaultTextPaintParams = {
  "text-halo-blur": 1,
  "text-halo-color": "#ffffff",
  "text-halo-width": 2
};

/**
 * Common text settings.
 */
const commonTextLayoutParams = {
  "symbol-placement": "point",
  "text-allow-overlap": false,
  "text-field": ["get", "name"],
  "text-justify": "auto",
  "text-radial-offset": 1,
  "text-size": 15
};

/**
 * Text size and layout that lets maplibre relocate text space permitting.
 */
const defaultTextLayoutParams = {
  ...commonTextLayoutParams,
  "text-variable-anchor": ["left", "right", "top", "bottom", "top-left", "top-right", "bottom-left", "bottom-right"]
};

/**
 * Default text + bold default fonts
 */
const defaultBoldTextLayoutParams = {
  ...commonTextLayoutParams,
  // FIXME: find a better way to set a bold font
  "text-font": ["Open Sans Bold", "Arial Unicode MS Bold"],
  "text-overlap": "never"
};
const routeFilter = ["==", "type", "route"];
const stopFilter = ["==", "type", "stop"];
const accessLegFilter = ["match", ["get", "type"], ["BICYCLE", "SCOOTER", "MICROMOBILITY", "MICROMOBILITY_RENT", "CAR"], true, false];
const loadImages = (map, images) => {
  images.forEach(img => {
    if (!map.hasImage(img.id)) {
      // Only load if the image hasn't already been added
      map.loadImage(img.url).then(response => {
        map.addImage(img.id, response.data, img.options);
      }).catch(error => {
        // eslint-disable-next-line no-console
        console.error(`Error loading image ${img.id}:`, error);
      });
    }
  });
};
const TransitiveCanvasOverlay = ({
  activeLeg,
  accessLegColorOverride,
  boundsFitting = true,
  showRouteArrows,
  transitiveData
}) => {
  const {
    current: map
  } = (0, _maplibre.useMap)();
  const mapImages = [];
  // This is used to render arrows along the route
  // Only load if that option is enabled to save the bandwidth
  if (showRouteArrows) {
    mapImages.push({
      id: "arrow-icon",
      url: _route_arrow.default,
      options: {
        sdf: true
      }
    });
  }
  function generateCapsulePadding(width) {
    // Low widths have no padding
    if (!WIDTH_IMAGE_SIZES[width]) {
      return undefined;
    }

    // This could be more efficient, but this makes it very clear what is happening.
    // Higher widths require more padding
    let topPad = 0;
    if (width === 6) {
      topPad = 100;
    }
    if (width > 6) {
      topPad = 150;
    }
    if (width > 12) {
      topPad = 175;
    }

    // Each image has same height
    return [topPad, 0, WIDTH_IMAGE_SIZES[width] - topPad, HEIGHT_IMAGE_SIZE];
  }
  mapImages.push({
    id: "1",
    url: _2.default,
    options: {
      // These paddings are specifically set so that the circle appears circular
      // despite non-circular padding
      content: [0, 15, 500, 485],
      sdf: true
    }
  });
  mapImages.push({
    id: "2",
    url: _2.default,
    options: {
      // These paddings are specifically set so that the circle appears circular
      // despite non-circular padding
      content: [0, 40, 500, 460],
      sdf: true
    }
  });

  // Generate each capsule image from 3 - 17
  for (let i = 3; i < 18; i++) {
    mapImages.push({
      id: `${i}`,
      url: CAPSULES[i],
      options: {
        content: generateCapsulePadding(i),
        sdf: true
      }
    });
  }
  mapImages.push({
    id: "rect",
    url: _square.default,
    options: {
      sdf: true
    }
  });
  (0, _react.useEffect)(() => {
    loadImages(map, mapImages);
  }, []);
  const geojson = {
    type: "FeatureCollection",
    features: transitiveData ? [...(transitiveData.places || []).flatMap(place => {
      return {
        type: "Feature",
        properties: {
          color: modeColorMap[place.type] || "#008",
          name: place.place_name,
          type: place.type || "place"
        },
        geometry: {
          type: "Point",
          coordinates: [place.place_lon, place.place_lat]
        }
      };
    }), ...(transitiveData.journeys || []).flatMap(journey => journey.segments.filter(segment => {
      var _segment$streetEdges;
      return ((_segment$streetEdges = segment.streetEdges) === null || _segment$streetEdges === void 0 ? void 0 : _segment$streetEdges.length) > 0;
    }).map(segment => ({
      ...segment,
      geometries: segment.streetEdges.map(edge => {
        return transitiveData.streetEdges.find(entry => entry.edge_id === edge);
      })
    })).flatMap(segment => {
      return segment.geometries.map(geometry => {
        const straight = _polyline.default.toGeoJSON(geometry.geometry.points);
        return {
          type: "Feature",
          properties: {
            type: "street-edge",
            color: accessLegColorOverride || modeColorMap[segment.type] || "#008",
            mode: segment.type
          },
          geometry: segment.arc ? (0, _util.drawArc)(straight) : straight
        };
      });
    })),
    // Extract the first and last stops of each transit segment for display.
    ...(transitiveData.journeys || []).flatMap(journey => journey.segments).filter(segment => segment.type === "TRANSIT").map(segment => transitiveData.patterns.find(p => {
      var _segment$patterns$;
      return p.pattern_id === ((_segment$patterns$ = segment.patterns[0]) === null || _segment$patterns$ === void 0 ? void 0 : _segment$patterns$.pattern_id);
    })).filter(pattern => !!pattern).flatMap(pattern => pattern.stops.filter((_, index, stopsArr) => index === 0 || index === stopsArr.length - 1)).map(pStop =>
    // pStop (from pattern.stops) only has an id (and sometimes line geometry)
    transitiveData.stops.find(stop => stop.stop_id === pStop.stop_id)).map(stop => ({
      type: "Feature",
      properties: {
        name: stop.stop_name,
        type: "stop"
      },
      geometry: {
        type: "Point",
        coordinates: [stop.stop_lon, stop.stop_lat]
      }
    })), ...(transitiveData.patterns || []).flatMap(pattern => (0, _routeLayers.patternToRouteFeature)(pattern, transitiveData.routes))] : []
  };
  const zoomToGeoJSON = geoJson => {
    if (!boundsFitting) return;
    const b = (0, _bbox.default)(geoJson);
    const bounds = [b[0], b[1], b[2], b[3]];
    if (map && bounds.length === 4 && bounds.every(Number.isFinite)) {
      map.fitBounds(bounds, {
        duration: 500,
        padding: _baseMap.util.getFitBoundsPadding(map, 0.2)
      });
    }
  };
  (0, _react.useEffect)(() => {
    zoomToGeoJSON(geojson);
  }, [transitiveData]);
  (0, _react.useEffect)(() => {
    if (!(activeLeg !== null && activeLeg !== void 0 && activeLeg.legGeometry)) return;
    zoomToGeoJSON(_polyline.default.toGeoJSON(activeLeg.legGeometry.points));
  }, [activeLeg]);
  if (!transitiveData) return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null);
  const {
    fromAnchor,
    toAnchor
  } = (0, _util.getFromToAnchors)(transitiveData);

  // Generally speaking, text/symbol layers placed first will be rendered in a lower layer
  // (or, if it is text, rendered with a lower priority or not at all if higher-priority text overlaps).
  return /*#__PURE__*/_react.default.createElement(_maplibre.Source, {
    data: geojson,
    id: "itinerary",
    type: "geojson"
  }, /*#__PURE__*/_react.default.createElement(_maplibre.Layer
  // This layer is for WALK modes - dotted path
  , {
    filter: ["all", ["==", "type", "street-edge"], ["==", "mode", "WALK"]],
    id: "street-edges-walk",
    layout: {
      "line-cap": "round",
      "line-join": "round"
    },
    paint: {
      // TODO: get from transitive properties
      "line-color": ["get", "color"],
      // First parameter of array is the length of the dash which is set to zero,
      // so that maplibre simply adds the rounded ends to make things look like dots.
      // Even so, note that maplibre still renders beans instead of dots
      // (as if maplibre fuses dots together).
      "line-dasharray": [0, 1.3],
      "line-opacity": 0.9,
      "line-width": 6
    },
    type: "line"
  }), /*#__PURE__*/_react.default.createElement(_maplibre.Layer
  // This layer is for other modes - dashed path
  , {
    filter: ["all", ["==", "type", "street-edge"], ["!=", "mode", "WALK"]],
    id: "street-edges",
    layout: {
      "line-cap": "butt"
    },
    paint: {
      // TODO: get from transitive properties
      "line-color": ["get", "color"],
      "line-dasharray": [2, 1],
      // TODO: get from transitive properties
      "line-width": 4,
      "line-opacity": 0.9
    },
    type: "line"
  }), /*#__PURE__*/_react.default.createElement(_maplibre.Layer, {
    filter: routeFilter,
    id: "routes",
    layout: {
      "line-join": "round",
      "line-cap": "round"
    },
    paint: {
      "line-color": ["get", "color"],
      // Apply a thinner line (width = 6) for bus routes (route_type = 3), set width to 10 otherwise.
      "line-width": ["match", ["get", "routeType"], 3, 6, 10],
      "line-opacity": 1
    },
    type: "line"
  }), showRouteArrows && /*#__PURE__*/_react.default.createElement(_maplibre.Layer, {
    id: "route-arrows",
    layout: {
      "symbol-placement": "line",
      "icon-image": "arrow-icon",
      "icon-size": 0.1,
      "symbol-spacing": 10,
      "icon-allow-overlap": true,
      "icon-ignore-placement": true,
      "icon-offset": [0, 8000]
    },
    paint: {
      "icon-color": ["get", "color"],
      "icon-opacity": 0.8
    },
    type: "symbol"
  }), /*#__PURE__*/_react.default.createElement(_maplibre.Layer, {
    filter: accessLegFilter,
    id: "access-leg-circles",
    paint: {
      "circle-color": ["get", "color"],
      "circle-radius": 8,
      "circle-stroke-color": "#fff",
      "circle-stroke-width": 3
    },
    type: "circle"
  }), /*#__PURE__*/_react.default.createElement(_maplibre.Layer, {
    filter: stopFilter,
    id: "stops-circles",
    paint: {
      "circle-color": "#fff",
      "circle-radius": 7,
      "circle-stroke-width": 3
    },
    type: "circle"
  }), /*#__PURE__*/_react.default.createElement(_maplibre.Layer, {
    filter: accessLegFilter,
    id: "access-leg-labels",
    layout: defaultTextLayoutParams,
    paint: defaultTextPaintParams,
    type: "symbol"
  }), /*#__PURE__*/_react.default.createElement(_maplibre.Layer, {
    filter: stopFilter,
    id: "stops-labels",
    layout: defaultTextLayoutParams,
    paint: defaultTextPaintParams,
    type: "symbol"
  }), /*#__PURE__*/_react.default.createElement(_maplibre.Layer
  // This layer renders transit route names (foreground).
  , {
    filter: routeFilter,
    id: "routes-labels",
    layout: (0, _routeLayers.getRouteLayerLayout)("name"),
    paint: {
      "icon-color": ["get", "color"],
      "text-color": ["get", "textColor"]
    },
    type: "symbol"
  }), /*#__PURE__*/_react.default.createElement(_maplibre.Layer, {
    filter: ["==", "type", "from"],
    id: "from-label",
    layout: {
      ...defaultBoldTextLayoutParams,
      "text-anchor": fromAnchor
    },
    paint: defaultTextPaintParams,
    type: "symbol"
  }), /*#__PURE__*/_react.default.createElement(_maplibre.Layer, {
    filter: ["==", "type", "to"],
    id: "to-label",
    layout: {
      ...defaultBoldTextLayoutParams,
      "text-anchor": toAnchor
    },
    paint: {
      ...defaultTextPaintParams,
      "text-color": "#910818"
    },
    type: "symbol"
  }));
};
var _default = exports.default = TransitiveCanvasOverlay;
//# sourceMappingURL=index.js.map