// ════════════════════════════════════════════════════════════════
// App — scroll/parallax engine + root + Tweaks
// ════════════════════════════════════════════════════════════════

const ENGINE = { parallax: 1 };  // live config the rAF loop reads

function clamp01(v) { return v < 0 ? 0 : v > 1 ? 1 : v; }

function useParallaxEngine(onChapter) {
  React.useEffect(() => {
    const root = document.getElementById("root");
    const scenes = Array.from(document.querySelectorAll(".scene"));
    const meter = document.querySelector(".descent-meter i");
    const marble = document.querySelector(".descent-marble");
    const stars = document.querySelector(".descent-stars");
    const threadPath = document.querySelector(".thread path");
    let threadLen = 1000;
    if (threadPath) { try { threadLen = threadPath.getTotalLength(); } catch (e) {} ; threadPath.style.strokeDasharray = threadLen; }

    // cache layers per scene
    const sceneData = scenes.map((s) => ({
      el: s,
      roman: s.getAttribute("data-roman"),
      layers: Array.from(s.querySelectorAll("[data-layer]")).map((l) => ({
        el: l, depth: parseFloat(l.getAttribute("data-depth")) || 0,
      })),
      svg: s.querySelector(".etch svg"),
    }));

    let raf = 0, lastChapter = null;
    const vh = () => window.innerHeight;

    const frame = () => {
      raf = 0;
      const H = vh();
      const px = ENGINE.parallax;

      // global descent progress
      const max = document.documentElement.scrollHeight - H;
      const prog = max > 0 ? clamp01(window.scrollY / max) : 0;
      if (meter) meter.style.width = (prog * 100).toFixed(2) + "%";
      if (marble) marble.style.opacity = Math.max(0, 0.62 * (1 - prog / 0.5)).toFixed(3);
      if (stars) stars.style.opacity = clamp01((prog - 0.45) / 0.4).toFixed(3) * 0.9;
      if (threadPath) threadPath.style.strokeDashoffset = (threadLen * (1 - clamp01(prog / 0.96))).toFixed(1);

      let best = null, bestAbs = 1e9;
      for (const sd of sceneData) {
        const r = sd.el.getBoundingClientRect();
        const center = r.top + r.height / 2;
        const p = (center - H / 2) / H;       // 0 = centered

        for (const ly of sd.layers) {
          ly.el.style.transform = "translate3d(0," + (p * ly.depth * px).toFixed(1) + "px,0)";
        }
        if (sd.svg) sd.svg.style.setProperty("--draw", clamp01(1 - p * 1.3).toFixed(3));

        const a = Math.abs(p);
        if (a < bestAbs && sd.roman && sd.roman !== "0") { bestAbs = a; best = sd.roman; }
      }
      if (best && best !== lastChapter && bestAbs < 0.55) { lastChapter = best; onChapter(best); }
    };

    const onScroll = () => { if (!raf) raf = requestAnimationFrame(frame); };
    frame();
    window.addEventListener("scroll", onScroll, { passive: true });
    window.addEventListener("resize", onScroll);
    return () => {
      window.removeEventListener("scroll", onScroll);
      window.removeEventListener("resize", onScroll);
      if (raf) cancelAnimationFrame(raf);
    };
  }, [onChapter]);
}

// vertical meander for the spine thread (fixed, full-height)
const THREAD_D =
  "M60 0 C 30 70, 90 130, 60 200 S 20 320, 60 420 S 96 540, 56 640 " +
  "S 24 760, 62 860 S 92 960, 58 1060 S 28 1180, 60 1280 S 90 1400, 58 1500 " +
  "S 26 1620, 60 1720 S 92 1840, 58 1940 L60 2000";

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "tone": "cinematic",
  "gold": 1,
  "parallax": 1,
  "dust": 1,
  "marble": true
}/*EDITMODE-END*/;

function AppParallax() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const [chapter, setChapter] = React.useState("I");
  const onChapter = React.useCallback((r) => setChapter(r), []);

  ENGINE.parallax = t.parallax;
  useParallaxEngine(onChapter);

  const rootStyle = {
    "--gold-intensity": t.gold,
    "--parallax": t.parallax,
    "--dust": t.dust,
    "--marble-on": t.marble ? 0.6 : 0,
  };

  const chapters = ["I", "II", "III", "IV"];
  const goTo = (roman) => {
    const el = document.querySelector('.scene[data-roman="' + roman + '"]');
    if (el) window.scrollTo({ top: window.scrollY + el.getBoundingClientRect().top - 0, behavior: "smooth" });
  };

  return (
    <div className={"tone-" + t.tone} style={rootStyle}>
      <window.SharedDefs />

      {/* the descending ground */}
      <div className="descent-bg" />
      {t.marble && <div className="descent-marble" />}
      <div className="descent-stars" />
      <div className="atmo" />

      {/* Ariadne's thread — the spine of the whole page */}
      <div className="thread" aria-hidden="true">
        <svg viewBox="0 0 120 2000" preserveAspectRatio="none">
          <path d={THREAD_D} />
        </svg>
      </div>

      {/* descent meter */}
      <div className="descent-meter"><i /></div>

      {/* chapter rail */}
      <nav className="chapter-rail" aria-label="Chapters"
           style={{ "--ink-rail": (chapter === "III" || chapter === "IV") ? "var(--onyx-stone)" : "var(--mb-stone)" }}>
        {chapters.map((r) => (
          <button key={r} className={r === chapter ? "active" : ""}
                  onClick={() => goTo(r)} aria-label={"Chapter " + r}>
            <span>{r}</span><span className="dot" />
          </button>
        ))}
      </nav>

      <div className="stage">
        <window.Overture />
        {window.SCENES.map((s) => <window.Scene key={s.idx} {...s} />)}
      </div>

      <window.Finale />

      <TweaksPanel title="Tweaks">
        <TweakSection label="Atmosphere" />
        <TweakRadio label="Tone" value={t.tone}
          options={["museum", "cinematic", "ethereal"]}
          onChange={(v) => setTweak("tone", v)} />
        <TweakToggle label="Marble veining" value={t.marble}
          onChange={(v) => setTweak("marble", v)} />

        <TweakSection label="Gold & depth" />
        <TweakSlider label="Gold intensity" value={t.gold} min={0.5} max={1.4} step={0.05}
          onChange={(v) => setTweak("gold", v)} />
        <TweakSlider label="Parallax depth" value={t.parallax} min={0} max={1.7} step={0.1}
          onChange={(v) => setTweak("parallax", v)} />
        <TweakSlider label="Gold dust" value={t.dust} min={0} max={2} step={0.25}
          onChange={(v) => setTweak("dust", v)} />
      </TweaksPanel>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<AppParallax />);
