/**
 * @file Model.h
 * @brief Domain types for the Qt StreamHub client.
 *
 * Mirrors the ImGui client's App.h domain model, but uses QColor instead of
 * ImVec4 and adds no Qt widget dependencies (pure data).
 */

#pragma once

#include "Protocol.h"       // SignalMeta, SourceStats (reused, framework-free)
#include "SignalBuffer.h"   // SignalBuffer, LTTBDecimate (reused)

#include <QColor>
#include <string>
#include <vector>
#include <cstdint>

namespace shq {

using StreamHubClient::SignalMeta;
using StreamHubClient::SourceStats;
using StreamHubClient::SignalBuffer;

/** One signal as known to the client. */
struct SignalView {
    SignalMeta   meta;
    SignalBuffer buf{1000000};
    QColor       color{137, 180, 250};   /* Catppuccin blue */
    double       lineWidth = 1.5;
    int          marker    = -1;          /* -1 = none, else MarkerStyle index */
    bool         visible   = true;
};

/** One connected UDPStreamer source. */
struct Source {
    std::string             id;
    std::string             label;
    std::string             addr;
    std::string             state      = "disconnected";
    uint32_t                port       = 0;
    bool                    configured = false;
    int                     publishMode = 0;
    std::vector<SignalView> signals;
    SourceStats             stats;
};

/** Trigger configuration + live state (hub-side trigger semantics). */
struct TriggerCfgState {
    /* Config (sent to hub via setTrigger) */
    std::string signalKey;            /* full key "src:sig" or "src:sig[i]" */
    int         edge        = 0;      /* 0=rising 1=falling 2=both */
    double      threshold   = 0.0;
    double      windowSec   = 0.1;    /* 100 µs .. 10 s */
    double      prePercent  = 20.0;
    bool        single      = false;  /* false=normal true=single */

    /* Live (driven by hub triggerState broadcasts) */
    std::string status      = "idle"; /* idle|armed|collecting|triggered */
    bool        stopped     = false;
    bool        hasTrigTime = false;
    double      trigTime    = 0.0;
};

/** Per-signal vertical scale state (oscilloscope style). */
struct VScale {
    int    mode      = 0;    /* 0=auto 1=range 2=manual */
    double divValue  = 1.0;  /* units per division (manual) */
    double offset    = 0.0;  /* raw value at center (manual) */
    double screenPos = 0.0;  /* position offset in divisions from center */
    bool   digitalInMixed = false;
    /* Resolved each frame: */
    double resolvedDiv    = 1.0;
    double resolvedOffset = 0.0;
};

/** Assignment of one signal to a plot panel. */
struct PlotAssignment {
    int    sourceIdx = -1;
    int    signalIdx = -1;
    VScale vs;
};

/*---------------------------------------------------------------------------*/
/* Plot layouts (mirror the web UI: cols×rows)                                */
/*---------------------------------------------------------------------------*/

enum class PlotLayout {
    k1x1 = 0, k2x1, k1x2, k3x1, k1x3, k2x2, k4x1, k1x4, kCount
};

inline const char* const* layoutNames() {
    static const char* const names[] = {
        "1\u00d71", "2\u00d71", "1\u00d72", "3\u00d71",
        "1\u00d73", "2\u00d72", "4\u00d71", "1\u00d74"
    };
    return names;
}

inline void layoutDims(PlotLayout l, int& cols, int& rows) {
    switch (l) {
        case PlotLayout::k1x1: cols = 1; rows = 1; break;
        case PlotLayout::k2x1: cols = 2; rows = 1; break;
        case PlotLayout::k1x2: cols = 1; rows = 2; break;
        case PlotLayout::k3x1: cols = 3; rows = 1; break;
        case PlotLayout::k1x3: cols = 1; rows = 3; break;
        case PlotLayout::k2x2: cols = 2; rows = 2; break;
        case PlotLayout::k4x1: cols = 4; rows = 1; break;
        case PlotLayout::k1x4: cols = 1; rows = 4; break;
        default:               cols = 1; rows = 1; break;
    }
}

inline int layoutSlots(PlotLayout l) {
    int c, r; layoutDims(l, c, r); return c * r;
}

static const int kMaxPlotSlots = 8;

} /* namespace shq */
