import {
  useCallback,
  useEffect,
  useState,
  useMemo,
} from "react";
import { observer } from "mobx-react-lite";
import { extend } from "@pixi/react";
import { Container, Graphics, Sprite, Text } from "pixi.js";
import { MapDataProvider, useMapData } from "./MapDataContext";
import { TransformProvider, useTransform } from "./transformContext";
// @ts-ignore
import Loader from "../Loader";
import { UP_SCALE } from "./Constants";
import "../../styles/MapLayer.css";
import { useCameraAnimationStore } from "../../stores";
import { coordinatesToLocal } from "./utils";
import { SCALE_FACTOR } from "../../assets/Constants";
import { apiStore } from "../../api/ApiStore/store";
import WebGLMap from "./WebGLMap";

extend({ Container, Graphics, Text, Sprite });

export function Map() {
  return (
    <MapDataProvider>
      <TransformProvider>
        <RouteMap />
      </TransformProvider>
    </MapDataProvider>
  );
}

function rotatePoint(x: number, y: number, originX: number, originY: number, angle: number) {
  const cos = Math.cos(angle);
  const sin = Math.sin(angle);
  const dx = x - originX;
  const dy = y - originY;
  const rotatedX = dx * cos - dy * sin + originX;
  const rotatedY = dx * sin + dy * cos + originY;
  return { x: rotatedX, y: rotatedY };
}

const RouteMap = observer(() => {
  const {
    routeData,
    stationData,
    stationDataEn,
    stationDataZh,
    sightData,
    setRouteData,
    setStationData,
    setStationDataEn,
    setStationDataZh,
    setSightData,
  } = useMapData();
  const {
    setPosition,
    setScreenCenter,
    position,
    screenCenter,
    isAutoMode,
    setScale,
    scale,
  } = useTransform();
  const cameraAnimationStore = useCameraAnimationStore();

  const [rotationAngle, setRotationAngle] = useState(0);

  const {
    routeStations,
    context,
    routeStationsEn,
    routeStationsZh,
    routeSights,
    route,
    isLoading,
  } = apiStore;

  useEffect(() => {
    if (!isLoading) {
      setStationData(routeStations!);
      setStationDataEn(routeStationsEn!);
      setStationDataZh(routeStationsZh!);
      setSightData(routeSights!);
      setRouteData(route!);
      setRotationAngle(route?.rotate! * (Math.PI / 180));
      cameraAnimationStore.setMaxZoom(route?.scale_max! / SCALE_FACTOR);
      cameraAnimationStore.setMinZoom(route?.scale_min! / SCALE_FACTOR);
    }
  }, [isLoading]);

  useEffect(() => {
    cameraAnimationStore.setUpdateCallback((newPos, newZoom) => {
      setPosition(newPos);
      setScale(newZoom);
    });

    cameraAnimationStore.syncState(position, scale);

    return () => {
      cameraAnimationStore.setUpdateCallback(null);
    };
  }, [cameraAnimationStore, setPosition, setScale]);

  useEffect(() => {
    const canvasRect = {
      left: 0,
      top: 0,
      width: window.innerWidth,
      height: window.innerHeight,
    };
    const centerX = canvasRect.width / 2;
    const centerY = canvasRect.height / 2;
    setScreenCenter({ x: centerX, y: centerY });

    if (routeData?.center_latitude && routeData?.center_longitude) {
      const initialCameraPosition = { x: centerX, y: centerY };
      const initialZoom = cameraAnimationStore.minZoom;
      cameraAnimationStore.syncState(initialCameraPosition, initialZoom);
    } else {
      setPosition({ x: centerX, y: centerY });
    }
  }, [routeData, cameraAnimationStore]);

  const centerLat = routeData?.center_latitude;
  const centerLon = routeData?.center_longitude;
  const rotationOriginX = 0;
  const rotationOriginY = 0;

  const transformGeoToMapLocal = useCallback(
    (latitude: number, longitude: number) => {
      if (centerLat === undefined || centerLon === undefined) {
        return { x: 0, y: 0 };
      }
      const local = coordinatesToLocal(
        latitude - centerLat,
        longitude - centerLon
      );
      return { x: local.x * UP_SCALE, y: local.y * UP_SCALE };
    },
    [centerLat, centerLon]
  );

  const transformedCurrentCoordinates = useMemo(() => {
    if (
      !context?.currentCoordinates ||
      centerLat === undefined ||
      centerLon === undefined
    ) {
      return undefined;
    }

    return rotatePoint(
      transformGeoToMapLocal(
        context.currentCoordinates.latitude,
        context.currentCoordinates.longitude
      ).x,
      transformGeoToMapLocal(
        context.currentCoordinates.latitude,
        context.currentCoordinates.longitude
      ).y,
      rotationOriginX,
      rotationOriginY,
      rotationAngle
    );
  }, [
    context?.currentCoordinates?.latitude,
    context?.currentCoordinates?.longitude,
    centerLat,
    centerLon,
    transformGeoToMapLocal,
    rotationOriginX,
    rotationOriginY,
    rotationAngle,
  ]);

  const transformedStations = useMemo(() => {
    if (!stationData) return [];

    return stationData.map((station) => {
      const { x, y } = transformGeoToMapLocal(
        station.latitude,
        station.longitude
      );
      const rotatedCoords = rotatePoint(
        x,
        y,
        rotationOriginX,
        rotationOriginY,
        rotationAngle
      );
      return {
        ...station,
        longitude: rotatedCoords.x,
        latitude: rotatedCoords.y,
      };
    });
  }, [
    stationData,
    transformGeoToMapLocal,
    rotationOriginX,
    rotationOriginY,
    rotationAngle,
  ]);

  useEffect(() => {
    if (isAutoMode && transformedCurrentCoordinates && screenCenter) {
      cameraAnimationStore.followTram(
        {
          x: transformedCurrentCoordinates.x,
          y: transformedCurrentCoordinates.y,
        },
        { x: screenCenter.x, y: screenCenter.y },
        transformedStations
      );
    } else if (!isAutoMode) {
      cameraAnimationStore.stopAnimation();
    }
  }, [
    isAutoMode,
    transformedCurrentCoordinates,
    screenCenter,
    cameraAnimationStore,
    transformedStations,
  ]);

  if (
    !routeData ||
    !stationData ||
    !stationDataEn ||
    !stationDataZh ||
    !sightData
  ) {
    return <Loader />;
  }

  if (true) {
    return <WebGLMap />;
  }
});
