/**
 * @file Protocol.h
 * @brief StreamHub wire protocol: binary frame parser + JSON builder/parser.
 */

#pragma once

#include "SignalBuffer.h"

#include <string>
#include <vector>
#include <functional>
#include <cstdint>

namespace StreamHubClient {

/* ── Signal metadata (mirrors UDPSSignalDescriptor fields used by UI) ─────── */

struct SignalMeta {
    std::string name;
    std::string unit;
    int         typeCode       = 0;
    int         quantType      = 0;
    int         timeMode       = 0;
    int         timeSignalIdx  = -1;
    float       samplingRate   = 0.f;
    double      rangeMin       = 0.0;
    double      rangeMax       = 0.0;
    uint32_t    numRows        = 1u;
    uint32_t    numCols        = 1u;
    uint32_t    numElements    = 1u;  /* numRows × numCols */
};

/* ── Per-source statistics (hub "stats" broadcast, Go field names) ────────── */

struct SourceStats {
    std::string state          = "disconnected";
    uint64_t    totalReceived  = 0;
    uint64_t    totalLost      = 0;
    double      rateHz         = 0.0;
    double      rateStdHz      = 0.0;
    double      fragsPerCycle  = 0.0;
    double      bytesPerCycle  = 0.0;
    double      cycleAvgMs     = 0.0;
    double      cycleStdMs     = 0.0;
    double      cycleMinMs     = 0.0;
    double      cycleMaxMs     = 0.0;
    double      cycleHistMin   = 0.0;
    double      cycleHistMax   = 0.0;
    double      cycleHist[20]  = {};
};

/* ── Source descriptor ────────────────────────────────────────────────────── */

struct SourceInfo {
    std::string id;
    std::string label;
    std::string addr;
    std::string state;
    uint32_t    port = 0;
};

/* ── Decoded push-frame signal data ──────────────────────────────────────── */

struct FrameSignal {
    std::string          name;
    std::vector<double>  t;
    std::vector<double>  v;
};

struct DataFrame {
    std::string              sourceId;
    std::vector<FrameSignal> signals;
};

/* ── Zoom response ────────────────────────────────────────────────────────── */

struct ZoomSignal {
    std::string          name;   /* full key "src:sig" */
    std::vector<double>  t;
    std::vector<double>  v;
};

struct ZoomResponse {
    uint32_t                 reqId = 0;
    std::vector<ZoomSignal>  signals;
};

/* ── Trigger capture (binary frame version=2) ─────────────────────────────── */

struct CaptureSignal {
    std::string          key;    /* full key "src:sig" */
    std::vector<double>  t;
    std::vector<double>  v;
};

struct CaptureFrame {
    double trigTime = 0.0;
    double preSec   = 0.0;
    double postSec  = 0.0;
    std::vector<CaptureSignal> signals;
};

/* ── Trigger state event ──────────────────────────────────────────────────── */

struct TriggerStateMsg {
    std::string state;            /* idle|armed|collecting|triggered */
    std::string mode = "normal";  /* normal|single */
    bool        stopped     = false;
    bool        hasTrigTime = false;
    double      trigTime    = 0.0;
};

/*---------------------------------------------------------------------------*/
/* Binary frame parser                                                        */
/*---------------------------------------------------------------------------*/

/**
 * @brief Parse one StreamHub binary push frame.
 *
 * Format:
 *   [1] version | [1] idLen | [idLen] sourceId | [4] numSignals
 *   per signal: [2] keyLen | [key] | [4] pairCount | [N×8] t | [N×8] v
 *
 * @return false if the frame is malformed.
 */
bool ParseBinaryFrame(const uint8_t* buf, size_t len, DataFrame& out);

/**
 * @brief Parse a version=2 trigger-capture binary frame.
 *
 * Format:
 *   [1] version=2 | [8] trigTime | [8] preSec | [8] postSec | [4] nSig
 *   per signal: [2] keyLen | [key] | [4] N | [N×8] t | [N×8] v
 *
 * @return false if the frame is malformed or version != 2.
 */
bool ParseCaptureFrame(const uint8_t* buf, size_t len, CaptureFrame& out);

/*---------------------------------------------------------------------------*/
/* JSON command builders                                                      */
/*---------------------------------------------------------------------------*/

std::string BuildPing();
std::string BuildGetSources();
std::string BuildGetConfig(const std::string& sourceId);
std::string BuildGetStats();
std::string BuildAddSource(const std::string& label, const std::string& addr,
                            const std::string& multicastGroup = std::string(),
                            uint16_t dataPort = 0);
std::string BuildRemoveSource(const std::string& id);
std::string BuildArm();
std::string BuildDisarm();
std::string BuildRearm();
std::string BuildTrigStop(bool stopped);
std::string BuildSaveSources();
/** edge: "rising" | "falling" | "both"; mode: "normal" | "single". */
std::string BuildSetTrigger(const std::string& signalKey, const std::string& edge,
                             double threshold, double windowSec,
                             double prePercent, const std::string& mode);
/** signalsCsv: comma-separated full keys "src:sig,src:sig2"; n<=0 → raw. */
std::string BuildZoom(uint32_t reqId, double t0, double t1, int n,
                       const std::string& signalsCsv);
/** History zoom: same shape as zoom but reads from disk history. */
std::string BuildHistoryZoom(uint32_t reqId, double t0, double t1, int n,
                              const std::string& signalsCsv);
std::string BuildHistoryInfo();
std::string BuildSetMaxPoints(uint32_t n);

/*---------------------------------------------------------------------------*/
/* JSON event parsers                                                         */
/*---------------------------------------------------------------------------*/

/** @brief Extract the "type" field from a JSON text frame. */
std::string ParseType(const std::string& json);

/** @brief Parse a "sources" event. */
bool ParseSources(const std::string& json, std::vector<SourceInfo>& out);

/** @brief Parse a "config" event (one source). */
bool ParseConfig(const std::string& json, std::string& sourceId,
                 int& publishMode, std::vector<SignalMeta>& signals);

/** @brief Parse a "stats" event. */
bool ParseStats(const std::string& json,
                std::vector<std::pair<std::string, SourceStats>>& out);

/** @brief Parse a "triggerState" event. */
bool ParseTriggerState(const std::string& json, TriggerStateMsg& out);

/** @brief Parse a "zoom" response. */
bool ParseZoom(const std::string& json, ZoomResponse& out);

/** @brief Parse a "historyInfo" event. */
struct HistorySignalRange {
    std::string key;
    double t0 = 0.0, t1 = 0.0;
    uint32_t count = 0, capacity = 0;
};

struct HistoryInfoMsg {
    bool     enabled       = false;
    double   durationHours = 0.0;
    uint32_t decimation    = 0;
    std::vector<HistorySignalRange> signals;
};

bool ParseHistoryInfo(const std::string& json, HistoryInfoMsg& out);

/** @brief Parse a "maxPointsUpdated" event. */
bool ParseMaxPointsUpdated(const std::string& json, uint32_t& maxPoints);

} /* namespace StreamHubClient */
