/**
 * @file HistoryWriter.h
 * @brief Disk-backed circular history storage for StreamHub signals.
 *
 * Each signal is stored in a separate binary file (.shist) with a fixed header
 * and a pre-allocated circular data region of (float64 time, float64 value) pairs.
 * The file size is determined at creation and never grows.
 *
 * File layout (per signal):
 *   Offset  Size  Field
 *   0       4     Magic: "SHR1"
 *   4       4     uint32 version (1)
 *   8       4     uint32 capacity (max pairs)
 *   12      4     uint32 head (next write position, 0-based, wraps)
 *   16      4     uint32 count (valid entries, <= capacity)
 *   20      4     uint32 decimation
 *   24      8     float64 tOldest
 *   32      8     float64 tNewest
 *   40      24    reserved (pad to 64 bytes)
 *   64      ...   data: capacity × 16 bytes (8 time + 8 value)
 *
 * Thread safety: WriteTick() is called from the push loop only.
 * ReadRange() uses pread() and is safe for concurrent reads from WS threads.
 */

#ifndef STREAMHUB_HISTORY_WRITER_H_
#define STREAMHUB_HISTORY_WRITER_H_

#include "CompilerTypes.h"
#include "StreamString.h"
#include "StructuredDataI.h"
#include "UDPSourceSession.h"

namespace StreamHub {

using MARTe::uint8;
using MARTe::uint16;
using MARTe::uint32;
using MARTe::uint64;
using MARTe::float64;
using MARTe::StreamString;
using MARTe::StructuredDataI;

/** Maximum number of sessions (matches kMaxSessions). */
static const uint32 kHistMaxSessions = 32u;

/** Header size in bytes. */
static const uint32 kHistHeaderSize = 64u;

/** Magic bytes. */
static const char kHistMagic[4] = {'S','H','R','1'};

class HistoryWriter {
public:

    HistoryWriter();
    ~HistoryWriter();

    /**
     * @brief Parse +History block from StreamHub config.
     *
     * Expected keys inside a +History node:
     *   Directory      (string, required)
     *   DurationHours  (float64, default 1)
     *   Decimation     (uint32, default 1)
     *   FlushIntervalSec (uint32, default 5)
     *   MinDiskFreeMB  (uint32, default 500)
     */
    bool Initialise(StructuredDataI &cfg);

    /** @return true if history storage is enabled and initialised. */
    bool IsEnabled() const { return enabled_; }

    /**
     * @brief Called when a source becomes configured.
     * Creates / reopens per-signal .shist files.
     */
    void OnSourceConfigured(uint32 sessionIdx, const char *sourceId,
                            UDPSourceSession &sess);

    /**
     * @brief Called from the push loop to write new samples for one session.
     * Uses per-signal ReadSince cursors. Applies decimation.
     */
    void WriteTick(uint32 sessionIdx, UDPSourceSession &sess);

    /**
     * @brief Flush in-memory header state to disk (periodic).
     */
    void FlushHeaders();

    /**
     * @brief Read a time range for one signal from disk.
     * @return Number of (t,v) pairs written to tOut/vOut.
     */
    uint32 ReadRange(const char *sourceId, const char *signalName,
                     float64 t0, float64 t1,
                     float64 *tOut, float64 *vOut, uint32 maxOut) const;

    /**
     * @brief Get the time range stored on disk for a signal.
     * @return true if the signal has history data.
     */
    bool GetTimeRange(const char *sourceId, const char *signalName,
                      float64 &t0, float64 &t1) const;

    /**
     * @brief Build a JSON fragment for historyInfo response.
     * Writes into buf at offset, growing via the provided append function.
     */
    void AppendInfoJSON(char *&buf, uint32 &off, uint32 &cap) const;

    /** @brief Check available disk space. */
    bool HasSufficientDisk() const;

    /** @brief Duration in hours. */
    float64 DurationHours() const { return durationHours_; }

    /** @brief Decimation factor. */
    uint32 Decimation() const { return decimation_; }

private:

    struct SignalFile {
        int      fd;             /**< File descriptor (-1 = not open) */
        uint32   capacity;       /**< Total capacity in pairs */
        uint32   head;           /**< Next write position */
        uint32   count;          /**< Valid entries */
        float64  tOldest;
        float64  tNewest;
        uint64   readCursor;     /**< For ReadSince from ring buffer */
        uint64   decimCounter;   /**< Decimation sample counter */
        bool     headerDirty;    /**< True if header needs flushing */
        char     sourceId[64];   /**< Source ID for lookup */
        char     signalName[64]; /**< Signal name for lookup */
    };

    /** Per-signal scratch for reading from rings. */
    static const uint32 kReadScratch = 65536u;
    float64 *scratchT_;
    float64 *scratchV_;

    /** Maximum signals tracked per session for history. */
    static const uint32 kHistMaxSignals = 64u;

    /** Heap-allocated to keep StreamHub object size manageable. */
    SignalFile (*files_)[kHistMaxSignals];      /**< [kHistMaxSessions][kHistMaxSignals] */
    bool       (*fileActive_)[kHistMaxSignals]; /**< [kHistMaxSessions][kHistMaxSignals] */

    bool     enabled_;
    char     directory_[512];
    float64  durationHours_;
    uint32   decimation_;
    uint32   flushIntervalSec_;
    uint32   minDiskFreeMB_;
    bool     diskLow_;          /**< True when disk space is below threshold */

    /** Create or reopen a .shist file for one signal. */
    bool OpenSignalFile(uint32 sessionIdx, uint32 sigIdx,
                        const char *sourceId, const char *signalName,
                        float64 estimatedRateHz);

    /** Write a batch of (t,v) pairs to a signal file. */
    void WritePairs(SignalFile &sf, const float64 *t, const float64 *v,
                    uint32 n);

    /** Flush one signal file header to disk. */
    void FlushHeader(SignalFile &sf);

    /** Find a signal file by sourceId:signalName. */
    const SignalFile* FindFile(const char *sourceId, const char *signalName) const;
};

} /* namespace StreamHub */

#endif /* STREAMHUB_HISTORY_WRITER_H_ */
