import React from "react";

import { Card, Popover, Switch } from "antd";


import "./CompassDisplay.scss";


const toNumber = (value, fallback = 0) => {
  const num = Number(value);
  return Number.isFinite(num) ? num : fallback;
};

const CALIBRATION_KEY = "raspicar-compass-calibration";


const loadCalibration = () => {
  try {
    const raw = localStorage.getItem(CALIBRATION_KEY);
    if (!raw) return { swapXY: false, invertX: false, invertY: false, offset: 0 };
    const parsed = JSON.parse(raw);
    return {
      swapXY: !!parsed.swapXY,
      invertX: !!parsed.invertX,
      invertY: !!parsed.invertY,
      offset: toNumber(parsed.offset, 0),
    };
  } catch (e) {
    return { swapXY: false, invertX: false, invertY: false, offset: 0 };
  }
};



const normalizeHeading = (value) => {
  let heading = value % 360;
  if (heading < 0) heading += 360;
  return Number(heading.toFixed(1));
};


export default function CompassDisplay({ ws, rtcChannel, editabled = false }) {
  const [compassData, setCompassData] = React.useState({
    heading: 0,
    pitch: 0,
    roll: 0,
    raw: null,
    isConnected: false,
    lastUpdate: 0,
  });

  const [calibration, setCalibration] = React.useState(loadCalibration());



  const handleCompassMessage = React.useCallback((raw) => {
    try {
      const data = JSON.parse(raw);
      if (data.action === "compass data" || data.type === "compass data") {
        const payload = data.payload || data;
        setCompassData((prev) => ({
          ...prev,
          heading: toNumber(payload.heading, prev.heading),
          pitch: toNumber(payload.pitch, prev.pitch),
          roll: toNumber(payload.roll, prev.roll),
          raw: payload.raw || prev.raw,
          isConnected: payload.isConnected ?? true,
          lastUpdate: Date.now(),
        }));
      }
    } catch (e) {
      // 忽略非JSON消息
    }
  }, []);

  React.useEffect(() => {
    if (!ws) return;

    const handleMessage = (event) => {
      handleCompassMessage(event.data);
    };

    ws.addEventListener("message", handleMessage);

    return () => {
      ws.removeEventListener("message", handleMessage);
    };
  }, [ws, handleCompassMessage]);

  React.useEffect(() => {
    if (!rtcChannel) return;

    const handleRtcMessage = (event) => {
      handleCompassMessage(event.data);
    };

    rtcChannel.addEventListener("message", handleRtcMessage);

    return () => {
      rtcChannel.removeEventListener("message", handleRtcMessage);
    };
  }, [rtcChannel, handleCompassMessage]);

  React.useEffect(() => {
    if (typeof window === "undefined") return;
    const protocol = window.location.protocol === "https:" ? "wss://" : "ws://";
    const socket = new WebSocket(`${protocol}${window.location.host}/compass`);

    const handleCompassSocketMessage = (event) => {
      handleCompassMessage(event.data);
    };

    const handleCompassSocketClose = () => {
      setCompassData((prev) => ({
        ...prev,
        isConnected: false,
      }));
    };

    socket.addEventListener("message", handleCompassSocketMessage);
    socket.addEventListener("close", handleCompassSocketClose);
    socket.addEventListener("error", handleCompassSocketClose);

    return () => {
      socket.removeEventListener("message", handleCompassSocketMessage);
      socket.removeEventListener("close", handleCompassSocketClose);
      socket.removeEventListener("error", handleCompassSocketClose);
      socket.close();
    };
  }, [handleCompassMessage]);

  const { heading, raw } = compassData;

  const computedHeading = React.useMemo(() => {

    if (raw && typeof raw.x === "number" && typeof raw.y === "number") {
      const x = calibration.swapXY ? raw.y : raw.x;
      const y = calibration.swapXY ? raw.x : raw.y;
      const adjX = (calibration.invertX ? -1 : 1) * x;
      const adjY = (calibration.invertY ? -1 : 1) * y;
      const base = (Math.atan2(adjY, adjX) * 180) / Math.PI;
      return normalizeHeading(base + calibration.offset);
    }
    return normalizeHeading(heading + calibration.offset);
  }, [raw, heading, calibration]);

  const directionText = React.useMemo(() => {
    const directions = ["北", "东北", "东", "东南", "南", "西南", "西", "西北"];
    const index = Math.round(computedHeading / 45) % 8;
    return directions[index] || "北";
  }, [computedHeading]);

  const cardStyle = {
    width: "100%",
    height: "100%",
    background: "transparent",
    opacity: 1,
    boxShadow: "none",
    border: "none",
  };


  const updateCalibration = (next) => {
    setCalibration(next);
    try {
      localStorage.setItem(CALIBRATION_KEY, JSON.stringify(next));
    } catch (e) {
      // ignore
    }
  };

  const settingsPanel = (
    <div className="compass-settings">
      <div className="tool-row">
        <span className="tool-label">横竖</span>
        <Switch
          size="small"
          checked={calibration.swapXY}
          onChange={(checked) => updateCalibration({ ...calibration, swapXY: checked })}
        />
        <span className="tool-label">X反向</span>
        <Switch
          size="small"
          checked={calibration.invertX}
          onChange={(checked) => updateCalibration({ ...calibration, invertX: checked })}
        />
        <span className="tool-label">Y反向</span>
        <Switch
          size="small"
          checked={calibration.invertY}
          onChange={(checked) => updateCalibration({ ...calibration, invertY: checked })}
        />
      </div>
    </div>
  );

  return (
    <div className={`compass-widget ${editabled ? "is-edit" : "is-locked"}`}>
      <Card size="small" style={cardStyle} bordered={false}>

        <div className="compass-content">
          <div className="compass-minimal">
            <span className="compass-minimal__dir">{directionText}</span>
            <span className="compass-minimal__deg">{computedHeading}°</span>
            {editabled && (
              <Popover content={settingsPanel} trigger="click" placement="top">
                <button type="button" className="compass-settings-btn">
                  设置
                </button>
              </Popover>
            )}
          </div>
        </div>
      </Card>
    </div>
  );
}


