/* anim-primitives.jsx — shared animation utilities */
const { useState, useEffect, useRef, useCallback, useMemo } = React;

/* ============ Hook: in-view ============ */
function useInView(threshold = 0.3) {
  const ref = useRef(null);
  const [inView, setInView] = useState(false);
  useEffect(() => {
    if (!ref.current) return;
    const obs = new IntersectionObserver(
      ([e]) => setInView(e.isIntersecting),
      { threshold }
    );
    obs.observe(ref.current);
    return () => obs.disconnect();
  }, [threshold]);
  return [ref, inView];
}

/* ============ Hook: stepper ============
 * Drives a discrete step counter 0..steps-1.
 * Auto-plays when inView and playing. Pauses otherwise.
 */
function useStepper({ steps, intervalMs = 1400, autoplay = true, loop = true }) {
  const [step, setStep] = useState(0);
  const [playing, setPlaying] = useState(autoplay);
  const [containerRef, inView] = useInView(0.4);
  const tick = useRef(null);

  useEffect(() => {
    clearInterval(tick.current);
    if (playing && inView) {
      tick.current = setInterval(() => {
        setStep(s => {
          if (s + 1 >= steps) return loop ? 0 : s;
          return s + 1;
        });
      }, intervalMs);
    }
    return () => clearInterval(tick.current);
  }, [playing, inView, steps, intervalMs, loop]);

  const next = () => setStep(s => Math.min(s + 1, steps - 1));
  const prev = () => setStep(s => Math.max(s - 1, 0));
  const reset = () => setStep(0);
  const toggle = () => setPlaying(p => !p);

  return { step, setStep, playing, toggle, next, prev, reset, containerRef, inView };
}

/* ============ Animation Frame ============ */
function AnimFrame({ label, children, controls }) {
  return (
    <div className="frame">
      <div className="frame-head">
        <div className="dots"><span></span><span></span><span></span></div>
        <div className="label">{label}</div>
      </div>
      <div className="frame-body">{children}</div>
      {controls && <div className="frame-controls">{controls}</div>}
    </div>
  );
}

function PlayBtn({ playing, onToggle }) {
  return (
    <button className="btn primary" onClick={onToggle}>
      {playing ? (
        <><span className="ic">❚❚</span> Pause</>
      ) : (
        <><span className="ic">▶</span> Play</>
      )}
    </button>
  );
}

function StepBtns({ onPrev, onNext, onReset }) {
  return (
    <>
      <button className="btn" onClick={onPrev}>← Prev</button>
      <button className="btn" onClick={onNext}>Next →</button>
      <button className="btn" onClick={onReset}>Reset</button>
    </>
  );
}

/* ============ Helpers ============ */
function clamp(v, lo, hi) { return Math.max(lo, Math.min(hi, v)); }
function lerp(a, b, t) { return a + (b - a) * t; }
function easeInOut(t) { return t < 0.5 ? 2*t*t : 1 - Math.pow(-2*t + 2, 2)/2; }

/* Smooth interp between integer steps for animations */
function useSmoothStep(step, durationMs = 400) {
  const [smooth, setSmooth] = useState(step);
  const fromRef = useRef(step);
  const toRef = useRef(step);
  const startRef = useRef(performance.now());
  const rafRef = useRef(null);

  useEffect(() => {
    fromRef.current = smooth;
    toRef.current = step;
    startRef.current = performance.now();
    cancelAnimationFrame(rafRef.current);
    const tick = () => {
      const t = clamp((performance.now() - startRef.current) / durationMs, 0, 1);
      const e = easeInOut(t);
      const v = lerp(fromRef.current, toRef.current, e);
      setSmooth(v);
      if (t < 1) rafRef.current = requestAnimationFrame(tick);
    };
    rafRef.current = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(rafRef.current);
    // eslint-disable-next-line
  }, [step, durationMs]);

  return smooth;
}

Object.assign(window, {
  useInView, useStepper, useSmoothStep,
  AnimFrame, PlayBtn, StepBtns,
  clamp, lerp, easeInOut,
});
