// RotorCompass — an azimuthal-equidistant rotor display (à la 4O3A RotorGenius).
//
// A world map centred on the operator's QTH (north up) fills the dial, ringed by
// a green azimuth bezel. A green needle shows the antenna heading (two needles
// when an Ultrabeam is bidirectional, the opposite one when reversed); a small
// red marker on the bezel shows the short-path bearing to the DX. Click the dial
// to turn the antenna there.
import { useMemo } from 'react';
import { geoAzimuthalEquidistant, geoPath, geoGraticule10 } from 'd3-geo';
import { feature } from 'topojson-client';
import landTopo from 'world-atlas/land-110m.json';
import { Compass, X } from 'lucide-react';
import { cn } from '@/lib/utils';

// Decode the coastline outline once (≈110 m simplified land polygons).
const LAND = feature(landTopo as any, (landTopo as any).objects.land);
const GRATICULE = geoGraticule10();

interface Props {
  bearing?: number | null;       // short-path azimuth to DX (deg)
  headings: number[];            // antenna heading(s) — rotor + Ultrabeam pattern
  centerLat?: number | null;     // operator latitude (projection centre)
  centerLon?: number | null;     // operator longitude
  rotorEnabled?: boolean;
  onGoto?: (az: number) => void; // click-to-turn
  onClose?: () => void;
}

const SIZE = 168;
const C = SIZE / 2;
const R = C - 6;          // outer bezel radius
const MAP_R = R - 6;      // map/clip radius (inside the bezel)

function pt(az: number, radius: number): [number, number] {
  const a = ((az - 90) * Math.PI) / 180;
  return [C + radius * Math.cos(a), C + radius * Math.sin(a)];
}

export function RotorCompass({ bearing, headings, centerLat, centerLon, rotorEnabled, onGoto, onClose }: Props) {
  const cardinals = useMemo(
    () => [ { d: 0, l: 'N' }, { d: 45, l: 'NE' }, { d: 90, l: 'E' }, { d: 135, l: 'SE' },
            { d: 180, l: 'S' }, { d: 225, l: 'SW' }, { d: 270, l: 'W' }, { d: 315, l: 'NW' } ],
    [],
  );

  // Project the world centred on the QTH (north up; antipode at the bezel).
  const { land, grat } = useMemo(() => {
    if (centerLat == null || centerLon == null) return { land: '', grat: '' };
    const proj = geoAzimuthalEquidistant()
      .rotate([-centerLon, -centerLat])
      .clipAngle(179.9)
      .scale(MAP_R / Math.PI)
      .translate([C, C]);
    const path = geoPath(proj as any);
    return { land: path(LAND as any) || '', grat: path(GRATICULE as any) || '' };
  }, [centerLat, centerLon]);

  function handleClick(e: React.MouseEvent<SVGSVGElement>) {
    if (!onGoto) return;
    const rect = e.currentTarget.getBoundingClientRect();
    const x = ((e.clientX - rect.left) / rect.width) * SIZE - C;
    const y = ((e.clientY - rect.top) / rect.height) * SIZE - C;
    let az = (Math.atan2(y, x) * 180) / Math.PI + 90;
    az = ((az % 360) + 360) % 360;
    onGoto(Math.round(az));
  }

  const headLabel = headings.length ? headings[0] : null;

  return (
    <section className="flex flex-col h-full min-h-0 rounded-lg border border-border bg-card overflow-hidden">
      {/* Header — matches the WinKeyer / Voice keyer panels. */}
      <div className="flex items-center gap-2 px-3 py-1.5 bg-muted/40 border-b border-border shrink-0">
        <Compass className="size-4 text-primary shrink-0" />
        <span className="text-xs font-semibold uppercase tracking-wider text-muted-foreground">Rotor</span>
        <span className={cn('size-2 rounded-full', rotorEnabled ? 'bg-emerald-500' : 'bg-muted-foreground/40')}
          title={rotorEnabled ? 'Rotator connected' : 'Rotator disabled'} />
        <div className="flex-1" />
        <span className="font-mono text-sm font-bold text-emerald-700 tabular-nums">
          {headLabel != null ? `${Math.round(headLabel).toString().padStart(3, '0')}°` : '—'}
        </span>
        {onClose && (
          <button className="text-muted-foreground hover:text-foreground" title="Hide rotor" onClick={onClose}>
            <X className="size-3.5" />
          </button>
        )}
      </div>

      <div className="flex items-center justify-center p-2 min-h-0">
        <svg
          viewBox={`0 0 ${SIZE} ${SIZE}`}
          className={onGoto ? 'cursor-pointer select-none' : 'select-none'}
          style={{ width: SIZE, height: SIZE }}
          onClick={handleClick}
        >
          <defs>
            <clipPath id="rotorDial"><circle cx={C} cy={C} r={MAP_R} /></clipPath>
          </defs>
          {/* water + world map, clipped to the dial */}
          <circle cx={C} cy={C} r={MAP_R} fill="#d3e7f1" />
          <g clipPath="url(#rotorDial)">
            {grat && <path d={grat} fill="none" stroke="#9cc0d6" strokeWidth={0.4} opacity={0.7} />}
            {land && <path d={land} fill="#dfe2cf" stroke="#9aa589" strokeWidth={0.4} />}
          </g>
          {/* green azimuth bezel */}
          <circle cx={C} cy={C} r={R} fill="none" stroke="#16a34a" strokeWidth={5} />

          {/* ticks every 10°, longer at 30° */}
          {Array.from({ length: 36 }, (_, i) => i * 10).map((d) => {
            const major = d % 30 === 0;
            const [x1, y1] = pt(d, MAP_R);
            const [x2, y2] = pt(d, MAP_R - (major ? 7 : 4));
            return <line key={d} x1={x1} y1={y1} x2={x2} y2={y2} stroke="#475569" strokeWidth={major ? 1 : 0.6} opacity={0.7} />;
          })}
          {/* cardinal labels + degree numbers at 45° */}
          {cardinals.map(({ d, l }) => {
            const [x, y] = pt(d, MAP_R - 13);
            return <text key={l} x={x} y={y} textAnchor="middle" dominantBaseline="central" className="fill-slate-700" style={{ fontSize: l.length > 1 ? 7 : 9, fontWeight: 700 }}>{l}</text>;
          })}

          {/* DX short-path bearing → small red marker on the bezel */}
          {bearing != null && (() => { const [x, y] = pt(bearing, MAP_R); return (
            <circle cx={x} cy={y} r={3} fill="#dc2626" stroke="#fff" strokeWidth={1} />
          ); })()}

          {/* antenna heading needle(s) — green; two when bidirectional */}
          {headings.map((h, i) => { const [x, y] = pt(h, MAP_R - 2); return (
            <g key={i}>
              <line x1={C} y1={C} x2={x} y2={y} stroke="#15803d" strokeWidth={3} strokeLinecap="round" opacity={i === 0 ? 1 : 0.55} />
              <polygon points={`${x},${y} ${pt(h - 5, MAP_R - 12).join(',')} ${pt(h + 5, MAP_R - 12).join(',')}`} fill="#15803d" opacity={i === 0 ? 1 : 0.55} />
            </g>
          ); })}
          <circle cx={C} cy={C} r={3.5} fill="#15803d" stroke="#fff" strokeWidth={1} />
        </svg>
      </div>
    </section>
  );
}
