/**
 * VirtualPiano — lightweight HTML5 Canvas virtual keyboard.
 * Renders a configurable range of octaves with highlighted note support.
 * Highlighted keys glow amber (#F3C969) for active notes.
 */
class VirtualPiano {
  // White key MIDI offsets within an octave (C=0, D=2, E=4, F=5, G=7, A=9, B=11)
  static WHITE = [0, 2, 4, 5, 7, 9, 11];
  // Black key MIDI offsets (C#=1, D#=3, F#=6, G#=8, A#=10)
  static BLACK = [1, 3, 6, 8, 10];

  constructor(canvas, opts = {}) {
    this.canvas = canvas;
    this.ctx = canvas.getContext('2d');
    this.active = new Set();
    // Portrait default: 4 octaves from C2 (MIDI 36) — centres middle C at position 14/28
    this.midiStart = opts.midiStart !== undefined ? opts.midiStart : 36;
    this.octaves   = opts.octaves  !== undefined ? opts.octaves  : 4;
    this._resize();
    window.addEventListener('resize', () => this._resize());
  }

  get whiteCount() { return this.octaves * 7; }
  get midiEnd()    { return this.midiStart + this.octaves * 12 - 1; }

  _midiToDisplay(midi) {
    const offset = midi - this.midiStart;
    if (offset < 0 || offset >= this.octaves * 12) return null;
    return { octave: Math.floor(offset / 12), semitone: offset % 12 };
  }

  _resize() {
    const dpr = window.devicePixelRatio || 1;
    const w = this.canvas.parentElement?.clientWidth || 320;
    const h = this.canvas.parentElement?.clientHeight || 90;
    this.canvas.width  = w * dpr;
    this.canvas.height = h * dpr;
    this.canvas.style.width  = w + 'px';
    this.canvas.style.height = h + 'px';
    this.ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
    this.W = w;
    this.H = h;
    this._draw();
  }

  _draw() {
    const { ctx, W, H, active } = this;
    const ww = W / this.whiteCount;
    const bw = ww * 0.58;
    const bh = H * 0.62;

    ctx.clearRect(0, 0, W, H);

    // White keys
    for (let o = 0; o < this.octaves; o++) {
      VirtualPiano.WHITE.forEach((semi, idx) => {
        const midi = this.midiStart + o * 12 + semi;
        const x    = (o * 7 + idx) * ww;
        const isOn = active.has(midi);
        ctx.fillStyle   = isOn ? '#F3C969' : '#FBF8F1';
        ctx.strokeStyle = '#D9D2C0';
        ctx.lineWidth   = 1;
        ctx.fillRect(x, 0, ww, H);
        ctx.strokeRect(x, 0, ww, H);
      });
    }

    // Middle C marker (MIDI 60 = C4)
    const middleC = 60;
    if (middleC >= this.midiStart && middleC <= this.midiEnd) {
      const mcOffset = middleC - this.midiStart;
      const mcOctave = Math.floor(mcOffset / 12);
      const mcIdx    = VirtualPiano.WHITE.indexOf(0); // C is index 0 in WHITE
      const mcX      = (mcOctave * 7 + mcIdx) * ww + ww / 2;
      const r = Math.max(2.5, ww * 0.1);
      ctx.beginPath();
      ctx.arc(mcX, H - r - 3, r, 0, Math.PI * 2);
      ctx.fillStyle = '#A89A78';
      ctx.fill();
    }

    // Black keys (drawn on top)
    for (let o = 0; o < this.octaves; o++) {
      VirtualPiano.BLACK.forEach((semi) => {
        const whitesBefore = VirtualPiano.WHITE.filter(w => w < semi).length;
        const midi = this.midiStart + o * 12 + semi;
        const x    = (o * 7 + whitesBefore) * ww - bw / 2;
        const isOn = active.has(midi);
        ctx.beginPath();
        ctx.roundRect(x, 0, bw, bh, [0, 0, 3, 3]);
        ctx.fillStyle = isOn ? '#F3C969' : '#101D31';
        ctx.fill();
      });
    }
  }

  /** Change octave range and re-render. midiStart defaults to C2 (MIDI 36) for wide ranges. */
  setOctaves(count, midiStart) {
    this.octaves   = count;
    if (midiStart !== undefined) this.midiStart = midiStart;
    this.active.clear();
    this._resize();
  }

  setKey(midi, on) {
    if (on) this.active.add(midi);
    else    this.active.delete(midi);
    this._draw();
  }

  reset() {
    this.active.clear();
    this._draw();
  }

  resize() {
    this._resize();
  }
}
