"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const fiber = require("@react-three/fiber");
const jsxRuntime = require("react/jsx-runtime");
const mapboxGl = require("mapbox-gl");
const react = require("react");
const mapbox = require("react-map-gl/mapbox");
const THREE = require("three");
const reactDom = require("react-dom");
function _interopNamespaceDefault(e) {
  const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
  if (e) {
    for (const k in e) {
      if (k !== "default") {
        const d = Object.getOwnPropertyDescriptor(e, k);
        Object.defineProperty(n, k, d.get ? d : {
          enumerable: true,
          get: () => e[k]
        });
      }
    }
  }
  n.default = e;
  return Object.freeze(n);
}
const THREE__namespace = /* @__PURE__ */ _interopNamespaceDefault(THREE);
const useMap$1 = () => fiber.useThree((s) => {
  var _a;
  return (_a = s.r3m) == null ? void 0 : _a.map;
});
const originMx = new THREE.Matrix4();
const projByView = new THREE.Matrix4();
const projByViewInv = new THREE.Matrix4();
const fwd = new THREE.Vector3();
function syncCamera(camera, origin, mapCamMx) {
  projByView.fromArray(mapCamMx).multiply(originMx.fromArray(origin));
  projByViewInv.copy(projByView).invert();
  updateCamera(camera, projByViewInv);
  camera.updateMatrix();
  camera.updateMatrixWorld(true);
  camera.projectionMatrix.copy(camera.matrix).premultiply(projByView);
  camera.projectionMatrixInverse.copy(camera.projectionMatrix).invert();
  camera.far = calculateFar(
    camera.matrix.elements[10],
    camera.matrix.elements[14],
    camera.near
  );
  camera.userData.projByView = projByView.toArray();
  camera.userData.projByViewInv = projByViewInv.toArray();
}
const updateCamera = (target, projByViewInv2) => {
  target.position.setScalar(0).applyMatrix4(projByViewInv2);
  target.up.set(0, -1, 0).applyMatrix4(projByViewInv2).negate().add(target.position).normalize();
  fwd.set(0, 0, 1).applyMatrix4(projByViewInv2);
  target.lookAt(fwd);
};
function calculateFar(c, d, near) {
  const numerator = d * (c - 1);
  const denominator = c * near + near;
  const far = numerator / denominator;
  return far;
}
const quat = new THREE.Quaternion();
const euler = new THREE.Euler();
const pos = new THREE.Vector3();
const scale = new THREE.Vector3();
const m4 = new THREE.Matrix4();
function coordsToMatrix({
  longitude,
  latitude,
  altitude,
  fromLngLat: fromLngLat2
}) {
  const center = fromLngLat2([longitude, latitude], altitude);
  const scaleUnit = center.meterInMercatorCoordinateUnits();
  pos.set(center.x, center.y, center.z || 0);
  scale.set(scaleUnit, -scaleUnit, scaleUnit);
  quat.setFromEuler(euler.set(-Math.PI * 0.5, 0, 0));
  return m4.compose(pos, quat, scale).toArray();
}
function useCoordsToMatrix({ latitude, longitude, altitude, fromLngLat: fromLngLat2 }) {
  const m42 = react.useMemo(() => coordsToMatrix({
    latitude,
    longitude,
    altitude,
    fromLngLat: fromLngLat2
  }), [latitude, longitude, altitude, fromLngLat2]);
  return m42;
}
function useR3M() {
  const r3m = fiber.useThree((s) => s.r3m);
  return r3m;
}
function useInitR3M(props) {
  const canvas = fiber.useThree((s) => s.gl.domElement);
  react.useState(() => {
    const store = fiber._roots.get(canvas).store;
    initR3M({ ...props, store });
  });
}
function initR3M({ store, ...props }) {
  const viewProjMx = new THREE.Matrix4().identity().toArray();
  const r3m = { ...props, viewProjMx };
  store.setState({ r3m });
  return r3m;
}
const Coordinates = react.memo(({
  latitude,
  longitude,
  altitude = 0,
  children
}) => {
  const [scene] = react.useState(() => new THREE.Scene());
  const r3m = useR3M();
  const origin = useCoordsToMatrix({
    latitude,
    longitude,
    altitude,
    fromLngLat: r3m.fromLngLat
  });
  return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: fiber.createPortal(/* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
    /* @__PURE__ */ jsxRuntime.jsx(RenderAtCoords, { r3m, origin }),
    children
  ] }), scene, { events: { priority: 2 } }) });
});
Coordinates.displayName = "Coordinates";
function RenderAtCoords({ r3m, origin }) {
  const { gl, scene, set } = fiber.useThree();
  const cameraRef = react.useRef(null);
  fiber.useFrame(() => {
    if (!cameraRef.current)
      return;
    syncCamera(cameraRef.current, origin, r3m.viewProjMx);
    gl.render(scene, cameraRef.current);
  });
  react.useLayoutEffect(() => {
    if (!cameraRef.current)
      return;
    set({
      invalidate: () => {
        if (!r3m.map)
          return;
        r3m.map.triggerRepaint();
      },
      camera: cameraRef.current
    });
  }, [set, r3m]);
  return /* @__PURE__ */ jsxRuntime.jsx("perspectiveCamera", { ref: cameraRef });
}
const earthRadius = 63710088e-1;
const mercatorScaleLookup = {};
function getMercatorScale(lat) {
  const index = Math.round(lat * 1e3);
  if (mercatorScaleLookup[index] === void 0) {
    mercatorScaleLookup[index] = 1 / Math.cos(lat * THREE.MathUtils.DEG2RAD);
  }
  return mercatorScaleLookup[index];
}
function averageMercatorScale(originLat, pointLat, steps = 10) {
  let totalScale = 0;
  const latStep = (pointLat - originLat) / steps;
  for (let i = 0; i <= steps; i++) {
    const lat = originLat + latStep * i;
    totalScale += getMercatorScale(lat);
  }
  return totalScale / (steps + 1);
}
function coordsToVector3(point, origin) {
  const latitudeDiff = (point.latitude - origin.latitude) * THREE.MathUtils.DEG2RAD;
  const longitudeDiff = (point.longitude - origin.longitude) * THREE.MathUtils.DEG2RAD;
  const altitudeDiff = (point.altitude || 0) - (origin.altitude || 0);
  const x = longitudeDiff * earthRadius * Math.cos(origin.latitude * THREE.MathUtils.DEG2RAD);
  const y = altitudeDiff;
  const steps = Math.ceil(Math.abs(point.latitude - origin.latitude)) * 100 + 1;
  const avgScale = averageMercatorScale(origin.latitude, point.latitude, steps);
  const z = -latitudeDiff * earthRadius / getMercatorScale(origin.latitude) * avgScale;
  return [x, y, z];
}
function useCoords() {
  const coords = fiber.useThree((s) => s.coords);
  return coords;
}
function useSetCoords({ longitude, latitude, altitude }) {
  const canvas = fiber.useThree((s) => s.gl.domElement);
  react.useMemo(() => {
    const store = fiber._roots.get(canvas).store;
    const coords = { longitude, latitude, altitude };
    setCoords(store, coords);
  }, [longitude, latitude, altitude]);
}
function useSetRootCoords(store, {
  longitude,
  latitude,
  altitude
}) {
  react.useMemo(() => {
    setCoords(store, { longitude, latitude, altitude });
  }, [longitude, latitude, altitude]);
}
function setCoords(store, coords) {
  store.setState({ coords });
}
const NearCoordinates = react.memo(({ children, ...coords }) => {
  const { latitude, longitude, altitude } = useCoords();
  const pos2 = react.useMemo(() => coordsToVector3(coords, { latitude, longitude, altitude }), [
    // eslint-disable-line react-hooks/exhaustive-deps
    latitude,
    longitude,
    altitude,
    coords.latitude,
    coords.longitude,
    coords.altitude
  ]);
  return /* @__PURE__ */ jsxRuntime.jsx("object3D", { position: pos2, children });
});
NearCoordinates.displayName = "NearCoordinates";
function vector3ToCoords(position, origin) {
  const [x, y, z] = position;
  const latitude = origin.latitude + -z / earthRadius * THREE.MathUtils.RAD2DEG;
  const longitude = origin.longitude + x / earthRadius * THREE.MathUtils.RAD2DEG / Math.cos(origin.latitude * THREE.MathUtils.DEG2RAD);
  const altitude = (origin.altitude || 0) + y;
  const coords = { latitude, longitude, altitude };
  return coords;
}
const useFunction = (callback) => {
  const callbackRef = react.useRef(callback);
  callbackRef.current = callback;
  return react.useCallback((...args) => {
    return callbackRef.current(...args);
  }, []);
};
function useRender({
  map,
  origin,
  useThree,
  frameloop,
  r3m
}) {
  const render = useFunction((_gl, projViewMx) => {
    const pVMx = "defaultProjectionData" in projViewMx ? Object.values(projViewMx.defaultProjectionData.mainMatrix) : projViewMx;
    r3m.viewProjMx.splice(0, 16, ...pVMx);
    const state = useThree.getState();
    const camera = state.camera;
    const { gl, advance } = state;
    syncCamera(camera, origin, projViewMx);
    gl.resetState();
    advance(Date.now() * 1e-3, true);
    if (!frameloop || frameloop === "always")
      map.triggerRepaint();
  });
  return render;
}
const projViewInv = new THREE.Matrix4();
const events = (store) => {
  const originalEvents = fiber.events(store);
  return {
    ...originalEvents,
    connect: (target) => {
      if (!originalEvents.connect)
        return;
      originalEvents.connect(target.parentElement);
    },
    compute: (event, state) => {
      state.pointer.x = event.offsetX / state.size.width * 2 - 1;
      state.pointer.y = 1 - event.offsetY / state.size.height * 2;
      if (state.camera.userData.projByViewInv)
        projViewInv.fromArray(state.camera.userData.projByViewInv);
      state.raycaster.camera = state.camera;
      state.raycaster.ray.origin.setScalar(0).applyMatrix4(projViewInv);
      state.raycaster.ray.direction.set(state.pointer.x, state.pointer.y, 1).applyMatrix4(projViewInv).sub(state.raycaster.ray.origin).normalize();
    }
  };
};
function useRoot(fromLngLat2, map, { frameloop, longitude, latitude, altitude, ...props }) {
  const [{ root, useThree, canvas, r3m }] = react.useState(() => {
    const canvas2 = map.getCanvas();
    const gl = canvas2.getContext("webgl2") || canvas2.getContext("webgl");
    const root2 = fiber.createRoot(canvas2);
    root2.configure({
      dpr: window.devicePixelRatio,
      events,
      ...props,
      frameloop: "never",
      gl: {
        context: gl,
        autoClear: false,
        antialias: true,
        ...props == null ? void 0 : props.gl
      },
      onCreated: (state) => {
        state.gl.forceContextLoss = () => {
        };
      },
      camera: {
        matrixAutoUpdate: false,
        near: 0
      },
      size: {
        width: canvas2.clientWidth,
        height: canvas2.clientHeight,
        top: canvas2.offsetTop,
        left: canvas2.offsetLeft,
        updateStyle: false,
        ...props == null ? void 0 : props.size
      }
    });
    const store = fiber._roots.get(canvas2).store;
    const r3m2 = initR3M({ map, fromLngLat: fromLngLat2, store });
    setCoords(store, { longitude, latitude, altitude });
    if (frameloop === "demand") {
      store.setState({
        frameloop,
        invalidate: () => {
          map.triggerRepaint();
        }
      });
    }
    return { root: root2, useThree: store, map, canvas: canvas2, r3m: r3m2 };
  });
  const onResize = useFunction(() => {
    const { setDpr, setSize } = useThree.getState();
    setDpr(window.devicePixelRatio);
    setSize(
      canvas.clientWidth,
      canvas.clientHeight,
      false,
      canvas.offsetTop,
      canvas.offsetLeft
    );
  });
  const onRemove = useFunction(() => {
    root.unmount();
  });
  useSetRootCoords(useThree, { longitude, latitude, altitude });
  react.useEffect(() => {
    if (frameloop !== "demand")
      return;
    const setState = useThree.setState;
    const { invalidate } = useThree.getState();
    setState({
      frameloop,
      invalidate: () => {
        map.triggerRepaint();
      }
    });
    return () => {
      setState({ frameloop: "never", invalidate });
    };
  }, [frameloop]);
  react.useEffect(() => {
    map.on("resize", onResize);
    return () => {
      map.off("resize", onResize);
    };
  }, []);
  react.useEffect(() => {
    root.render(/* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: props.children }));
  }, [props.children]);
  return { onRemove, useThree, r3m };
}
function useCanvasInLayer(props, fromLngLat2, map) {
  const { latitude, longitude, altitude, frameloop } = props;
  const origin = useCoordsToMatrix({
    latitude,
    longitude,
    altitude,
    fromLngLat: fromLngLat2
  });
  const { onRemove, useThree, r3m } = useRoot(fromLngLat2, map, props);
  const render = useRender({ origin, frameloop, useThree, map, r3m });
  return {
    id: props.id,
    beforeId: props.beforeId,
    onRemove,
    render,
    type: "custom",
    renderingMode: "3d"
  };
}
const InitR3M = react.memo(({
  longitude,
  latitude,
  altitude,
  ...props
}) => {
  useInitR3M(props);
  useSetCoords({ longitude, latitude, altitude });
  return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, {});
});
InitR3M.displayName = "InitR3M";
const SyncCameraFC = react.memo(({
  latitude,
  longitude,
  altitude = 0,
  setOnRender,
  manualRender,
  onReady,
  map
}) => {
  const mapCanvas = map.getCanvas();
  const r3m = useR3M();
  const camRef = react.useRef(null);
  const camera = fiber.useThree((s) => s.camera);
  const gl = fiber.useThree((s) => s.gl);
  const threeCanvas = fiber.useThree((s) => s.gl.domElement);
  const scene = fiber.useThree((s) => s.scene);
  const advance = fiber.useThree((s) => s.advance);
  const setSize = fiber.useThree((s) => s.setSize);
  const set = fiber.useThree((s) => s.set);
  const origin = useCoordsToMatrix({ latitude, longitude, altitude, fromLngLat: r3m.fromLngLat });
  const ready = react.useRef(false);
  const triggerRepaint = react.useMemo(() => map.triggerRepaint, [map]);
  const mapPaintRequests = react.useRef(0);
  const triggerRepaintOff = useFunction(() => {
    mapPaintRequests.current++;
  });
  fiber.useFrame(() => {
    syncCamera(camera, origin, r3m.viewProjMx);
    if (manualRender)
      gl.render(scene, camera);
    map.triggerRepaint = triggerRepaint;
    if (mapPaintRequests.current > 0) {
      mapPaintRequests.current = 0;
      map.triggerRepaint();
    }
  }, -Infinity);
  const onRender = useFunction((viewProjMx) => {
    map.triggerRepaint = triggerRepaintOff;
    if (threeCanvas.width !== mapCanvas.width || threeCanvas.height !== mapCanvas.height) {
      setSize(
        mapCanvas.clientWidth,
        mapCanvas.clientHeight,
        true,
        mapCanvas.offsetTop,
        mapCanvas.offsetLeft
      );
    }
    r3m.viewProjMx = viewProjMx;
    if (!ready.current && onReady) {
      ready.current = true;
      onReady();
    }
    advance(Date.now() * 1e-3, true);
  });
  react.useEffect(() => {
    setOnRender && setOnRender(() => onRender);
  }, [setOnRender, onRender]);
  react.useLayoutEffect(() => {
    if (!manualRender)
      return;
    set({ camera: camRef.current });
  }, []);
  return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: manualRender && /* @__PURE__ */ jsxRuntime.jsx(
    "perspectiveCamera",
    {
      ref: camRef,
      matrixAutoUpdate: false,
      matrixWorldAutoUpdate: false
    }
  ) });
});
SyncCameraFC.displayName = "SyncCameraFC";
const CanvasPortal = react.memo(({
  children,
  latitude,
  longitude,
  altitude,
  setOnRender,
  map,
  fromLngLat: fromLngLat2,
  ...props
}) => {
  const mapCanvas = map.getCanvas();
  const eventSource = mapCanvas.parentElement;
  const [ready, setReady] = react.useState(false);
  const onReady = useFunction(() => {
    setReady(true);
  });
  return /* @__PURE__ */ jsxRuntime.jsxs(
    fiber.Canvas,
    {
      events,
      eventSource,
      ...props,
      gl: { autoClear: false, ...props.gl },
      children: [
        /* @__PURE__ */ jsxRuntime.jsx(
          InitR3M,
          {
            map,
            fromLngLat: fromLngLat2,
            latitude,
            longitude,
            altitude
          }
        ),
        /* @__PURE__ */ jsxRuntime.jsx(
          SyncCameraFC,
          {
            latitude,
            longitude,
            altitude,
            setOnRender,
            onReady,
            map
          }
        ),
        ready && children
      ]
    }
  );
});
CanvasPortal.displayName = "CanvasPortal";
const InitCanvasFC = react.memo((props) => {
  const canvas = props.map.getCanvas();
  const [el] = react.useState(() => {
    const el2 = document.createElement("div");
    el2.style.position = "absolute";
    el2.style.top = "0";
    el2.style.bottom = "0";
    el2.style.left = "0";
    el2.style.right = "0";
    el2.style.pointerEvents = "none";
    return el2;
  });
  react.useEffect(() => {
    const parent = canvas.parentElement;
    parent.appendChild(el);
    return () => {
      parent.removeChild(el);
    };
  }, []);
  return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: reactDom.createPortal(/* @__PURE__ */ jsxRuntime.jsx(CanvasPortal, { ...props }), el) });
});
InitCanvasFC.displayName = "InitCanvasFC";
fiber.extend(THREE__namespace);
const fromLngLat = mapboxGl.MercatorCoordinate.fromLngLat;
const Canvas = react.memo(({ overlay, ...props }) => {
  const map = mapbox.useMap().current.getMap();
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
    overlay && /* @__PURE__ */ jsxRuntime.jsx(CanvasOverlay, { map, ...props }),
    !overlay && /* @__PURE__ */ jsxRuntime.jsx(CanvasInLayer, { map, ...props })
  ] });
});
Canvas.displayName = "Canvas";
const CanvasInLayer = react.memo(({ map, ...props }) => {
  const layerProps = useCanvasInLayer(props, fromLngLat, map);
  return /* @__PURE__ */ jsxRuntime.jsx(mapbox.Layer, { ...layerProps });
});
CanvasInLayer.displayName = "CanvasInLayer";
const CanvasOverlay = react.memo(({ map, id, beforeId, ...props }) => {
  const [onRender, setOnRender] = react.useState();
  const render = useFunction((_gl, mx) => {
    if (!onRender)
      return;
    onRender(mx);
  });
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
    /* @__PURE__ */ jsxRuntime.jsx(mapbox.Layer, { id, beforeId, type: "custom", render }),
    /* @__PURE__ */ jsxRuntime.jsx(
      InitCanvasFC,
      {
        ...props,
        setOnRender,
        map,
        fromLngLat
      }
    )
  ] });
});
CanvasInLayer.displayName = "CanvasInLayer";
const useMap = useMap$1;
exports.Canvas = Canvas;
exports.Coordinates = Coordinates;
exports.NearCoordinates = NearCoordinates;
exports.averageMercatorScale = averageMercatorScale;
exports.coordsToVector3 = coordsToVector3;
exports.useMap = useMap;
exports.vector3ToCoords = vector3ToCoords;
