// 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[];            // radiating heading(s) — rotor + Ultrabeam pattern
  boomHeading?: number | null;   // mechanical boom (rotor) azimuth, shown grey when it differs
  pattern?: 'normal' | 'reverse' | 'bi' | null; // Ultrabeam pattern (for the badge)
  centerLat?: number | null;     // operator latitude (projection centre)
  centerLon?: number | null;     // operator longitude
  rotorEnabled?: boolean;
  rotors?: string[];             // logical rotor names; >1 → show a selector
  activeRotor?: number;          // index of the selected rotor (0-based)
  onSelectRotor?: (i: number) => void; // switch the active rotor
  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, boomHeading, pattern, centerLat, centerLon, rotorEnabled, rotors, activeRotor, onSelectRotor, 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-success' : 'bg-muted-foreground/40')}
          title={rotorEnabled ? 'Rotator connected' : 'Rotator disabled'} />
        <div className="flex-1" />
        {pattern && (
          <span
            className={cn('px-1 py-px rounded text-[9px] font-bold tracking-wide',
              pattern === 'reverse' ? 'bg-warning-muted text-warning-muted-foreground'
                : pattern === 'bi' ? 'bg-info-muted text-info-muted-foreground'
                : 'bg-success-muted text-success-muted-foreground')}
            title={pattern === 'reverse' ? 'Ultrabeam reversed — radiates opposite the boom'
              : pattern === 'bi' ? 'Ultrabeam bidirectional — radiates both ways'
              : 'Ultrabeam normal'}>
            {pattern === 'reverse' ? 'REV' : pattern === 'bi' ? 'BI' : 'NORM'}
          </span>
        )}
        <span className="font-mono text-sm font-bold text-success 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>

      {/* Multiple rotors — pick which one the dial shows and turns. */}
      {rotors && rotors.length > 1 && (
        <div className="flex flex-wrap gap-1 px-2 pt-1.5">
          {rotors.map((nm, i) => {
            const active = (activeRotor ?? 0) === i;
            const label = nm?.trim() || `Rotor ${i + 1}`;
            return (
              <button
                key={i}
                onClick={() => onSelectRotor?.(i)}
                className={cn(
                  'flex-1 min-w-[48px] px-1.5 py-0.5 rounded text-[10px] font-semibold truncate transition-colors',
                  active
                    ? 'bg-success text-success-foreground'
                    : 'bg-muted text-muted-foreground hover:bg-muted/70',
                )}
                title={label}
              >
                {label}
              </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} />
          ); })()}

          {/* mechanical boom (rotor) heading — grey dashed needle, shown when the
              Ultrabeam radiates somewhere other than the boom (reverse/bi) so the
              operator sees where the antenna physically points vs where it boom-sits */}
          {boomHeading != null && pattern && pattern !== 'normal' && (() => {
            const [x, y] = pt(boomHeading, MAP_R - 2);
            return (
              <g>
                <title>Boom (rotor) {Math.round(boomHeading)}°</title>
                <line x1={C} y1={C} x2={x} y2={y} stroke="#64748b" strokeWidth={2} strokeDasharray="3 3" strokeLinecap="round" />
                <circle cx={x} cy={y} r={3} fill="#64748b" stroke="#fff" strokeWidth={1} />
              </g>
            );
          })()}

          {/* radiating 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>
  );
}
