/* ========================================================================
   LUMINA — Projector Calibration & Test Patterns
   Critical tool for multi-projector videomapping on complex buildings.
   Floating panel with full-screen test patterns + per-projector
   focus / convergence / edge-blend / color calibration aids.
   ======================================================================== */
(function () {
  const { useState, useEffect, useRef } = React;

  const PATTERNS = [
    { id: "grid", label: "Grid · Geometry" },
    { id: "convergence", label: "Convergence" },
    { id: "checkerboard", label: "Checker · Pixel grid" },
    { id: "colorbars", label: "SMPTE Color Bars" },
    { id: "gamma", label: "Gamma · Brightness" },
    { id: "rgbs", label: "RGB Separation" },
    { id: "edgeblend", label: "Edge Blend Test" },
    { id: "circles", label: "Centering Circles" },
    { id: "focus", label: "Focus · Star burst" },
    { id: "dot", label: "Single Dot · Hot pixel" },
    { id: "white", label: "Full White" },
    { id: "black", label: "Full Black" },
  ];

  function drawPattern(ctx, w, h, kind, opts) {
    ctx.fillStyle = "#000";
    ctx.fillRect(0, 0, w, h);

    if (kind === "grid") {
      const step = Math.min(w, h) / 20;
      ctx.strokeStyle = "#fff"; ctx.lineWidth = 1;
      for (let x = 0; x <= w; x += step) {
        ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, h); ctx.stroke();
      }
      for (let y = 0; y <= h; y += step) {
        ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(w, y); ctx.stroke();
      }
      // Border
      ctx.lineWidth = 4;
      ctx.strokeStyle = "#ffae3a";
      ctx.strokeRect(2, 2, w - 4, h - 4);
      // Center cross
      ctx.strokeStyle = "#4be6ff"; ctx.lineWidth = 2;
      ctx.beginPath();
      ctx.moveTo(w/2, 0); ctx.lineTo(w/2, h);
      ctx.moveTo(0, h/2); ctx.lineTo(w, h/2);
      ctx.stroke();
      // Corner crosshairs
      const o = step * 2;
      ctx.strokeStyle = "#ff3b3b";
      [[o, o], [w-o, o], [w-o, h-o], [o, h-o]].forEach(([cx, cy]) => {
        ctx.beginPath();
        ctx.arc(cx, cy, step * 0.4, 0, Math.PI * 2);
        ctx.moveTo(cx - step * 0.6, cy); ctx.lineTo(cx + step * 0.6, cy);
        ctx.moveTo(cx, cy - step * 0.6); ctx.lineTo(cx, cy + step * 0.6);
        ctx.stroke();
      });
    }

    if (kind === "convergence") {
      const cx = w / 2, cy = h / 2;
      const lines = 24;
      for (let i = 0; i < lines; i++) {
        const a = (i / lines) * Math.PI * 2;
        const r = Math.max(w, h);
        ctx.strokeStyle = `hsl(${(i / lines) * 360}, 100%, 60%)`;
        ctx.lineWidth = 1;
        ctx.beginPath();
        ctx.moveTo(cx, cy);
        ctx.lineTo(cx + Math.cos(a) * r, cy + Math.sin(a) * r);
        ctx.stroke();
      }
      // Center bullseye
      for (let i = 1; i <= 6; i++) {
        ctx.strokeStyle = i % 2 ? "#fff" : "#000";
        ctx.lineWidth = 2;
        ctx.beginPath(); ctx.arc(cx, cy, i * 30, 0, Math.PI * 2); ctx.stroke();
      }
    }

    if (kind === "checkerboard") {
      const sz = opts.checkerSize || 20;
      for (let y = 0; y < h; y += sz) {
        for (let x = 0; x < w; x += sz) {
          ctx.fillStyle = ((x / sz + y / sz) | 0) % 2 ? "#fff" : "#000";
          ctx.fillRect(x, y, sz, sz);
        }
      }
    }

    if (kind === "colorbars") {
      const colors = ["#fff", "#ff0", "#0ff", "#0f0", "#f0f", "#f00", "#00f", "#000"];
      colors.forEach((c, i) => {
        ctx.fillStyle = c;
        ctx.fillRect(i * w / colors.length, 0, w / colors.length, h * 0.7);
      });
      // Bottom: gradient + black levels
      const black = ["#000", "#080808", "#101010"];
      black.forEach((c, i) => {
        ctx.fillStyle = c;
        ctx.fillRect(i * w / black.length, h * 0.7, w / black.length, h * 0.3);
      });
    }

    if (kind === "gamma") {
      const N = 20;
      for (let i = 0; i < N; i++) {
        const g = Math.floor((i / (N - 1)) * 255);
        ctx.fillStyle = `rgb(${g},${g},${g})`;
        ctx.fillRect((i / N) * w, 0, w / N + 1, h);
        ctx.fillStyle = g > 127 ? "#000" : "#fff";
        ctx.font = "12px monospace";
        ctx.textAlign = "center";
        ctx.fillText(g.toString(), (i + 0.5) * w / N, h - 10);
      }
    }

    if (kind === "rgbs") {
      ctx.fillStyle = "#f00"; ctx.fillRect(0, 0, w / 3, h);
      ctx.fillStyle = "#0f0"; ctx.fillRect(w / 3, 0, w / 3, h);
      ctx.fillStyle = "#00f"; ctx.fillRect(2 * w / 3, 0, w / 3, h);
    }

    if (kind === "edgeblend") {
      const half = w / 2;
      // Left projector simulation
      const g1 = ctx.createLinearGradient(0, 0, half + 100, 0);
      g1.addColorStop(0, "#ff7a1a");
      g1.addColorStop(0.7, "#ff7a1a");
      g1.addColorStop(1, "rgba(255,122,26,0)");
      ctx.fillStyle = g1;
      ctx.fillRect(0, 0, half + 100, h);
      // Right projector simulation
      const g2 = ctx.createLinearGradient(half - 100, 0, w, 0);
      g2.addColorStop(0, "rgba(75,230,255,0)");
      g2.addColorStop(0.3, "#4be6ff");
      g2.addColorStop(1, "#4be6ff");
      ctx.globalCompositeOperation = "lighter";
      ctx.fillStyle = g2;
      ctx.fillRect(half - 100, 0, w - half + 100, h);
      ctx.globalCompositeOperation = "source-over";
      // Center reference line
      ctx.strokeStyle = "#fff"; ctx.lineWidth = 1;
      ctx.beginPath(); ctx.moveTo(half, 0); ctx.lineTo(half, h); ctx.stroke();
      // Label
      ctx.fillStyle = "#fff"; ctx.font = "bold 14px monospace";
      ctx.textAlign = "center";
      ctx.fillText("OVERLAP ZONE", half, 24);
    }

    if (kind === "circles") {
      const cx = w / 2, cy = h / 2;
      ctx.strokeStyle = "#fff"; ctx.lineWidth = 2;
      for (let r = 50; r < Math.max(w, h); r += 50) {
        ctx.beginPath(); ctx.arc(cx, cy, r, 0, Math.PI * 2); ctx.stroke();
      }
      ctx.fillStyle = "#ffae3a";
      ctx.beginPath(); ctx.arc(cx, cy, 6, 0, Math.PI * 2); ctx.fill();
    }

    if (kind === "focus") {
      const cx = w / 2, cy = h / 2;
      const N = 60;
      ctx.strokeStyle = "#fff";
      ctx.lineWidth = 1;
      for (let i = 0; i < N; i++) {
        const a = (i / N) * Math.PI * 2;
        const r1 = 40, r2 = Math.min(w, h) * 0.45;
        ctx.beginPath();
        ctx.moveTo(cx + Math.cos(a) * r1, cy + Math.sin(a) * r1);
        ctx.lineTo(cx + Math.cos(a) * r2, cy + Math.sin(a) * r2);
        ctx.stroke();
      }
      ctx.font = "bold 11px monospace";
      ctx.fillStyle = "#ffae3a";
      ctx.textAlign = "center";
      ctx.fillText("FOCUS HERE", cx, cy);
    }

    if (kind === "dot") {
      ctx.fillStyle = "#fff";
      ctx.fillRect(w / 2 - 1, h / 2 - 1, 2, 2);
    }

    if (kind === "white") { ctx.fillStyle = "#fff"; ctx.fillRect(0, 0, w, h); }
    if (kind === "black") { ctx.fillStyle = "#000"; ctx.fillRect(0, 0, w, h); }
  }

  function CalibrationButton() {
    const [open, setOpen] = useState(false);
    const [pattern, setPattern] = useState(null);
    const [overlay, setOverlay] = useState(false);
    const [checker, setChecker] = useState(20);
    const cnvRef = useRef(null);
    const winRef = useRef(null);

    // Local preview render
    useEffect(() => {
      if (!cnvRef.current || !pattern) return;
      const c = cnvRef.current;
      const dpr = 2;
      c.width = c.clientWidth * dpr;
      c.height = c.clientHeight * dpr;
      const ctx = c.getContext("2d");
      drawPattern(ctx, c.width, c.height, pattern, { checkerSize: checker * dpr });
    }, [pattern, checker]);

    // External fullscreen window
    const openFullscreen = (p) => {
      if (!winRef.current || winRef.current.closed) {
        winRef.current = window.open("", "lumina_calib_" + Date.now(),
          "width=1280,height=720,menubar=no,toolbar=no,location=no");
        if (!winRef.current) { alert("Popup blocked"); return; }
        winRef.current.document.write(`<!doctype html><html><head><title>Lumina · Calibration</title>
          <style>body{margin:0;background:#000;cursor:none;overflow:hidden}canvas{display:block}
          .info{position:fixed;top:10px;left:10px;color:#ffae3a;font:11px monospace;
            text-shadow:0 0 4px #ff7a1a;opacity:0.7;letter-spacing:0.2em}
          body.idle .info{opacity:0}</style></head><body><canvas id="c"></canvas>
          <div class="info">CALIBRATION · double-click for fullscreen · ESC closes</div>
          <script>
            const c=document.getElementById("c");const cx=c.getContext("2d");
            let last=Date.now();
            function r(){c.width=window.innerWidth;c.height=window.innerHeight;
              window.__redraw&&window.__redraw(cx,c.width,c.height);
              if(Date.now()-last>2000)document.body.classList.add("idle");
              requestAnimationFrame(r);}
            document.addEventListener("dblclick",()=>{
              if(!document.fullscreenElement)document.documentElement.requestFullscreen();
              else document.exitFullscreen();});
            document.addEventListener("keydown",e=>{if(e.key==="Escape")window.close();});
            document.addEventListener("mousemove",()=>{last=Date.now();document.body.classList.remove("idle");});
            r();
          </script></body></html>`);
        winRef.current.document.close();
      }
      const win = winRef.current;
      win.__redraw = (cx, w, h) => drawPattern(cx, w, h, p, { checkerSize: checker });
      win.focus();
    };

    const fullStop = () => {
      if (winRef.current && !winRef.current.closed) winRef.current.close();
      setPattern(null);
    };

    if (!open) {
      return (
        <button onClick={() => setOpen(true)}
          style={{
            padding: "8px 14px", border: "none", borderRadius: 8,
            background: "linear-gradient(180deg, var(--panel-hi), var(--panel))",
            color: "var(--led-cyan)",
            fontFamily: "var(--font-display)", fontWeight: 800, fontSize: 11,
            letterSpacing: "0.18em", textTransform: "uppercase",
            cursor: "pointer",
            boxShadow: "var(--nshadow-out-sm)",
            display: "flex", alignItems: "center", gap: 6,
            textShadow: "0 0 4px var(--led-cyan-glow)",
          }}>
          ◎ Calibrate {pattern ? "·●" : ""}
        </button>
      );
    }

    return (
      <div style={{
        width: 380,
        background: "linear-gradient(180deg, var(--panel-hi), var(--panel-lo))",
        borderRadius: 10, padding: 12,
        boxShadow: "0 20px 50px rgba(0,0,0,0.7), 0 0 0 1px rgba(75,230,255,0.3)",
      }}>
        <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 10 }}>
          <span style={{
            fontFamily: "var(--font-display)", fontWeight: 800, fontSize: 12,
            letterSpacing: "0.18em", textTransform: "uppercase",
            color: "var(--led-cyan)", flex: 1,
            textShadow: "0 0 4px var(--led-cyan-glow)",
          }}>◎ Projector Calibration</span>
          <button data-lum-close="1" className="nbtn compact" onClick={() => setOpen(false)} style={{ padding: "2px 7px" }}>×</button>
        </div>

        <div style={{ fontSize: 9, color: "var(--ink-faint)", marginBottom: 8, lineHeight: 1.5 }}>
          Pop out a test pattern to a 2nd monitor / projector to align focus, geometry, color, and edge-blend overlap.
        </div>

        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 4, marginBottom: 10 }}>
          {PATTERNS.map(p => (
            <button key={p.id}
              onClick={() => { setPattern(p.id); openFullscreen(p.id); }}
              className={`nbtn compact ${pattern === p.id ? "glow pressed" : ""}`}
              style={{ padding: "6px 4px", fontSize: 9, textAlign: "left", justifyContent: "flex-start" }}>
              {p.label}
            </button>
          ))}
        </div>

        {/* Preview */}
        <div style={{
          width: "100%", aspectRatio: "16/9",
          borderRadius: 6, overflow: "hidden",
          background: "#000",
          boxShadow: "var(--nshadow-in-sm)",
          marginBottom: 8,
        }}>
          {pattern ? <canvas ref={cnvRef} style={{ width: "100%", height: "100%" }} />
            : <div style={{
                width: "100%", height: "100%",
                display: "grid", placeItems: "center",
                color: "var(--ink-faint)", fontSize: 10,
                fontFamily: "var(--font-mono)",
              }}>preview</div>}
        </div>

        {pattern === "checkerboard" && (
          <div style={{ display: "grid", gridTemplateColumns: "40px 1fr 40px",
            gap: 6, alignItems: "center", marginBottom: 8 }}>
            <span className="cap" style={{ fontSize: 9 }}>Size</span>
            <input type="range" min="2" max="100" step="1" value={checker}
              onChange={(e) => setChecker(Number(e.target.value))} style={{ width: "100%" }} />
            <span style={{ fontFamily: "var(--font-mono)", fontSize: 9, color: "var(--led-cyan)" }}>
              {checker}px
            </span>
          </div>
        )}

        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 6 }}>
          <button className="nbtn glow pressed" onClick={() => {
            if (pattern) openFullscreen(pattern);
            else { setPattern("grid"); openFullscreen("grid"); }
          }} style={{ padding: "8px", fontWeight: 700 }}>
            ↗ Pop Out
          </button>
          <button className="nbtn compact danger" onClick={fullStop}
            style={{ padding: "8px" }} disabled={!pattern}>
            ■ Stop
          </button>
        </div>

        <div style={{ fontSize: 9, color: "var(--ink-faint)", lineHeight: 1.5,
          marginTop: 8, textAlign: "center" }}>
          Drag popup to projector display · double-click for fullscreen
        </div>
      </div>
    );
  }

  function mount() {
    if (!window.React || !window.ReactDOM) { setTimeout(mount, 100); return; }
    let host = document.getElementById("__calib_panel");
    if (!host) {
      host = document.createElement("div");
      host.id = "__calib_panel";
      host.style.cssText = "position: fixed; z-index: 596;";
      document.body.appendChild(host);
    }
    ReactDOM.createRoot(host).render(<CalibrationButton />);
  }
  if (document.readyState === "complete" || document.readyState === "interactive") setTimeout(mount, 1300);
  else document.addEventListener("DOMContentLoaded", () => setTimeout(mount, 1300));
})();
