// app/static/js/services/unifiedMidiRouter.js
// Single MIDI entry point for Web MIDI keyboard + timeline playback.
// bridge connected -> NativeBridgeService.dispatchMidiEvent (Rust SHM -> C++ bridge)
// bridge down      -> onFallback callback (app.jsx wires SonicSF/Carla path).
(function () {
  var MELODIC_CHANNELS = [0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15]; // skip 9 (percussion)

  var router = {
    bridgeConnected: false,
    onFallback: null,          // function(cmd, channel, pitch, velocity) — set by app.jsx
    forceWasm: false,          // debug: D10 "Ép dùng WASM"
    _channels: {},             // trackId -> { ch, percussion }
    _nextMelodicIdx: 0,

    setBridgeConnected: function (flag) {
      this.bridgeConnected = !!flag;
    },

    setForceWasm: function (flag) { this.forceWasm = !!flag; },

    isBridgeActive: function () {
      return this.bridgeConnected && !this.forceWasm && !!window.NativeBridgeService;
    },

    /** trackId -> MIDI channel; percussion (bank 128) -> ch 9. */
    allocateChannel: function (trackId, isPercussion) {
      if (this._channels[trackId]) return this._channels[trackId].ch;
      var ch;
      if (isPercussion) {
        ch = 9;
      } else {
        ch = MELODIC_CHANNELS[this._nextMelodicIdx % MELODIC_CHANNELS.length];
        this._nextMelodicIdx++;
      }
      this._channels[trackId] = { ch: ch, percussion: !!isPercussion };
      return ch;
    },

    resetChannels: function () {
      this._channels = {};
      this._nextMelodicIdx = 0;
    },

    /**
     * cmd: 'NOTE_ON' | 'NOTE_OFF' | 'CC' | 'PROGRAM' | 'PITCH_BEND'
     * channel: MIDI channel (or trackId -> allocateChannel first)
     * velocity: 0..1 (normalized); sampleOffset: samples within current block.
     * data2/data3 (A12): CC value / program / PB LSB|MSB — passed to bridge only.
     */
    pushEvent: function (opts) {
      var cmd = opts.cmd, ch = opts.channel, pitch = opts.pitch;
      var vel = (opts.velocity === undefined ? 1.0 : opts.velocity);
      var sampleOffset = opts.sampleOffset || 0;
      if (typeof ch === 'string') ch = this.allocateChannel(ch, opts.percussion);
      if (ch === undefined || ch === null) ch = 0;
      if (this.isBridgeActive()) {
        window.NativeBridgeService.dispatchMidiEvent(cmd, ch, pitch, vel, sampleOffset, opts.data2, opts.data3);
        return;
      }
      if (this.onFallback) this.onFallback(cmd, ch, pitch, vel);
    },

    /** Stop/panic -> bridge transport panic (flush all notes) + fallback local stopAll. */
    panic: function () {
      if (this.isBridgeActive() && window.NativeBridgeService.transport) {
        window.NativeBridgeService.transport('panic');
      }
      if (this.onFallback) this.onFallback('PANIC', 0, 0, 0);
    }
  };

  window.SonicMidiRouter = router;
})();
