/**
 * @file TriggerEngine.h
 * @brief Hub-side trigger FSM with web-oscilloscope semantics.
 *
 * Mirrors the web client's trigger logic (Client/udpstreamer/static/app.js):
 *
 *   IDLE      --Arm()-->      ARMED
 *   ARMED     --edge fires--> COLLECTING   (trigTime + pre/post window latched)
 *   COLLECTING -capture done-> TRIGGERED   (StreamHub broadcasts the capture)
 *   TRIGGERED --Arm()-->      ARMED        (auto after ~200 ms in normal mode,
 *                                           manual "rearm" in single mode)
 *   any       --Disarm()-->   IDLE
 *
 * Configuration is the web client's: full signal key ("src:sig" or
 * "src:sig[i]"), edge rising/falling/both, threshold, capture window length
 * windowSec, pre-trigger percentage, and acquisition mode normal/single with
 * a "stopped" flag pausing auto-rearm.
 *
 * CheckSample() is called from the UDPSClient receive thread for every decoded
 * sample of the configured signal.  All state is protected by a
 * FastPollingMutexSem so the StreamHub push thread can poll it safely.
 */

#ifndef STREAMHUB_TRIGGER_ENGINE_H_
#define STREAMHUB_TRIGGER_ENGINE_H_

#include "CompilerTypes.h"
#include "FastPollingMutexSem.h"
#include "StreamString.h"

namespace StreamHub {

using MARTe::uint32;
using MARTe::float64;
using MARTe::StreamString;
using MARTe::FastPollingMutexSem;

/** Trigger FSM states (badge mapping: idle/armed/collecting/triggered). */
enum TrigState {
    kTrigIdle       = 0,
    kTrigArmed      = 1,
    kTrigCollecting = 2,
    kTrigTriggered  = 3
};

/** Edge selection. */
enum TrigEdge {
    kEdgeRising  = 0,
    kEdgeFalling = 1,
    kEdgeBoth    = 2
};

/** Acquisition mode. */
enum TrigAcqMode {
    kTrigNormal = 0,  ///< Auto-rearm after each capture (unless stopped)
    kTrigSingle = 1   ///< Stay TRIGGERED until an explicit rearm
};

/** @brief Trigger configuration (web client semantics). */
struct TriggerConfig {
    TriggerConfig();
    StreamString signalKey;   ///< Full key: "src:sig" or "src:sig[i]"
    TrigEdge     edge;        ///< Rising / falling / both
    float64      threshold;   ///< Trigger threshold (physical units)
    float64      windowSec;   ///< Capture window length [1e-4 .. 10] s
    float64      prePercent;  ///< Pre-trigger part of the window [0 .. 100] %
    TrigAcqMode  mode;        ///< Normal (auto-rearm) or single
};

/**
 * @brief Hub-side trigger FSM.
 */
class TriggerEngine {
public:

    TriggerEngine();

    /** @brief Replace the configuration (bumps the config epoch). */
    void SetConfig(const TriggerConfig &cfg);

    /** @return Copy of the current configuration. */
    TriggerConfig GetConfig() const;

    /**
     * @return Monotonic configuration epoch.  UDPSourceSession caches the
     * resolved (signal idx, element idx) per epoch and re-resolves on change.
     */
    uint32 GetConfigEpoch() const;

    /** @brief Arm: any state → ARMED (resets edge detection). */
    void Arm();

    /** @brief Disarm: any state → IDLE; clears the stopped flag. */
    void Disarm();

    /** @brief Set/clear the stopped flag (pauses normal-mode auto-rearm). */
    void SetStopped(bool stopped);

    /** @return Current stopped flag. */
    bool GetStopped() const;

    /**
     * @brief Edge-detect one decoded sample of the configured signal.
     * Receive-thread context.  Only acts in ARMED state; on a matching edge
     * latches trigTime and the pre/post window and moves to COLLECTING.
     */
    void CheckSample(float64 t, float64 v);

    /** @return Current FSM state. */
    TrigState GetState() const;

    /**
     * @brief Get the latched capture window of the last fired trigger.
     * @return false if no trigger has fired since the last Disarm().
     */
    bool GetFiredWindow(float64 &trigTime, float64 &preSec,
                        float64 &postSec) const;

    /** @brief COLLECTING → TRIGGERED (push thread, after the capture frame). */
    void MarkTriggered();

private:
    mutable FastPollingMutexSem mutex_;
    TriggerConfig config_;
    uint32        epoch_;
    TrigState     state_;
    bool          stopped_;
    float64       prevValue_;    ///< Last sample (edge detection)
    bool          prevValid_;    ///< First-sample guard in ARMED state
    float64       trigTime_;     ///< Latched trigger time (Unix s)
    float64       firedPreSec_;  ///< Window pre-part latched at fire time
    float64       firedPostSec_; ///< Window post-part latched at fire time
    bool          firedValid_;   ///< true after a fire, until Disarm()
};

inline TriggerConfig::TriggerConfig()
    : edge(kEdgeRising),
      threshold(0.0),
      windowSec(1.0),
      prePercent(20.0),
      mode(kTrigNormal) {
}

} /* namespace StreamHub */

#endif /* STREAMHUB_TRIGGER_ENGINE_H_ */
