/**
 * GimbalPanel.jsx — Gimbal control panel with live camera preview (Issue #551).
 *
 * Features:
 *   - Interactive 2-D pan/tilt joystick pad (click-drag + touch)
 *   - Live camera stream via rosbridge (sensor_msgs/CompressedImage)
 *   - Camera selector: front / rear / left / right
 *   - Preset positions: CENTER / LEFT / RIGHT / UP / DOWN
 *   - Home button (0°, 0°)
 *   - Person-tracking toggle (publishes std_msgs/Bool)
 *   - Current angle display from /gimbal/state feedback
 *   - Mobile-responsive two-column → single-column layout
 *
 * ROS topics:
 *   /gimbal/cmd              geometry_msgs/Vector3   publish  (x=pan°, y=tilt°)
 *   /gimbal/state            geometry_msgs/Vector3   subscribe
 *   /gimbal/tracking_enabled std_msgs/Bool           publish
 *   /camera/<name>/image_raw/compressed  sensor_msgs/CompressedImage  subscribe
 */

import { useEffect, useRef, useState, useCallback } from 'react';

// ── Constants ─────────────────────────────────────────────────────────────────

const PAN_MIN  = -180;
const PAN_MAX  =  180;
const TILT_MIN =  -45;
const TILT_MAX =   90;

const CAMERAS = [
  { id: 'front', label: 'Front', topic: '/camera/front/image_raw/compressed' },
  { id: 'rear',  label: 'Rear',  topic: '/camera/rear/image_raw/compressed'  },
  { id: 'left',  label: 'Left',  topic: '/camera/left/image_raw/compressed'  },
  { id: 'right', label: 'Right', topic: '/camera/right/image_raw/compressed' },
];

const PRESETS = [
  { label: 'CENTER', pan:   0, tilt:  0 },
  { label: 'LEFT',   pan: -45, tilt:  0 },
  { label: 'RIGHT',  pan:  45, tilt:  0 },
  { label: 'UP',     pan:   0, tilt: 45 },
  { label: 'DOWN',   pan:   0, tilt:-30 },
];

// ── Pan/Tilt pad ──────────────────────────────────────────────────────────────

function GimbalPad({ targetPan, targetTilt, currentPan, currentTilt, onMove }) {
  const canvasRef = useRef(null);
  const dragging  = useRef(false);

  const draw = useCallback(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;
    const ctx = canvas.getContext('2d');
    const W = canvas.width;
    const H = canvas.height;

    ctx.clearRect(0, 0, W, H);
    ctx.fillStyle = '#0a0a1a';
    ctx.fillRect(0, 0, W, H);

    // Grid
    ctx.strokeStyle = '#1e3a5f';
    ctx.lineWidth = 1;
    for (let i = 1; i < 4; i++) {
      ctx.beginPath(); ctx.moveTo(W * i / 4, 0); ctx.lineTo(W * i / 4, H); ctx.stroke();
      ctx.beginPath(); ctx.moveTo(0, H * i / 4); ctx.lineTo(W, H * i / 4); ctx.stroke();
    }

    // Center cross
    ctx.strokeStyle = '#374151';
    ctx.beginPath();
    ctx.moveTo(W / 2, 0);    ctx.lineTo(W / 2, H);
    ctx.moveTo(0, H / 2);    ctx.lineTo(W, H / 2);
    ctx.stroke();

    const panToX  = (p) => ((p - PAN_MIN)  / (PAN_MAX  - PAN_MIN))  * W;
    const tiltToY = (t) => (1 - (t - TILT_MIN) / (TILT_MAX - TILT_MIN)) * H;

    // Targeting line
    const tx = panToX(targetPan);
    const ty = tiltToY(targetTilt);
    ctx.strokeStyle = 'rgba(6,182,212,0.25)';
    ctx.lineWidth = 1;
    ctx.beginPath(); ctx.moveTo(W / 2, H / 2); ctx.lineTo(tx, ty); ctx.stroke();

    // Target ring
    ctx.strokeStyle = '#0891b2';
    ctx.lineWidth = 2;
    ctx.beginPath(); ctx.arc(tx, ty, 10, 0, Math.PI * 2); ctx.stroke();
    // Cross-hair inside ring
    ctx.strokeStyle = '#0e7490';
    ctx.lineWidth = 1;
    ctx.beginPath();
    ctx.moveTo(tx - 6, ty); ctx.lineTo(tx + 6, ty);
    ctx.moveTo(tx, ty - 6); ctx.lineTo(tx, ty + 6);
    ctx.stroke();

    // Current position dot (feedback)
    const cx_ = panToX(currentPan);
    const cy_ = tiltToY(currentTilt);
    ctx.fillStyle = '#22d3ee';
    ctx.beginPath(); ctx.arc(cx_, cy_, 5, 0, Math.PI * 2); ctx.fill();

    // Axis labels
    ctx.fillStyle = '#374151';
    ctx.font = '9px Courier New';
    ctx.fillText(`${PAN_MIN}°`, 3, H / 2 - 3);
    ctx.fillText(`${PAN_MAX}°`, W - 28, H / 2 - 3);
    ctx.fillText(`+${TILT_MAX}°`, W / 2 + 3, 11);
    ctx.fillText(`${TILT_MIN}°`, W / 2 + 3, H - 3);

    // Live readout
    ctx.fillStyle = '#0891b2';
    ctx.font = 'bold 9px Courier New';
    ctx.fillText(`▶ ${targetPan.toFixed(0)}° / ${targetTilt.toFixed(0)}°`, 4, H - 3);
  }, [targetPan, targetTilt, currentPan, currentTilt]);

  useEffect(() => { draw(); }, [draw]);

  const toAngles = (clientX, clientY) => {
    const rect = canvasRef.current.getBoundingClientRect();
    const rx = (clientX - rect.left) / rect.width;
    const ry = (clientY - rect.top)  / rect.height;
    const pan  = PAN_MIN  + rx * (PAN_MAX  - PAN_MIN);
    const tilt = TILT_MAX - ry * (TILT_MAX - TILT_MIN);
    return {
      pan:  Math.max(PAN_MIN,  Math.min(PAN_MAX,  pan)),
      tilt: Math.max(TILT_MIN, Math.min(TILT_MAX, tilt)),
    };
  };

  const onPD = useCallback((e) => {
    dragging.current = true;
    canvasRef.current.setPointerCapture(e.pointerId);
    const { pan, tilt } = toAngles(e.clientX, e.clientY);
    onMove(pan, tilt);
  }, [onMove]);

  const onPM = useCallback((e) => {
    if (!dragging.current) return;
    const { pan, tilt } = toAngles(e.clientX, e.clientY);
    onMove(pan, tilt);
  }, [onMove]);

  const onPU = useCallback(() => { dragging.current = false; }, []);

  return (
    <canvas
      ref={canvasRef}
      width={240}
      height={160}
      className="rounded border border-cyan-950 cursor-crosshair w-full"
      style={{ touchAction: 'none' }}
      onPointerDown={onPD}
      onPointerMove={onPM}
      onPointerUp={onPU}
      onPointerCancel={onPU}
    />
  );
}

// ── Camera feed ───────────────────────────────────────────────────────────────

function CameraFeed({ subscribe, topic }) {
  const [src, setSrc]  = useState(null);
  const [fps, setFps]  = useState(0);
  const cntRef         = useRef(0);
  const timeRef        = useRef(Date.now());

  useEffect(() => {
    setSrc(null);
    cntRef.current  = 0;
    timeRef.current = Date.now();

    const unsub = subscribe(topic, 'sensor_msgs/CompressedImage', (msg) => {
      const fmt = msg.format?.includes('png') ? 'png' : 'jpeg';
      setSrc(`data:image/${fmt};base64,${msg.data}`);
      cntRef.current++;
      const now = Date.now();
      const dt  = now - timeRef.current;
      if (dt >= 1000) {
        setFps(Math.round(cntRef.current * 1000 / dt));
        cntRef.current  = 0;
        timeRef.current = now;
      }
    });
    return unsub;
  }, [subscribe, topic]);

  return (
    <div className="relative flex-1 min-h-0 bg-black rounded-lg border border-cyan-950 flex items-center justify-center overflow-hidden">
      {src ? (
        <img src={src} alt="gimbal camera" className="max-w-full max-h-full object-contain" draggable={false} />
      ) : (
        <div className="flex flex-col items-center gap-2 text-gray-700">
          <div className="text-4xl">📷</div>
          <div className="text-xs tracking-widest">NO SIGNAL</div>
        </div>
      )}
      {src && (
        <div className="absolute top-2 right-2 bg-black bg-opacity-60 text-green-400 text-xs font-mono px-1.5 py-0.5 rounded">
          {fps} fps
        </div>
      )}
    </div>
  );
}

// ── Main component ────────────────────────────────────────────────────────────

export function GimbalPanel({ subscribe, publish }) {
  const [selectedCam,  setSelectedCam]  = useState('front');
  const [targetPan,    setTargetPan]    = useState(0);
  const [targetTilt,   setTargetTilt]   = useState(0);
  const [currentPan,   setCurrentPan]   = useState(0);
  const [currentTilt,  setCurrentTilt]  = useState(0);
  const [tracking,     setTracking]     = useState(false);

  const currentCam = CAMERAS.find(c => c.id === selectedCam);

  // Subscribe to gimbal state feedback
  useEffect(() => {
    const unsub = subscribe('/gimbal/state', 'geometry_msgs/Vector3', (msg) => {
      setCurrentPan(msg.x  ?? 0);
      setCurrentTilt(msg.y ?? 0);
    });
    return unsub;
  }, [subscribe]);

  // Publish gimbal command
  const sendCmd = useCallback((pan, tilt) => {
    const p = Math.round(pan  * 10) / 10;
    const t = Math.round(tilt * 10) / 10;
    setTargetPan(p);
    setTargetTilt(t);
    if (publish) {
      publish('/gimbal/cmd', 'geometry_msgs/Vector3', { x: p, y: t, z: 0.0 });
    }
  }, [publish]);

  // Preset handler
  const applyPreset = useCallback((pan, tilt) => {
    sendCmd(pan, tilt);
  }, [sendCmd]);

  // Tracking toggle
  const toggleTracking = () => {
    const next = !tracking;
    setTracking(next);
    if (publish) {
      publish('/gimbal/tracking_enabled', 'std_msgs/Bool', { data: next });
    }
  };

  return (
    <div className="flex flex-col lg:flex-row h-full gap-3">

      {/* ── Left: Camera + pad ── */}
      <div className="flex flex-col gap-2 flex-1 min-h-0">

        {/* Camera selector */}
        <div className="flex gap-1 shrink-0 flex-wrap">
          {CAMERAS.map((cam) => (
            <button
              key={cam.id}
              onClick={() => setSelectedCam(cam.id)}
              className={`px-3 py-1 text-xs font-bold rounded border tracking-widest transition-colors ${
                selectedCam === cam.id
                  ? 'bg-cyan-950 border-cyan-700 text-cyan-300'
                  : 'bg-gray-950 border-gray-800 text-gray-600 hover:text-gray-300'
              }`}
            >
              {cam.label.toUpperCase()}
            </button>
          ))}
          <div className="flex-1" />
          <div className="text-xs text-gray-700 self-center font-mono truncate max-w-xs">
            {currentCam?.topic}
          </div>
        </div>

        {/* Camera feed */}
        <CameraFeed subscribe={subscribe} topic={currentCam?.topic} />
      </div>

      {/* ── Right: Controls ── */}
      <div className="flex flex-col gap-3 w-full lg:w-72 shrink-0">

        {/* Pan/Tilt pad */}
        <div className="bg-gray-950 rounded-lg border border-cyan-950 p-3 space-y-2">
          <div className="text-xs font-bold tracking-widest text-cyan-700">PAN / TILT</div>
          <GimbalPad
            targetPan={targetPan}
            targetTilt={targetTilt}
            currentPan={currentPan}
            currentTilt={currentTilt}
            onMove={sendCmd}
          />
          <div className="text-xs text-gray-700 text-center">click or drag to aim</div>
        </div>

        {/* Angle readout */}
        <div className="bg-gray-950 rounded-lg border border-cyan-950 p-3 grid grid-cols-2 gap-2">
          <div>
            <div className="text-gray-600 text-xs mb-1">TARGET PAN</div>
            <div className="text-xl font-mono text-cyan-300">{targetPan.toFixed(1)}°</div>
          </div>
          <div>
            <div className="text-gray-600 text-xs mb-1">TARGET TILT</div>
            <div className="text-xl font-mono text-amber-300">{targetTilt.toFixed(1)}°</div>
          </div>
          <div>
            <div className="text-gray-600 text-xs mb-1">ACTUAL PAN</div>
            <div className="text-sm font-mono text-cyan-600">{currentPan.toFixed(1)}°</div>
          </div>
          <div>
            <div className="text-gray-600 text-xs mb-1">ACTUAL TILT</div>
            <div className="text-sm font-mono text-amber-600">{currentTilt.toFixed(1)}°</div>
          </div>
        </div>

        {/* Presets */}
        <div className="bg-gray-950 rounded-lg border border-cyan-950 p-3 space-y-2">
          <div className="text-xs font-bold tracking-widest text-cyan-700">PRESETS</div>
          <div className="grid grid-cols-3 gap-1.5">
            {PRESETS.map((p) => (
              <button
                key={p.label}
                onClick={() => applyPreset(p.pan, p.tilt)}
                className="py-1.5 text-xs font-bold rounded border border-cyan-950 bg-gray-900 text-gray-500 hover:bg-cyan-950 hover:text-cyan-300 hover:border-cyan-800 transition-colors"
              >
                <div>{p.label}</div>
                <div className="text-gray-700 font-normal text-xs">{p.pan}°/{p.tilt}°</div>
              </button>
            ))}
            <button
              onClick={() => applyPreset(0, 0)}
              className="py-1.5 col-span-3 text-xs font-bold rounded border border-gray-800 bg-gray-900 text-gray-600 hover:bg-gray-800 hover:text-gray-300 transition-colors"
            >
              ⌂ HOME (0° / 0°)
            </button>
          </div>
        </div>

        {/* Person tracking */}
        <div className="bg-gray-950 rounded-lg border border-cyan-950 p-3 space-y-2">
          <div className="text-xs font-bold tracking-widest text-cyan-700">PERSON TRACKING</div>
          <div className="flex items-center justify-between">
            <span className={`text-xs ${tracking ? 'text-cyan-400' : 'text-gray-600'}`}>
              {tracking ? 'ON — following person' : 'OFF — manual control'}
            </span>
            <button
              onClick={toggleTracking}
              className={`relative w-10 h-5 rounded-full border transition-colors ${
                tracking
                  ? 'bg-cyan-950 border-cyan-700'
                  : 'bg-gray-900 border-gray-700'
              }`}
            >
              <span className={`absolute top-0.5 w-4 h-4 rounded-full transition-transform ${
                tracking
                  ? 'translate-x-5 bg-cyan-400'
                  : 'translate-x-0.5 bg-gray-600'
              }`} />
            </button>
          </div>
          <div className="text-xs text-gray-700 font-mono">/gimbal/tracking_enabled</div>
        </div>

        {/* Topic reference */}
        <div className="bg-gray-950 rounded border border-gray-800 p-2 text-xs text-gray-700 space-y-1">
          <div className="font-bold text-gray-600 mb-1">ROS TOPICS</div>
          <div><span className="text-gray-600">PUB</span> /gimbal/cmd (Vector3)</div>
          <div><span className="text-gray-600">SUB</span> /gimbal/state (Vector3)</div>
          <div><span className="text-gray-600">PUB</span> /gimbal/tracking_enabled (Bool)</div>
        </div>

      </div>
    </div>
  );
}
