/* GlobeLoader — 200×200 monochrome spinning globe.
   Loads REAL country outlines from world-atlas (Natural Earth 110m countries) via TopoJSON,
   converts to GeoJSON polygons, then renders with a hand-rolled orthographic projection.
   Pure visual; no text. */

const TOPOJSON_URL = 'https://cdn.jsdelivr.net/npm/world-atlas@2/countries-110m.json';

// Cache across instances
let _countriesPromise = null;
function loadCountries() {
  if (_countriesPromise) return _countriesPromise;
  _countriesPromise = (async () => {
    const topoRes = await fetch(TOPOJSON_URL);
    const topo = await topoRes.json();
    // Inline minimal TopoJSON -> GeoJSON converter for the geometries we need.
    const obj = topo.objects.countries;
    const transform = topo.transform;
    const arcs = topo.arcs;
    const tx = transform.translate, sc = transform.scale;

    function decodeArc(idx) {
      const reverse = idx < 0;
      if (reverse) idx = ~idx;
      const arc = arcs[idx];
      let x = 0, y = 0;
      const out = [];
      for (let i = 0; i < arc.length; i++) {
        x += arc[i][0]; y += arc[i][1];
        out.push([x * sc[0] + tx[0], y * sc[1] + tx[1]]);
      }
      return reverse ? out.reverse() : out;
    }
    function arcsToRing(arcIdxs) {
      const ring = [];
      for (let i = 0; i < arcIdxs.length; i++) {
        const pts = decodeArc(arcIdxs[i]);
        if (i > 0) pts.shift(); // dedupe shared endpoint
        for (const p of pts) ring.push(p);
      }
      return ring;
    }
    const polys = []; // array of rings (each ring = array of [lon,lat])
    for (const g of obj.geometries) {
      if (g.type === 'Polygon') {
        for (const ringArcs of g.arcs) polys.push(arcsToRing(ringArcs));
      } else if (g.type === 'MultiPolygon') {
        for (const poly of g.arcs)
          for (const ringArcs of poly) polys.push(arcsToRing(ringArcs));
      }
    }
    return polys;
  })().catch(err => { console.warn('country load failed', err); return []; });
  return _countriesPromise;
}

function GlobeLoader({ size = 200, speed = 1.0, paused = false }) {
  const canvasRef = React.useRef(null);
  const rotRef = React.useRef(0);
  const speedRef = React.useRef(speed);
  const pausedRef = React.useRef(paused);
  const polysRef = React.useRef(null);
  React.useEffect(() => { speedRef.current = speed; }, [speed]);
  React.useEffect(() => { pausedRef.current = paused; }, [paused]);

  React.useEffect(() => {
    let alive = true;
    loadCountries().then(p => { if (alive) polysRef.current = p; });

    const canvas = canvasRef.current;
    if (!canvas) return;
    const dpr = Math.min(window.devicePixelRatio || 1, 2);
    canvas.width = size * dpr;
    canvas.height = size * dpr;
    const ctx = canvas.getContext('2d');
    ctx.setTransform(dpr, 0, 0, dpr, 0, 0);

    const cx = size / 2, cy = size / 2, r = size * 0.46;
    let raf, last = performance.now();

    function project(lon, lat, rotY) {
      const lr = (lon + rotY) * Math.PI / 180;
      const lt = lat * Math.PI / 180;
      const x = Math.cos(lt) * Math.sin(lr);
      const y = -Math.sin(lt);
      const z = Math.cos(lt) * Math.cos(lr);
      return { x: cx + x * r, y: cy + y * r, z };
    }

    function draw(now) {
      const dt = now - last; last = now;
      if (!pausedRef.current) rotRef.current += (dt / 1000) * 24 * speedRef.current;
      const rotY = rotRef.current;

      ctx.clearRect(0, 0, size, size);
      ctx.beginPath(); ctx.arc(cx, cy, r, 0, Math.PI * 2);
      ctx.fillStyle = 'rgba(20, 30, 50, 0.04)'; ctx.fill();

      // graticule
      ctx.strokeStyle = 'rgba(20, 30, 50, 0.10)'; ctx.lineWidth = 0.6;
      for (let lat = -60; lat <= 60; lat += 30) {
        ctx.beginPath(); let started = false;
        for (let lon = -180; lon <= 180; lon += 6) {
          const p = project(lon, lat, rotY);
          if (p.z > 0) {
            if (!started) { ctx.moveTo(p.x, p.y); started = true; } else ctx.lineTo(p.x, p.y);
          } else started = false;
        }
        ctx.stroke();
      }
      for (let lon = -180; lon < 180; lon += 30) {
        ctx.beginPath(); let started = false;
        for (let lat = -85; lat <= 85; lat += 5) {
          const p = project(lon, lat, rotY);
          if (p.z > 0) {
            if (!started) { ctx.moveTo(p.x, p.y); started = true; } else ctx.lineTo(p.x, p.y);
          } else started = false;
        }
        ctx.stroke();
      }

      // real country polygons
      const polys = polysRef.current;
      if (polys) {
        ctx.fillStyle = 'rgba(20, 30, 50, 0.55)';
        ctx.strokeStyle = 'rgba(20, 30, 50, 0.85)';
        ctx.lineWidth = 0.8;
        ctx.lineJoin = 'round';
        for (const ring of polys) {
          ctx.beginPath();
          let drawing = false;
          for (let i = 0; i < ring.length; i++) {
            const [lon, lat] = ring[i];
            const p = project(lon, lat, rotY);
            if (p.z > 0.02) {
              if (!drawing) { ctx.moveTo(p.x, p.y); drawing = true; }
              else ctx.lineTo(p.x, p.y);
            } else { drawing = false; }
          }
          ctx.fill();
          ctx.stroke();
        }
      }

      // outer ring
      ctx.beginPath(); ctx.arc(cx, cy, r, 0, Math.PI * 2);
      ctx.strokeStyle = 'rgba(20, 30, 50, 0.55)'; ctx.lineWidth = 1.2; ctx.stroke();
      // shading
      const grad = ctx.createRadialGradient(cx - r * 0.3, cy - r * 0.3, r * 0.2, cx, cy, r);
      grad.addColorStop(0, 'rgba(255,255,255,0)');
      grad.addColorStop(1, 'rgba(20,30,50,0.10)');
      ctx.beginPath(); ctx.arc(cx, cy, r, 0, Math.PI * 2);
      ctx.fillStyle = grad; ctx.fill();

      raf = requestAnimationFrame(draw);
    }
    raf = requestAnimationFrame(draw);
    return () => { alive = false; cancelAnimationFrame(raf); };
  }, [size]);

  return <canvas ref={canvasRef} style={{ width: size, height: size, display: 'block' }} aria-hidden="true" />;
}

/* ScanWhirl unchanged */
function ScanWhirl({ size = 220, speed = 1.0, paused = false }) {
  const ref = React.useRef(null);
  const rotRef = React.useRef(0);
  const speedRef = React.useRef(speed);
  const pausedRef = React.useRef(paused);
  React.useEffect(() => { speedRef.current = speed; }, [speed]);
  React.useEffect(() => { pausedRef.current = paused; }, [paused]);

  React.useEffect(() => {
    const canvas = ref.current; if (!canvas) return;
    const dpr = Math.min(window.devicePixelRatio || 1, 2);
    canvas.width = size * dpr; canvas.height = size * dpr;
    const ctx = canvas.getContext('2d');
    ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
    const cx = size / 2, cy = size / 2;
    const rings = [
      { r: size * 0.32, count: 8, dir: 1, dotR: 4, alpha: 0.85 },
      { r: size * 0.42, count: 12, dir: -1, dotR: 3, alpha: 0.55 },
      { r: size * 0.48, count: 16, dir: 1, dotR: 2, alpha: 0.30 },
    ];
    const phases = rings.map(r => Array.from({length: r.count}, () => Math.random() * Math.PI * 2));
    const types = rings.map(r => Array.from({length: r.count}, () => Math.random() < 0.25 ? 'check' : 'dot'));
    let raf, last = performance.now();
    function draw(now) {
      const dt = now - last; last = now;
      if (!pausedRef.current) rotRef.current += (dt / 1000) * 0.4 * speedRef.current;
      ctx.clearRect(0, 0, size, size);
      const t = now / 1000;
      for (let i = 0; i < 3; i++) {
        const phase = (t * 0.4 * speedRef.current + i * 0.33) % 1;
        const pr = size * 0.20 + phase * size * 0.30;
        const a = (1 - phase) * 0.18;
        ctx.beginPath(); ctx.arc(cx, cy, pr, 0, Math.PI * 2);
        ctx.strokeStyle = `rgba(40, 100, 70, ${a})`; ctx.lineWidth = 1; ctx.stroke();
      }
      rings.forEach((ring, ri) => {
        ctx.beginPath(); ctx.arc(cx, cy, ring.r, 0, Math.PI * 2);
        ctx.strokeStyle = `rgba(30, 50, 80, ${0.06 + ri * 0.02})`;
        ctx.lineWidth = 0.8; ctx.setLineDash([3, 5]); ctx.stroke(); ctx.setLineDash([]);
        for (let i = 0; i < ring.count; i++) {
          const ang = phases[ri][i] + rotRef.current * ring.dir;
          const x = cx + Math.cos(ang) * ring.r;
          const y = cy + Math.sin(ang) * ring.r;
          for (let k = 1; k <= 6; k++) {
            const tAng = ang - ring.dir * 0.05 * k;
            const tx = cx + Math.cos(tAng) * ring.r;
            const ty = cy + Math.sin(tAng) * ring.r;
            ctx.beginPath(); ctx.arc(tx, ty, ring.dotR * (1 - k / 8), 0, Math.PI * 2);
            ctx.fillStyle = `rgba(58, 138, 85, ${ring.alpha * (1 - k / 6) * 0.25})`; ctx.fill();
          }
          if (types[ri][i] === 'check' && ri === 0) {
            ctx.strokeStyle = `rgba(58, 138, 85, ${ring.alpha})`;
            ctx.lineWidth = 1.6; ctx.lineCap = 'round';
            ctx.beginPath(); ctx.moveTo(x - 4, y); ctx.lineTo(x - 1, y + 3); ctx.lineTo(x + 4, y - 3); ctx.stroke();
          } else {
            const g = ctx.createRadialGradient(x, y, 0, x, y, ring.dotR * 4);
            g.addColorStop(0, `rgba(78, 163, 106, ${ring.alpha * 0.6})`);
            g.addColorStop(1, 'rgba(78, 163, 106, 0)');
            ctx.fillStyle = g;
            ctx.beginPath(); ctx.arc(x, y, ring.dotR * 4, 0, Math.PI * 2); ctx.fill();
            ctx.fillStyle = `rgba(42, 109, 68, ${ring.alpha})`;
            ctx.beginPath(); ctx.arc(x, y, ring.dotR, 0, Math.PI * 2); ctx.fill();
          }
        }
      });
      raf = requestAnimationFrame(draw);
    }
    raf = requestAnimationFrame(draw);
    return () => cancelAnimationFrame(raf);
  }, [size]);
  return <canvas ref={ref} style={{ width: size, height: size, display: 'block' }} aria-hidden="true" />;
}

window.GlobeLoader = GlobeLoader;
window.ScanWhirl = ScanWhirl;
