// entry-ring.jsx — the motto ring entry. The orbiting slogan (title-page treatment:
// Helvetica light) masks its answer as blanks that fill letter-by-letter as you type.
// A separate safe-lock dial rotates a notch per keystroke — independent of the spin —
// clicking like a combination lock; on the right word it flashes blue and the whole
// thing fades out to reveal the real title page.
//
// Tweaks: Show (Sentence "Virtualis fit ___ ?" | Slogan only — no field, just the
// caption) · Caption (Helvetica black or Times lowercase — styles COMPLETE THE …) · Word.

const { useState: useS, useEffect: useE, useRef: useR } = React;
const { BLUE, RED, HELV, TIMES, MONO, isRight, reveal, normCase, useMode, useReveal, EntryStage } = window;
const { useTweaks, TweaksPanel, TweakSection, TweakRadio, TweakText, TweakSlider } = window;

const RING_DEFAULTS = /*EDITMODE-BEGIN*/{
  "presentation": "sentence",
  "type": "helveticaBlack",
  "word": "slogan",
  "revealS": 2
}/*EDITMODE-END*/;

// safe-lock dial around the cube — my design: a thin rim + 48 radial ticks (every
// 4th longer) plus the title-page inner ring. This layer is rotated by typing.
function OrnamentBezel({ ink, accent, hot }) {
  const ticks = [];
  for (let i = 0; i < 48; i++) {
    const a = (i / 48) * Math.PI * 2;
    const major = i % 4 === 0;
    const rin = major ? 73 : 78, rout = 86;
    ticks.push({ x1: 200 + Math.cos(a) * rin, y1: 200 + Math.sin(a) * rin, x2: 200 + Math.cos(a) * rout, y2: 200 + Math.sin(a) * rout, major });
  }
  return (
    <g>
      <circle cx="200" cy="200" r="90" fill="none" stroke={hot ? BLUE : ink} strokeWidth="1" opacity={hot ? 0.85 : 0.2} style={{ transition: "stroke .4s, opacity .4s" }} />
      <circle cx="200" cy="200" r="66" fill="none" stroke={hot ? BLUE : (accent || ink)} strokeWidth="1" opacity={hot ? 0.9 : 0.32} style={{ transition: "stroke .4s, opacity .4s" }} />
      {ticks.map((t, i) => (
        <line key={i} x1={t.x1} y1={t.y1} x2={t.x2} y2={t.y2} stroke={hot ? BLUE : ink} strokeWidth={t.major ? 1.4 : 0.8} opacity={hot ? 0.7 : (t.major ? 0.34 : 0.16)} style={{ transition: "stroke .4s, opacity .4s" }} />
      ))}
    </g>
  );
}

function MottoRingEntry() {
  const mode = useMode();
  const [t, setTweak] = useTweaks(RING_DEFAULTS);
  const rev = useReveal(Math.round(t.revealS * 500));
  const [v, setV] = useS("");
  const [denied, setDenied] = useS(false);
  const ref = useR(null);
  const right = isRight(v);
  const focus = () => { try { ref.current && ref.current.focus(); } catch (e) {} };
  useE(() => { const id = setTimeout(focus, 480); return () => clearTimeout(id); }, [t.presentation]);
  const onChange = (e) => {
    const val = normCase(e.target.value); setV(val); setDenied(false);
    if (!rev.content && isRight(val)) { window.Sfx && Sfx.unlock && Sfx.unlock(); rev.solve(); }
  };
  const onKey = (e) => {
    if (e.key === "Enter" && v.trim() && !isRight(v)) {
      setDenied(true); window.Sfx && Sfx.deny && Sfx.deny();
      setTimeout(() => setDenied(false), 900);   // one brief blink, then back to ink
      return;
    }
    window.Sfx && Sfx.latch && Sfx.latch();
  };

  const labelHelv = t.type === "helveticaBlack";
  const ink = mode.ink;
  // oars mirror the ink: dark ground → white-oar mark, light → black-oar recolour
  const markSrc = mode.night ? "assets/kopelasia-mark.png" : "assets/kopelasia-mark-black.png";
  // orbiting slogan: the title-page treatment — Helvetica light, the three
  // capitalisations, masked and filling as you type (never tweak-controlled).
  const ringStr = "VIRTUALIS FIT " + reveal(v, "upper") + " \u00b7 virtualis fit " + reveal(v, "lower") + " \u00b7 Virtualis fit " + reveal(v, "title") + " \u00b7\u00a0";
  // the safe dial turns a notch per typed letter, and takes a long full swing on
  // unlock; its rotation is deliberately separate from the spinning slogan.
  const dial = -(v.length * 27) - (right ? 150 : 0);
  // the sentence stays exactly as before — Times; "Virtualis" upright, "fit" oblique,
  // the word upright.
  const inputStyle = {
    fontFamily: TIMES, fontStyle: "normal", fontWeight: 400, fontSize: "clamp(22px,3vw,30px)",
    color: right ? BLUE : denied ? RED : ink, background: "transparent", border: "none", borderBottom: "2px solid " + (right ? BLUE : denied ? RED : ink),
    outline: "none", textAlign: "center", width: Math.max(6, v.length + 1) + "ch", padding: "0 0.12em 4px",
    caretColor: ink, transition: "color .3s, border-color .3s",
  };

  return (
    <React.Fragment>
      <EntryStage mode={mode} rev={rev} fadeS={t.revealS}>
        <div onClick={focus} style={{ position: "absolute", inset: 0, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", gap: "clamp(16px,4vh,42px)", padding: "0 20px" }}>
          <div style={{ position: "relative", width: "min(560px, 88vw, 64vh)", aspectRatio: "1 / 1" }}>
            {/* the slogan — Helvetica light, spinning continuously (the title-page ring) */}
            <svg viewBox="0 0 400 400" className="es-spin" style={{ position: "absolute", inset: 0, width: "100%", height: "100%", overflow: "visible", willChange: "transform" }}>
              <defs><path id="er-path" d="M 80,200 a 120,120 0 1,1 240,0 a 120,120 0 1,1 -240,0" /></defs>
              <text fontFamily={HELV} fontWeight="300" fontSize="13.5" fill={right ? BLUE : mode.accent2} letterSpacing="0.5" style={{ transition: "fill .4s" }}>
                <textPath href="#er-path" textLength="752" lengthAdjust="spacing">{ringStr}</textPath>
              </text>
            </svg>
            {/* the safe-lock dial — rotates with your typing, independent of the spin */}
            <svg viewBox="0 0 400 400" style={{ position: "absolute", inset: 0, width: "100%", height: "100%", overflow: "visible", transform: "rotate(" + dial + "deg)", transition: "transform " + (right ? "1.25s" : "0.45s") + " cubic-bezier(.3,.75,.2,1)" }}>
              <OrnamentBezel ink={ink} accent={mode.accent2} hot={right} />
            </svg>
            <img src={markSrc} alt="" style={{ position: "absolute", top: "50%", left: "50%", transform: "translate(-50%,-50%)", width: "22%", height: "22%" }} />
          </div>

          {t.presentation === "sentence" ? (
            <div style={{ fontFamily: TIMES, fontWeight: 400, fontSize: "clamp(22px,3vw,30px)", color: ink, display: "inline-flex", alignItems: "baseline", gap: "0.3em", whiteSpace: "nowrap", maxWidth: "94vw", padding: "0.3em 0.65em", border: "1.5px solid " + (denied ? RED : "transparent"), transition: "border-color .3s" }}>
              <span>Virtualis</span>
              <span style={{ fontStyle: "italic" }}>fit</span>
              <input ref={ref} value={v} onChange={onChange} onKeyDown={onKey} aria-label="the missing word" placeholder="______" style={inputStyle} />
              <span>?</span>
            </div>
          ) : (
            <div style={{ display: "flex", flexDirection: "column", alignItems: "center" }}>
              <div style={{ fontFamily: labelHelv ? HELV : TIMES, fontSize: labelHelv ? 13 : 21, fontWeight: labelHelv ? 900 : 400, letterSpacing: labelHelv ? "0.22em" : "0.01em", textTransform: labelHelv ? "uppercase" : "lowercase", opacity: 0.75, color: denied ? RED : ink, transition: "color .3s" }}>Complete the {t.word}</div>
              <input ref={ref} value={v} onChange={onChange} onKeyDown={onKey} aria-label="the missing word"
                style={{ position: "absolute", opacity: 0, width: 1, height: 1, pointerEvents: "none" }} />
            </div>
          )}
        </div>
      </EntryStage>

      <TweaksPanel title="Tweaks">
        <TweakSection label="Ring">
          <TweakRadio label="Show" value={t.presentation} onChange={(x) => setTweak("presentation", x)}
            options={[{ value: "sentence", label: "Sentence" }, { value: "label", label: "Slogan only" }]} />
          <TweakRadio label="Caption" value={t.type} onChange={(x) => setTweak("type", x)}
            options={[{ value: "helveticaBlack", label: "Helv. black" }, { value: "timesLower", label: "Times l/c" }]} />
          <TweakText label="Word" value={t.word} placeholder="slogan" onChange={(x) => setTweak("word", x)} />
          <TweakSlider label="Reveal fade (s)" min={0.6} max={5} step={0.2} value={t.revealS} onChange={(x) => setTweak("revealS", x)} />
        </TweakSection>
      </TweaksPanel>
    </React.Fragment>
  );
}
ReactDOM.createRoot(document.getElementById("root")).render(<MottoRingEntry />);
