/**
 * @file TriggerPanel.cpp
 * @brief Trigger configuration bar — hub-side trigger semantics.
 *
 * The trigger runs in the C++ StreamHub: this panel only edits the config
 * (signal/edge/threshold/window/pre%/mode), sends setTrigger + arm/disarm/
 * rearm/trigStop commands, and reflects the hub triggerState broadcasts.
 *
 * Single-row compact layout: all controls fit on one line on a ≥1280 px window;
 * ImGui wraps naturally on narrower screens without a scrollbar.
 */

#include "TriggerPanel.h"
#include "App.h"
#include "Protocol.h"

#include "imgui.h"
#include "Icons.h"

#include <cstdio>
#include <cstring>
#include <cmath>

namespace StreamHubClient {

/* Window presets (mirrors the web UI: 100 µs .. 10 s) */
static const double kWinVals[] = {1e-4, 1e-3, 1e-2, 1e-1, 1.0, 10.0};
static const char*  kWinLabels[] = {"100 us", "1 ms", "10 ms", "100 ms", "1 s", "10 s"};
static const int    kNumWins = 6;

static const char* kEdgeLabels[] = {"Rising", "Falling", "Both"};
static const char* kEdgeWire[]   = {"rising", "falling", "both"};

/* Send the current config to the hub. */
static void sendTrigConfig(App& app) {
    auto& trig = app.trigger();
    if (trig.signalKey.empty()) { return; }
    app.ws().sendText(BuildSetTrigger(
        trig.signalKey, kEdgeWire[trig.edge], trig.threshold,
        trig.windowSec, trig.prePercent,
        trig.single ? "single" : "normal"));
}

void RenderTriggerPanel(App& app) {
    auto& trig    = app.trigger();
    auto& sources = app.sources();

    ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4.f, 4.f));

    bool cfgChanged = false;

    /* ── Signal selector ──────────────────────────────────────────────── */
    ImGui::SetNextItemWidth(150.f);
    if (ImGui::BeginCombo("##trigsig", trig.signalKey.empty()
            ? "<signal>" : trig.signalKey.c_str())) {
        for (const auto& src : sources) {
            for (const auto& sig : src.signals) {
                char key[192];
                snprintf(key, sizeof(key), "%s:%s",
                         src.id.c_str(), sig.meta.name.c_str());
                bool sel = (trig.signalKey == key);
                if (ImGui::Selectable(key, sel)) {
                    trig.signalKey = key;
                    cfgChanged = true;
                }
                if (sel) { ImGui::SetItemDefaultFocus(); }
            }
        }
        ImGui::EndCombo();
    }
    ImGui::SameLine();

    /* ── Edge ─────────────────────────────────────────────────────────── */
    ImGui::SetNextItemWidth(70.f);
    if (ImGui::Combo("##trigedge", &trig.edge, kEdgeLabels, 3)) {
        cfgChanged = true;
    }
    ImGui::SameLine();

    /* ── Threshold ───────────────────────────────────────────────────── */
    ImGui::SetNextItemWidth(65.f);
    if (ImGui::InputDouble("Thr##trig", &trig.threshold, 0.0, 0.0, "%.4g",
                           ImGuiInputTextFlags_EnterReturnsTrue)) {
        cfgChanged = true;
    }
    ImGui::SameLine();

    /* ── Window ──────────────────────────────────────────────────────── */
    int winIdx = 3; /* default 100 ms */
    for (int w = 0; w < kNumWins; w++) {
        if (std::fabs(trig.windowSec - kWinVals[w]) < kWinVals[w] * 1e-6) {
            winIdx = w; break;
        }
    }
    ImGui::SetNextItemWidth(75.f);
    if (ImGui::Combo("Win##trig", &winIdx, kWinLabels, kNumWins)) {
        trig.windowSec = kWinVals[winIdx];
        cfgChanged = true;
    }
    ImGui::SameLine();

    /* ── Pre-trigger % ───────────────────────────────────────────────── */
    ImGui::SetNextItemWidth(70.f);
    float preP = static_cast<float>(trig.prePercent);
    if (ImGui::SliderFloat("Pre##trig", &preP, 0.f, 100.f, "%.0f%%")) {
        trig.prePercent = static_cast<double>(preP);
    }
    if (ImGui::IsItemDeactivatedAfterEdit()) { cfgChanged = true; }
    ImGui::SameLine();

    /* ── Mode ────────────────────────────────────────────────────────── */
    int singleInt = trig.single ? 1 : 0;
    if (ImGui::RadioButton("Norm##trig", &singleInt, 0)) { cfgChanged = true; }
    ImGui::SameLine();
    if (ImGui::RadioButton("1x##trig", &singleInt, 1)) { cfgChanged = true; }
    trig.single = (singleInt != 0);
    ImGui::SameLine(0.f, 10.f);

    /* ── Status badge ────────────────────────────────────────────────── */
    ImVec4 badgeColor;
    const char* badgeText;
    if      (trig.status == "armed")      { badgeColor = ImVec4(1.f,0.8f,0.f,1.f);   badgeText = "ARMED"; }
    else if (trig.status == "collecting") { badgeColor = ImVec4(0.4f,0.7f,1.f,1.f);  badgeText = "COLLECTING"; }
    else if (trig.status == "triggered")  { badgeColor = ImVec4(0.2f,0.9f,0.3f,1.f); badgeText = "TRIGGERED"; }
    else                                  { badgeColor = ImVec4(0.6f,0.6f,0.6f,1.f); badgeText = "IDLE"; }
    ImGui::TextColored(badgeColor, "[%s]", badgeText);
    ImGui::SameLine();

    /* ── Arm / Disarm / Stop ────────────────────────────────────────── */
    bool canArm = !trig.signalKey.empty();
    if (!canArm) { ImGui::BeginDisabled(); }
    if (ImGui::SmallButton(ICON_FA_BOLT " Arm")) {
        sendTrigConfig(app);
        app.ws().sendText(BuildArm());
    }
    if (!canArm) { ImGui::EndDisabled(); }
    ImGui::SameLine();

    if (ImGui::SmallButton("Disarm")) {
        app.ws().sendText(BuildDisarm());
    }
    ImGui::SameLine();

    if (!trig.single) {
        if (ImGui::SmallButton(trig.stopped ? ICON_FA_PLAY " Run" : ICON_FA_STOP " Stop")) {
            trig.stopped = !trig.stopped;
            app.ws().sendText(BuildTrigStop(trig.stopped));
        }
        ImGui::SameLine();
    }

    /* Trigger time readout (inline) */
    if (trig.hasTrigTime &&
        (trig.status == "collecting" || trig.status == "triggered")) {
        ImGui::TextDisabled("t=%.6f", trig.trigTime);
    } else {
        ImGui::NewLine();
    }

    /* Config edits while armed take effect immediately on the hub */
    if (cfgChanged && !trig.signalKey.empty()) {
        sendTrigConfig(app);
    }

    ImGui::PopStyleVar();
    ImGui::Separator();
}

} /* namespace StreamHubClient */
