// Primitives: reusable small components + hooks

const { useState, useEffect, useRef, useCallback } = React;

function useRafTick(active = true) {
  const [, setT] = useState(0);
  useEffect(() => {
    if (!active) return;
    let id; const loop = () => { setT(t => t + 1); id = requestAnimationFrame(loop); };
    id = requestAnimationFrame(loop);
    return () => cancelAnimationFrame(id);
  }, [active]);
}

// Animated waveform bars (reacts to playing state)
function Waveform({ bars = 40, playing = false, color, height = 32 }) {
  const ref = useRef([]);
  const [, force] = useState(0);
  useEffect(() => {
    let id;
    const loop = () => {
      force(v => v + 1);
      id = requestAnimationFrame(loop);
    };
    id = requestAnimationFrame(loop);
    return () => cancelAnimationFrame(id);
  }, []);
  const t = Date.now() / 1000;
  const cells = [];
  for (let i = 0; i < bars; i++) {
    const base = 0.35 + 0.65 * Math.abs(Math.sin(i * 0.4 + t * (playing ? 3.5 : 0.6)) * Math.cos(i * 0.25 + t * (playing ? 1.2 : 0.3)));
    const h = Math.max(4, base * height);
    cells.push(<i key={i} style={{ height: h + 'px', background: color || 'currentColor' }} />);
  }
  return <div className="wave" style={{ height: height + 'px' }}>{cells}</div>;
}

// Knob: drag vertically to change
function Knob({ label, value, onChange, min = 0, max = 100, color }) {
  const ref = useRef(null);
  const onPointerDown = (e) => {
    e.preventDefault();
    const start = e.clientY; const startV = value;
    const move = (ev) => {
      const dy = start - ev.clientY;
      const range = max - min;
      const nv = Math.max(min, Math.min(max, startV + (dy / 120) * range));
      onChange(Math.round(nv));
    };
    const up = () => { window.removeEventListener('pointermove', move); window.removeEventListener('pointerup', up); };
    window.addEventListener('pointermove', move);
    window.addEventListener('pointerup', up);
  };
  const pct = (value - min) / (max - min);
  const angle = -135 + pct * 270;
  return (
    <div className="knob-wrap">
      <div className="knob" ref={ref} onPointerDown={onPointerDown} style={{ '--knob-color': color }}>
        <style>{`.knob::after { transform: translateX(-50%) rotate(${angle}deg); background: ${color || 'var(--c1)'}; }`}</style>
      </div>
      <div className="knob-label">{label}</div>
      <div className="knob-val" style={{ color: color || 'var(--c1)' }}>{value}</div>
    </div>
  );
}

// Workaround: inline style with a CSS var for knob angle to avoid global injection
function KnobV2({ label, value, onChange, min = 0, max = 100, color = 'var(--c1)' }) {
  const onPointerDown = (e) => {
    e.preventDefault();
    e.currentTarget.setPointerCapture?.(e.pointerId);
    const start = e.clientY; const startV = value;
    const move = (ev) => {
      const dy = start - ev.clientY;
      const range = max - min;
      const nv = Math.max(min, Math.min(max, startV + (dy / 120) * range));
      onChange(Math.round(nv));
    };
    const up = () => { window.removeEventListener('pointermove', move); window.removeEventListener('pointerup', up); };
    window.addEventListener('pointermove', move);
    window.addEventListener('pointerup', up);
  };
  const pct = (value - min) / (max - min);
  const angle = -135 + pct * 270;
  return (
    <div className="knob-wrap">
      <div
        className="knob"
        onPointerDown={onPointerDown}
        style={{
          // @ts-ignore
          '--knob-angle': angle + 'deg',
          '--knob-color': color,
        }}
      >
        <div style={{
          position: 'absolute', top: 6, left: '50%',
          width: 3, height: 20,
          background: color, borderRadius: 2,
          transform: `translateX(-50%) rotate(${angle}deg)`,
          transformOrigin: '50% 24px',
        }} />
      </div>
      <div className="knob-label">{label}</div>
      <div className="knob-val" style={{ color }}>{value}</div>
    </div>
  );
}

// Fader: drag vertically
function Fader({ label, value, onChange, color = 'var(--c3)' }) {
  const trackRef = useRef(null);
  const onPointerDown = (e) => {
    e.preventDefault();
    const track = trackRef.current;
    const rect = track.getBoundingClientRect();
    const set = (clientY) => {
      const y = clientY - rect.top;
      const pct = 1 - Math.max(0, Math.min(1, y / rect.height));
      onChange(Math.round(pct * 100));
    };
    set(e.clientY);
    const move = (ev) => set(ev.clientY);
    const up = () => { window.removeEventListener('pointermove', move); window.removeEventListener('pointerup', up); };
    window.addEventListener('pointermove', move);
    window.addEventListener('pointerup', up);
  };
  return (
    <div className="fader-col">
      <div className="fader-track" ref={trackRef} onPointerDown={onPointerDown}>
        <div className="fader-cap" style={{ top: `calc(${100 - value}% - 10px)`, background: color }} />
      </div>
      <div className="fader-label">{label}</div>
      <div className="knob-val" style={{ color }}>{value}</div>
    </div>
  );
}

// Marquee
function Marquee({ items }) {
  const doubled = [...items, ...items];
  return (
    <div className="marquee">
      <div className="marquee-track">
        {doubled.map((it, i) => (
          <span key={i}>{it}<span className="sep">✦</span></span>
        ))}
      </div>
    </div>
  );
}

// Section head
function SectionHead({ num, title, sub, id }) {
  return (
    <div className="sec-head" id={id}>
      <div>
        <div className="sec-num">{num}</div>
        <h2 dangerouslySetInnerHTML={{ __html: title }} />
      </div>
      <div className="sec-sub">{sub}</div>
    </div>
  );
}

Object.assign(window, { Waveform, Knob: KnobV2, Fader, Marquee, SectionHead, useRafTick });
