'use strict';

// The Device — physical control layer (Phase 4). Renders the knobs, switches
// and the ΔKT button over the baseplate, registered to the painted control
// positions, and wires every interaction to window.DeviceControls. This layer
// sits ABOVE the baseplate; the container is pointer-events:none so only the
// controls themselves are interactive (the scope cutout below stays playable).

const { useState, useRef } = React;

// ── Registration (% of the plate box; nudge to taste) ─────────────────
// Eye-flank knobs (4), lower-left arp knob (1), two switches right of the
// scope, central ΔKT button below the eye. Measured from baseplate.png.
// Each knob carries its own size so it can be tuned to fully occlude the knob
// painted into baseplate.png beneath it (measured against the plate). cx/cy are
// the knob CENTER as % of the plate box; size is the diameter as % of plate
// WIDTH. Tune per-knob by eye if any painted crown still peeks out.
// Positions for the CLEAN plate (baseplate-2.png) — no painted knobs/switches
// underneath, so these just place the controls at their glyph-label spots.
const POS = {
  waveform:  { cx: 13, cy: 15, size: 16.5 }, // Knob 1 — top-left  [ΤΛΥΦ-97]
  resonance: { cx: 87, cy: 15, size: 16.5 }, // Knob 2 — top-right [VV ΞΦ]
  scale:     { cx: 13, cy: 35, size: 16.5 }, // Knob 3 — mid-left  [ΤΛΥΦ-97]
  octave:    { cx: 87, cy: 35, size: 16.5 }, // Knob 4 — mid-right [VV ΞΦ]
  arp:       { cx: 9.7, cy: 76, size: 8.5 }, // Knob 5 — centered in the flat panel left of the scope (panel ≈ x5.2–14.2%, center 9.7%)
  record:    { cx: 87, cy: 67 },  // Switch B — upper-right label group
  wrong:     { cx: 87, cy: 82 },  // Switch A — lower-right label group
  clear:     { cx: 49.8, cy: 49.5 },// ΔKT button — over the painted boss below the eye
};
const KNOB_SIZE_PCT = 16.5; // fallback knob diameter, % of plate width
const SW_SIZE_PCT    = 11;
const BTN_SIZE_PCT   = 8.5;
const KNOB_ARC_DEG   = 135;  // value 0..1 → −135°..+135°
const KNOB_TRAVEL_PX = 180;  // vertical drag for full 0..1 travel

const A = '/the-device/assets';

// ── Knob (vertical-drag, NOT rotary) ──────────────────────────────────
function Knob({ pos, norm, onChange }) {
  const drag = useRef(null);
  const angle = (norm - 0.5) * 2 * KNOB_ARC_DEG;
  const size = pos.size || KNOB_SIZE_PCT;

  const onDown = (e) => {
    e.preventDefault(); e.stopPropagation();
    try { e.currentTarget.setPointerCapture(e.pointerId); } catch (_) {}
    drag.current = { y: e.clientY, norm };
  };
  const onMove = (e) => {
    if (!drag.current) return;
    const dy = drag.current.y - e.clientY;          // up = increase
    const v = drag.current.norm + dy / KNOB_TRAVEL_PX;
    onChange(v < 0 ? 0 : v > 1 ? 1 : v);
  };
  const onUp = (e) => {
    drag.current = null;
    try { e.currentTarget.releasePointerCapture(e.pointerId); } catch (_) {}
  };

  return (
    <div
      onPointerDown={onDown} onPointerMove={onMove} onPointerUp={onUp} onPointerCancel={onUp}
      style={{
        position: 'absolute', left: `${pos.cx}%`, top: `${pos.cy}%`,
        width: `${size}%`, aspectRatio: '1 / 1',
        transform: 'translate(-50%, -50%)',
        pointerEvents: 'auto', cursor: 'ns-resize', touchAction: 'none',
      }}
    >
      <div style={{ position: 'absolute', inset: 0, transform: `rotate(${angle}deg)` }}>
        <img src={`${A}/knob.png`} alt="" draggable={false}
             style={{ width: '100%', height: '100%', display: 'block', pointerEvents: 'none' }} />
        {/* glowing indicator tick over the painted line (points up at 0°) */}
        <div style={{
          position: 'absolute', left: '50%', top: '9%', width: '3.5%', height: '34%',
          transform: 'translateX(-50%)', borderRadius: '2px', pointerEvents: 'none',
          background: 'linear-gradient(rgba(70,255,192,0.95), rgba(70,255,192,0))',
          boxShadow: '0 0 7px rgba(70,255,192,0.85)',
        }} />
      </div>
    </div>
  );
}

// The sliced switch sprites have the plate at DIFFERENT horizontal positions
// (up content centered at ~39%, down at ~61% of the sprite) — so naively
// swapping them jumps the whole plate sideways. These per-state corrections
// (% of the sprite/element width) re-center the plate so ONLY the lever flips.
const SWITCH_ALIGN = { 'a-up': 10.6, 'a-down': -11.1, 'b-up': 10.6, 'b-down': -10.7 };

// ── Toggle / momentary switch ─────────────────────────────────────────
function Switch({ pos, asset, on, momentary, onDown, onUp, onToggle }) {
  const state = on ? 'down' : 'up'; // 'down' = engaged
  const corr = SWITCH_ALIGN[`${asset}-${state}`] || 0;
  const handleDown = (e) => {
    e.preventDefault(); e.stopPropagation();
    try { e.currentTarget.setPointerCapture(e.pointerId); } catch (_) {}
    if (momentary) onDown && onDown();
    else onToggle && onToggle();
  };
  const handleUp = (e) => {
    try { e.currentTarget.releasePointerCapture(e.pointerId); } catch (_) {}
    if (momentary) onUp && onUp();
  };
  return (
    <img
      src={`${A}/switch-${asset}-${state}.png`} alt="" draggable={false}
      onPointerDown={handleDown} onPointerUp={handleUp} onPointerCancel={handleUp}
      style={{
        position: 'absolute', left: `${pos.cx}%`, top: `${pos.cy}%`,
        width: `${SW_SIZE_PCT}%`, transform: `translate(-50%, -50%) translateX(${corr}%)`,
        pointerEvents: 'auto', cursor: 'pointer', touchAction: 'none', display: 'block',
      }}
    />
  );
}

// ── ΔKT clear button (momentary) ──────────────────────────────────────
function ClearButton({ pos, down, onDown, onUp }) {
  return (
    <img
      src={`${A}/${down ? 'button-2' : 'button-1'}.png`} alt="" draggable={false}
      onPointerDown={(e) => { e.preventDefault(); e.stopPropagation(); try { e.currentTarget.setPointerCapture(e.pointerId); } catch (_) {} onDown(); }}
      onPointerUp={(e) => { try { e.currentTarget.releasePointerCapture(e.pointerId); } catch (_) {} onUp(); }}
      onPointerCancel={onUp}
      style={{
        position: 'absolute', left: `${pos.cx}%`, top: `${pos.cy}%`,
        width: `${BTN_SIZE_PCT}%`, transform: 'translate(-50%, -50%)',
        pointerEvents: 'auto', cursor: 'pointer', touchAction: 'none', display: 'block',
        filter: down ? 'drop-shadow(0 0 10px rgba(70,255,192,0.8))' : 'none',
      }}
    />
  );
}

function ControlsLayer() {
  const C = window.DeviceControls;
  // continuous 0..1
  const [waveform, setWaveform]   = useState(0.25);
  const [resonance, setResonance] = useState(0.18);
  // stepped indices
  const [scaleIdx, setScaleIdx]   = useState(0);
  const [octaveIdx, setOctaveIdx] = useState(1);
  const [arpIdx, setArpIdx]       = useState(0);
  // switches / button
  const [recordOn, setRecordOn]   = useState(false);
  const [wrongOn, setWrongOn]     = useState(false);
  const [btnDown, setBtnDown]     = useState(false);

  const SCALE_N = C.SCALE_COUNT, OCT_N = C.OCTAVE_COUNT, ARP_N = C.ARP_RATES;
  const stepNorm = (idx, n) => (n > 1 ? idx / (n - 1) : 0);
  const normToIdx = (v, n) => Math.round(v * (n - 1));

  return (
    <div style={{ position: 'absolute', inset: 0, zIndex: 3, pointerEvents: 'none' }}>
      <Knob pos={POS.waveform} norm={waveform}
            onChange={(v) => { setWaveform(v); C.setWaveform(v); }} />
      <Knob pos={POS.resonance} norm={resonance}
            onChange={(v) => { setResonance(v); C.setResonance(v); }} />
      <Knob pos={POS.scale} norm={stepNorm(scaleIdx, SCALE_N)}
            onChange={(v) => { const i = normToIdx(v, SCALE_N); setScaleIdx(i); C.setScaleIndex(i); }} />
      <Knob pos={POS.octave} norm={stepNorm(octaveIdx, OCT_N)}
            onChange={(v) => { const i = normToIdx(v, OCT_N); setOctaveIdx(i); C.setOctaveIndex(i); }} />
      <Knob pos={POS.arp} norm={stepNorm(arpIdx, ARP_N)}
            onChange={(v) => { const i = normToIdx(v, ARP_N); setArpIdx(i); C.setArpRate(i); }} />

      {/* Switch B — RECORD (toggle; kicks back down on 'full') */}
      <Switch pos={POS.record} asset="b" on={recordOn}
              onToggle={() => {
                if (!recordOn) { const r = C.setRecord(true); if (r.ok) setRecordOn(true); }
                else { C.setRecord(false); setRecordOn(false); }
              }} />

      {/* THE WRONG SWITCH (toggle: flip on, stays on until flipped off).
          Uses the 'b' sprites — the 'a' slice is a bad pair (both states read
          as bat-down, ~22% vs 25%), while 'b' is a clean up/down lever. */}
      <Switch pos={POS.wrong} asset="b" on={wrongOn}
              onToggle={() => { const next = !wrongOn; setWrongOn(next); C.setWrong(next); }} />

      <ClearButton pos={POS.clear} down={btnDown}
                   onDown={() => { setBtnDown(true); C.clearLoop(); }}
                   onUp={() => setBtnDown(false)} />
    </div>
  );
}

window.ControlsLayer = ControlsLayer;
