// app/static/js/services/nativeBridgeService.js
// JS client for the C++ Native Host Bridge (daw_vst_bridge.exe).
// All IPC goes through Tauri commands (Rust writes the shared memory) —
// WebView2 JS cannot map Windows shared memory directly.
(function () {
  // Must match native_bridge/include/INativeInstrument.h enum InstrumentType
  var INSTRUMENT_TYPE = { VST3: 0, VST2: 1, SF2: 2, SF3: 2, SFZ: 3 };

  var service = {
    isBridgeConnected: false,
    activeInstrumentType: 'VST3',
    _audioCb: null,
    _statusCb: null,
    _tauri: function () { return window.__TAURI__; },

    /** D10: UI subscribes to bridge connection changes. cb({connected}) or null to clear. */
    onStatusChange: function (cb) { this._statusCb = cb; },
    _notifyStatus: function (connected) {
      this.isBridgeConnected = !!connected;
      if (this._statusCb) this._statusCb({ connected: !!connected });
    },

    /** Fix C: re-establish bridge routing once audio frames arrive again
     *  after bridge-down. Same wiring as the bootstrap block in app.jsx
     *  (getAudioContext -> BridgeAudioNode.init -> AudioRoutingEngine.connect). */
    _reconnectInFlight: false,
    _reconnectIfNeeded: function () {
      if (this._reconnectInFlight) return;
      this._reconnectInFlight = true;
      var self = this;
      var finish = function () { self._reconnectInFlight = false; };
      try {
        var ctx = (typeof getAudioContext === 'function') ? getAudioContext() : (window.__sharedAudioCtx || null);
        if (ctx && window.BridgeAudioNode && window.AudioRoutingEngine) {
          window.BridgeAudioNode.init(ctx);
          window.AudioRoutingEngine.connect(window.BridgeAudioNode, null, null);
        }
        if (window.SonicMidiRouter) window.SonicMidiRouter.setBridgeConnected(true);
        this._notifyStatus(true);
        console.warn('[BridgeService] bridge recovered — routing re-established.');
      } catch (e) {
        console.warn('[BridgeService] reconnect failed:', e);
      }
      finish();
    },

    _init: function () {
      if (!this._tauri()) {
        // Dev mode (Linux / plain browser): no Tauri -> log-only, app uses SonicSF.
        console.log('[BridgeService] Dev mode: no __TAURI__, native bridge disabled.');
        return;
      }
      var self = this;
      try {
        window.__TAURI__.event.listen('bridge-audio', function (e) {
          // Bridge recovered after bridge-down (or never bootstrapped):
          // audio frames flow again -> re-establish routing (Fix C).
          if (!self.isBridgeConnected) self._reconnectIfNeeded();
          if (self._audioCb) self._audioCb(e.payload.l, e.payload.r);
        });
        window.__TAURI__.event.listen('bridge-down', function () {
          self._notifyStatus(false);
          if (window.SonicMidiRouter) window.SonicMidiRouter.setBridgeConnected(false);
          if (window.AudioRoutingEngine) window.AudioRoutingEngine.disconnect();
          console.warn('[BridgeService] bridge-down event received.');
        });
      } catch (e) {
        console.warn('[BridgeService] event listen failed:', e);
      }
      this.queryStatus();
    },

    queryStatus: async function () {
      if (!this._tauri()) return { connected: false };
      try {
        var s = await window.__TAURI__.core.invoke('bridge_status');
        this._notifyStatus(!!s.connected);
        return s;
      } catch (e) {
        this._notifyStatus(false);
        return { connected: false };
      }
    },

    /**
     * 1. LOAD NEW INSTRUMENT INTO BRIDGE (VST3 / VST2 / SF2 / SF3 / SFZ)
     * instrumentType: 'VST3' | 'VST2' | 'SF2' | 'SF3' | 'SFZ'
     * channel: MIDI channel to assign this instrument to (A10 multi-instance).
     */
    loadInstrument: async function (filePath, instrumentType, channel) {
      this.activeInstrumentType = instrumentType;
      console.log('[BridgeService] Loading ' + instrumentType + ' asset: ' + filePath + ' ch=' + channel);
      if (!this._tauri()) return false;
      try {
        await window.__TAURI__.core.invoke('load_native_instrument', {
          path: filePath,
          instrumentType: INSTRUMENT_TYPE[instrumentType] !== undefined ? INSTRUMENT_TYPE[instrumentType] : 0,
          channel: channel === undefined ? 0 : channel
        });
        return true;
      } catch (e) {
        console.warn('[BridgeService] loadInstrument failed:', e);
        return false;
      }
    },

    /**
     * 2. UNIFIED MIDI EVENT DISPATCH
     * cmd: 'NOTE_ON' | 'NOTE_OFF' | 'CC' | 'PROGRAM' | 'PITCH_BEND'
     * velocity 0..1; sampleOffset in samples within the current audio block.
     * data2/data3 (A12): CC value / program / PB LSB|MSB.
     */
    dispatchMidiEvent: function (cmd, channel, pitch, velocity, sampleOffset, data2, data3) {
      if (!this._tauri()) return false;
      var safeVelocity = Math.floor(Math.min(1.0, Math.max(0.0, velocity)) * 127);
      var byteCmd;
      switch (cmd) {
        case 'CC':        byteCmd = 0xB; break;
        case 'PROGRAM':   byteCmd = 0xC; break;
        case 'PITCH_BEND': byteCmd = 0xE; break;
        default:          byteCmd = cmd === 'NOTE_ON' ? 0x9 : 0x8; break;
      }
      window.__TAURI__.core.invoke('push_midi_event', {
        command: byteCmd,
        channel: channel,
        pitch: pitch,
        velocity: safeVelocity,
        data2: data2 === undefined ? 0 : data2,
        data3: data3 === undefined ? 0 : data3,
        sampleOffset: sampleOffset || 0
      }).catch(function (e) { console.warn('[BridgeService] push_midi_event:', e); });
      return true;
    },

    /**
     * 3. OPEN FLOATING CHILD WINDOW NATIVE GUI
     */
    openNativeGUI: async function (pluginId, channel) {
      if (!this._tauri()) return false;
      try {
        await window.__TAURI__.core.invoke('open_vst_gui', {
          pluginId: pluginId,
          channel: channel === undefined ? 0 : channel
        });
        return true;
      } catch (e) {
        console.warn('[BridgeService] open_vst_gui:', e);
        return false;
      }
    },

    /**
     * 4. TRANSPORT CONTROL ('play' | 'stop' | 'panic' | 'set_position')
     * playhead: sample position (A13) — used by 'play' and 'set_position'.
     */
    transport: function (kind, playhead) {
      if (!this._tauri()) return false;
      var args = { kind: kind };
      if (playhead !== undefined) args.playhead = playhead;
      window.__TAURI__.core.invoke('transport_control', args)
        .catch(function (e) { console.warn('[BridgeService] transport_control:', e); });
      return true;
    },

    /**
     * 5. AUDIO SINK: register consumer of PCM frames from the bridge.
     * cb(l: Float32Array, r: Float32Array)
     */
    onAudio: function (cb) {
      this._audioCb = cb;
    }
  };

  window.NativeBridgeService = service;
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', function () { service._init(); });
  } else {
    service._init();
  }
})();
