import {
  hostCore,
  hostState
} from "./chunk-KL66PICH.js";

// src/plugins/vchan.ts
var vchanWindow = window;
var vchanSessionId = null;
var vchanRigId = null;
var vchanChannels = [];
var vchanActiveId = null;
var schedulerReleaseState = null;
var schedulerReleasePollTimer = null;
function vchanFmtFreq(hz) {
  if (!Number.isFinite(hz) || hz <= 0) return "--";
  if (hz >= 1e9) return (hz / 1e9).toFixed(4).replace(/\.?0+$/, "") + " GHz";
  if (hz >= 1e6) return (hz / 1e6).toFixed(4).replace(/\.?0+$/, "") + " MHz";
  if (hz >= 1e3) return (hz / 1e3).toFixed(1).replace(/\.?0+$/, "") + " kHz";
  return `${String(hz)} Hz`;
}
function schedulerReleaseSummaryText(state) {
  if (!state) return "Scheduler is controlling the rig.";
  const connected = Number(state.connected_sessions) || 0;
  const released = Number(state.released_sessions) || 0;
  if (connected === 0) return "Scheduler can control the rig.";
  if (state.all_released) {
    return connected === 1 ? "Scheduler is controlling the rig." : `Scheduler is controlling the rig for all ${connected} users.`;
  }
  if (!state.current_session_released) {
    const othersReleased = Math.max(released, 0);
    return othersReleased > 0 ? `You are holding control. ${othersReleased} other user${othersReleased === 1 ? "" : "s"} already released it.` : "You are holding control. Release it to return control to the scheduler.";
  }
  const blocking = Math.max(connected - released, 0);
  return blocking > 0 ? `Scheduler is waiting for ${blocking} user${blocking === 1 ? "" : "s"} to stop manual tuning.` : "Scheduler can control the rig.";
}
function vchanRenderSchedulerRelease() {
  const btn = document.getElementById("scheduler-release-btn");
  const status = document.getElementById("scheduler-release-status");
  if (!btn || !status) return;
  const currentReleased = !!(schedulerReleaseState && schedulerReleaseState.current_session_released);
  btn.disabled = !vchanSessionId || currentReleased;
  btn.classList.toggle("active", !currentReleased);
  btn.textContent = "Release to Scheduler";
  status.textContent = schedulerReleaseSummaryText(schedulerReleaseState);
}
async function vchanPollSchedulerRelease() {
  if (!vchanSessionId) {
    schedulerReleaseState = null;
    vchanRenderSchedulerRelease();
    return;
  }
  try {
    const resp = await fetch(`/scheduler-control?session_id=${encodeURIComponent(vchanSessionId)}`);
    if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
    schedulerReleaseState = await resp.json();
    vchanRenderSchedulerRelease();
  } catch (e) {
    console.error("scheduler release status failed", e);
  }
}
function vchanStartSchedulerReleasePolling() {
  if (schedulerReleasePollTimer) {
    clearInterval(schedulerReleasePollTimer);
  }
  schedulerReleasePollTimer = setInterval(() => {
    void vchanPollSchedulerRelease();
  }, 1e4);
}
async function vchanToggleSchedulerRelease() {
  if (!vchanSessionId) return;
  const rigId = vchanRigId || hostState.lastActiveRigId || null;
  try {
    const resp = await fetch("/scheduler-control", {
      method: "PUT",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ session_id: vchanSessionId, released: true, remote: rigId })
    });
    if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
    schedulerReleaseState = await resp.json();
    vchanRenderSchedulerRelease();
  } catch (e) {
    console.error("scheduler release toggle failed", e);
  }
}
async function vchanTakeSchedulerControl() {
  if (!vchanSessionId) return;
  if (schedulerReleaseState && !schedulerReleaseState.current_session_released) return;
  try {
    const resp = await fetch("/scheduler-control", {
      method: "PUT",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ session_id: vchanSessionId, released: false })
    });
    if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
    schedulerReleaseState = await resp.json();
    vchanRenderSchedulerRelease();
  } catch (e) {
    console.error("scheduler control takeover failed", e);
  }
}
function vchanHandleSession(data) {
  try {
    const d = JSON.parse(data);
    vchanSessionId = d.session_id || null;
    void vchanPollSchedulerRelease();
  } catch (e) {
    console.warn("vchan: bad session event", e);
  }
}
function vchanHandleChannels(data) {
  try {
    const d = JSON.parse(data);
    vchanRigId = d.remote || null;
    vchanChannels = d.channels || [];
    const ids = new Set(vchanChannels.map((c) => c.id));
    const primaryChannel = vchanChannels[0];
    if (!vchanActiveId && primaryChannel && vchanSessionId) {
      void vchanAutoJoinPrimary(primaryChannel.id);
    } else if (vchanActiveId && !ids.has(vchanActiveId)) {
      vchanActiveId = vchanChannels[0]?.id ?? null;
      vchanReconnectAudio();
    }
    vchanRender();
    vchanRenderSchedulerRelease();
    vchanWindow.renderRdsOverlays?.();
  } catch (e) {
    console.warn("vchan: bad channels event", e);
  }
}
function vchanRender() {
  const picker = document.getElementById("vchan-picker");
  if (!picker) return;
  picker.innerHTML = "";
  vchanChannels.forEach((ch) => {
    const btn = document.createElement("button");
    btn.type = "button";
    btn.title = `Ch ${ch.index}: ${vchanFmtFreq(ch.freq_hz)} ${ch.mode} · ${ch.subscribers} subscriber${ch.subscribers !== 1 ? "s" : ""}`;
    if (ch.id === vchanActiveId) btn.classList.add("active");
    const label = document.createElement("span");
    label.className = "vchan-label";
    label.textContent = `${ch.index}: ${vchanFmtFreq(ch.freq_hz)} ${ch.mode}`;
    btn.appendChild(label);
    if (!ch.permanent) {
      const del = document.createElement("span");
      del.className = "vchan-del";
      del.textContent = "×";
      del.title = "Delete channel";
      del.addEventListener("click", (e) => {
        e.stopPropagation();
        void vchanDelete(ch.id);
      });
      btn.appendChild(del);
    }
    btn.addEventListener("click", () => {
      if (ch.id !== vchanActiveId) void vchanSubscribe(ch.id);
    });
    picker.appendChild(btn);
  });
  const addBtn = document.createElement("button");
  addBtn.type = "button";
  addBtn.className = "vchan-add";
  addBtn.textContent = "+";
  addBtn.title = "Allocate new virtual channel at current frequency";
  addBtn.addEventListener("click", () => {
    void vchanAllocate();
  });
  picker.appendChild(addBtn);
  vchanSyncAccentUI();
  hostCore.updateDocumentTitle(hostCore.activeChannelRds());
  vchanRenderSchedulerRelease();
}
async function vchanAllocate() {
  if (!vchanSessionId || !vchanRigId) return;
  const freqHz = typeof hostState.lastFreqHz === "number" && hostState.lastFreqHz > 0 ? hostState.lastFreqHz : 0;
  const modeEl = document.getElementById("mode");
  const mode = modeEl ? modeEl.value || "USB" : "USB";
  try {
    const resp = await fetch(`/channels/${encodeURIComponent(vchanRigId)}`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ session_id: vchanSessionId, freq_hz: freqHz, mode })
    });
    if (!resp.ok) {
      const msg = await resp.text().catch(() => String(resp.status));
      console.warn("vchan: allocate failed —", msg);
      return;
    }
    const ch = await resp.json();
    vchanActiveId = ch.id;
    vchanRender();
    vchanReconnectAudio();
  } catch (e) {
    console.error("vchan: allocate error", e);
  }
}
async function vchanDelete(channelId) {
  if (!vchanRigId) return;
  try {
    const resp = await fetch(
      `/channels/${encodeURIComponent(vchanRigId)}/${encodeURIComponent(channelId)}`,
      { method: "DELETE" }
    );
    if (!resp.ok) {
      console.warn("vchan: delete failed", resp.status);
    }
  } catch (e) {
    console.error("vchan: delete error", e);
  }
}
async function vchanAutoJoinPrimary(channelId) {
  if (!vchanSessionId || !vchanRigId) return;
  try {
    const resp = await fetch(
      `/channels/${encodeURIComponent(vchanRigId)}/${encodeURIComponent(channelId)}/subscribe`,
      {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ session_id: vchanSessionId })
      }
    );
    if (!resp.ok) {
      console.warn("vchan: auto-join primary failed", resp.status);
      return;
    }
    vchanActiveId = channelId;
    vchanRender();
  } catch (e) {
    console.error("vchan: auto-join error", e);
  }
}
async function vchanSubscribe(channelId) {
  if (!vchanSessionId || !vchanRigId) return;
  try {
    await vchanTakeSchedulerControl();
    const resp = await fetch(
      `/channels/${encodeURIComponent(vchanRigId)}/${encodeURIComponent(channelId)}/subscribe`,
      {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ session_id: vchanSessionId })
      }
    );
    if (!resp.ok) {
      console.warn("vchan: subscribe failed", resp.status);
      return;
    }
    vchanActiveId = channelId;
    vchanRender();
    vchanSyncModeDisplay();
    vchanReconnectAudio();
  } catch (e) {
    console.error("vchan: subscribe error", e);
  }
}
function vchanReconnectAudio() {
  const ch = vchanIsOnVirtual() ? vchanActiveChannel() : null;
  hostState.audioChannelOverride = ch?.id ?? null;
  if (!hostState.rxActive) return;
  hostCore.stopRxAudio();
  setTimeout(() => {
    hostCore.startRxAudio();
  }, 300);
}
function vchanApplyCapabilities(caps) {
  const picker = document.getElementById("vchan-picker");
  if (!picker) return;
  picker.style.display = caps && caps.filter_controls ? "" : "none";
  vchanRenderSchedulerRelease();
}
function vchanIsOnVirtual() {
  if (!vchanActiveId || vchanChannels.length === 0) return false;
  return vchanActiveId !== vchanChannels[0]?.id;
}
function vchanActiveChannel() {
  return vchanChannels.find((c) => c.id === vchanActiveId) || null;
}
function vchanUpdateFreqDisplay() {
  const ch = vchanActiveChannel();
  if (!ch) return;
  const el = document.getElementById("freq");
  if (!el) return;
  el.value = hostCore.formatFreqForStep(ch.freq_hz, hostState.jogUnit);
}
function vchanSyncModeDisplay() {
  const modeEl = document.getElementById("mode");
  if (!modeEl) return;
  if (vchanIsOnVirtual()) {
    const ch = vchanActiveChannel();
    if (ch && ch.mode) {
      modeEl.value = ch.mode.toUpperCase();
      hostCore.syncModePicker();
    }
  }
  const modeUpper = (modeEl.value || "").toUpperCase();
  if (typeof hostState.lastModeName === "string") {
    if (modeUpper === "WFM" && hostState.lastModeName !== "WFM") {
      hostCore.setJogDivisor(10);
      hostCore.resetRdsDisplay();
    } else if (modeUpper !== "WFM" && hostState.lastModeName === "WFM") {
      hostCore.resetRdsDisplay();
    }
    hostState.lastModeName = modeUpper;
  }
  hostCore.updateWfmControls();
  hostCore.updateSdrSquelchControlVisibility();
  if (vchanWindow.refreshRdsUi) {
    vchanWindow.refreshRdsUi();
  } else {
    hostCore.positionRdsPsOverlay();
  }
}
function vchanSyncBwDisplay() {
  if (!vchanIsOnVirtual()) return;
  const ch = vchanActiveChannel();
  if (!ch) return;
  const bwEl = document.getElementById("spectrum-bw-input");
  if (!bwEl) return;
  let bwHz = ch.bandwidth_hz || 0;
  if (bwHz === 0) {
    bwHz = hostCore.mwDefaultsForMode(ch.mode)[0] || 0;
  }
  if (bwHz > 0) {
    bwEl.value = (bwHz / 1e3).toFixed(3).replace(/\.?0+$/, "");
    hostState.currentBandwidthHz = bwHz;
  }
}
function vchanSyncAccentUI() {
  const onVirtual = vchanIsOnVirtual();
  const freqEl = document.getElementById("freq");
  const bwEl = document.getElementById("spectrum-bw-input");
  if (freqEl) freqEl.classList.toggle("vchan-ch-active", onVirtual);
  if (bwEl) bwEl.classList.toggle("vchan-ch-active", onVirtual);
  if (onVirtual) {
    vchanUpdateFreqDisplay();
    vchanSyncModeDisplay();
    vchanSyncBwDisplay();
  } else {
    hostCore.refreshFreqDisplay();
  }
  hostCore.updateDocumentTitle(hostCore.activeChannelRds());
}
function vchanSetChannelFreq(freqHz) {
  if (!vchanRigId || !vchanActiveId) return;
  if (hostState.lastSpectrumData && hostState.lastSpectrumData.sample_rate > 0) {
    const halfSpan = hostState.lastSpectrumData.sample_rate / 2;
    const center = hostState.lastSpectrumData.center_hz;
    if (Math.abs(freqHz - center) > halfSpan) {
      hostCore.showHint(
        `Out of SDR bandwidth (center ${(center / 1e6).toFixed(3)} MHz ±${(halfSpan / 1e3).toFixed(0)} kHz)`,
        3e3
      );
      return;
    }
  }
  void vchanTakeSchedulerControl();
  void fetch(
    `/channels/${encodeURIComponent(vchanRigId)}/${encodeURIComponent(vchanActiveId)}/freq`,
    {
      method: "PUT",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ freq_hz: Math.round(freqHz) })
    }
  ).catch((error) => {
    console.error("vchan: set freq error", error);
  });
}
async function vchanSetChannelBandwidth(bwHz) {
  if (!vchanRigId || !vchanActiveId) return;
  try {
    await vchanTakeSchedulerControl();
    const resp = await fetch(
      `/channels/${encodeURIComponent(vchanRigId)}/${encodeURIComponent(vchanActiveId)}/bw`,
      {
        method: "PUT",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ bandwidth_hz: Math.round(bwHz) })
      }
    );
    if (!resp.ok) console.warn("vchan: set bw failed", resp.status);
  } catch (e) {
    console.error("vchan: set bw error", e);
  }
}
async function vchanSetChannelMode(mode) {
  if (!vchanRigId || !vchanActiveId) return;
  try {
    await vchanTakeSchedulerControl();
    const resp = await fetch(
      `/channels/${encodeURIComponent(vchanRigId)}/${encodeURIComponent(vchanActiveId)}/mode`,
      {
        method: "PUT",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ mode })
      }
    );
    if (!resp.ok) console.warn("vchan: set mode failed", resp.status);
  } catch (e) {
    console.error("vchan: set mode error", e);
  }
}
async function vchanInterceptMode(mode) {
  if (!vchanIsOnVirtual()) return false;
  await vchanSetChannelMode(mode);
  return true;
}
async function vchanInterceptBandwidth(bwHz) {
  if (!vchanIsOnVirtual()) return false;
  await vchanSetChannelBandwidth(bwHz);
  return true;
}
function vchanInterceptFrequency(freqHz) {
  if (!vchanIsOnVirtual()) return false;
  const targetHz = Math.round(freqHz);
  hostCore.armOptimisticFrequency(targetHz);
  hostCore.applyLocalTunedFrequency(targetHz);
  vchanSetChannelFreq(freqHz);
  return true;
}
function vchanInterceptFreqDisplay() {
  if (!vchanIsOnVirtual()) return false;
  vchanUpdateFreqDisplay();
  return true;
}
vchanWindow.trx ??= {};
vchanWindow.trx.modules ??= {};
vchanWindow.trx.modules.vchan = {
  get channels() {
    return vchanChannels;
  },
  get activeId() {
    return vchanActiveId;
  },
  activeChannel: vchanActiveChannel,
  applyCapabilities: vchanApplyCapabilities,
  handleSession: vchanHandleSession,
  handleChannels: vchanHandleChannels,
  isOnVirtual: vchanIsOnVirtual,
  interceptMode: vchanInterceptMode,
  interceptBandwidth: vchanInterceptBandwidth,
  interceptFrequency: vchanInterceptFrequency,
  interceptFreqDisplay: vchanInterceptFreqDisplay,
  takeSchedulerControl: vchanTakeSchedulerControl,
  releaseToScheduler: vchanToggleSchedulerRelease
};
(function initSchedulerReleaseControl() {
  const btn = document.getElementById("scheduler-release-btn");
  if (btn) {
    btn.addEventListener("click", () => {
      void vchanToggleSchedulerRelease();
    });
  }
  vchanStartSchedulerReleasePolling();
  vchanRenderSchedulerRelease();
})();
