// v5 ASCII FIELD (React mount)
// A thin wrapper around the engine in src/zn-ascii-engine.js, which must be
// loaded before this file. The engine owns the simulation, the paint step, the
// guards and the lifecycle; this component owns the DOM node and forwards the
// word. Same props as before (word, cycleMs, className) plus the engine's
// options passed straight through: words, anchor, avoid, host, opacity, ring,
// minWidth, exclude, name, still.
//
// Conventions: window globals, no ES imports (Babel compiles this in-browser).

const V5_ASCII_REDUCED = !!(
window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches);

// Below this width the hero lede is no longer capped at 70% (see
// .v5-hero-lede in v5-styles.css), the headline runs the full measure and there
// is no gutter left for the field, so it is disabled outright rather than drawn
// behind the type. Phones and small tablets get nothing, which is also the
// cheapest option for them. The engine applies the same floor on its own.
const V5_ASCII_MIN_WIDTH = 1024;

const V5AsciiField = ({
  word = "",
  words,
  cycleMs = 3600,
  className = "",
  anchor,
  avoid,
  host,
  opacity,
  ring,
  minWidth = V5_ASCII_MIN_WIDTH,
  exclude,
  name,
  still }) =>
{
  const wrapRef = useRef(null);
  const canvasRef = useRef(null);
  const fieldRef = useRef(null);
  // The option objects are captured once at mount; the engine re-evaluates any
  // functions inside them at every resize, so pass functions for anything that
  // has to follow the layout.
  const optsRef = useRef({ words, anchor, avoid, host, opacity, ring, exclude, name, still });
  optsRef.current = { words, anchor, avoid, host, opacity, ring, exclude, name, still };
  const [enabled, setEnabled] = useState(
  typeof window === "undefined" ? false : window.innerWidth >= minWidth);

  // Track the breakpoint so a desktop-to-phone resize tears the engine down.
  useEffect(() => {
    if (!window.matchMedia) return;
    const mq = window.matchMedia(`(min-width:${minWidth}px)`);
    const on = () => setEnabled(mq.matches);
    on();
    if (mq.addEventListener) mq.addEventListener("change", on);else
    mq.addListener(on);
    return () => {
      if (mq.removeEventListener) mq.removeEventListener("change", on);else
      mq.removeListener(on);
    };
  }, [minWidth]);

  // The word comes from the host component's own state (the hero's cycleWords,
  // the active lens, the active stage). Forward it without re-mounting.
  useEffect(() => {
    if (fieldRef.current && !words) fieldRef.current.setWord(word);
  }, [word]);

  useEffect(() => {
    if (!enabled) return;
    const wrap = wrapRef.current;
    if (!wrap || typeof window.znAsciiField !== "function") return;
    const o = optsRef.current;
    const field = window.znAsciiField(wrap, {
      word,
      words: o.words,
      cycleMs,
      anchor: o.anchor,
      avoid: o.avoid,
      host: o.host,
      opacity: o.opacity,
      ring: o.ring,
      minWidth,
      exclude: o.exclude,
      name: o.name,
      still: o.still });
    fieldRef.current = field;
    return () => {
      field.destroy();
      if (fieldRef.current === field) fieldRef.current = null;
    };
  }, [enabled, cycleMs, minWidth]);

  if (!enabled) return null;
  return (
    <div ref={wrapRef} className={`v5-hero-ascii zn-ascii-field ${className}`} aria-hidden="true">
      <canvas ref={canvasRef} className="v5-hero-ascii-canvas"></canvas>
    </div>);

};

Object.assign(window, { V5AsciiField, V5_ASCII_REDUCED, V5_ASCII_MIN_WIDTH });
