#ifndef DEBUGSERVICE_H
#define DEBUGSERVICE_H

#include "BasicTCPSocket.h"
#include "DebugServiceBase.h"
#include "EmbeddedServiceMethodBinderI.h"
#include "MessageI.h"
#include "ReferenceT.h"
#include "SingleThreadService.h"
#include "UDPSServer.h"

namespace MARTe {

/**
 * @brief TCP/UDP implementation of DebugServiceI (via DebugServiceBase).
 *
 * Signal tracing now uses the UDPS binary protocol (same wire format as
 * UDPStreamer) so that the same Go web client can consume both sources.
 *
 * Flow:
 *  1. The Go client listens on udpPort.
 *  2. DebugService binds the same port on its end (or uses the client's
 *     configured IP:port as destination for the old push model).
 *  3. On startup and on every TRACE change, DebugService sends a CONFIG
 *     packet listing the currently-traced signals.
 *  4. For each RT drain cycle that contains new data, DebugService sends
 *     one DATA packet (Strict mode) with the most-recent value per signal.
 */
class DebugService : public DebugServiceBase,
                     public MessageI,
                     public EmbeddedServiceMethodBinderI {
public:
    friend class DebugServiceTest;
    CLASS_REGISTER_DECLARATION()

    DebugService();
    virtual ~DebugService();

    virtual bool Initialise(StructuredDataI &data);

    virtual ErrorManagement::ErrorType Execute(ExecutionInfo &info);
    virtual ErrorManagement::ErrorType HandleMessage(ReferenceT<Message> &data);

    /**
     * @brief Override TraceSignal to trigger a CONFIG re-send after each change.
     */
    virtual uint32 TraceSignal(const char8 *name, bool enable, uint32 decimation = 1u);

protected:
    virtual void GetServiceInfo(StreamString &out);
    virtual void RebuildTransportConfig();

private:
    void InjectTcpLoggerIfNeeded();

    ErrorManagement::ErrorType Server  (ExecutionInfo &info);
    ErrorManagement::ErrorType Streamer(ExecutionInfo &info);

    /**
     * @brief Build and transmit a CONFIG packet for the current traced-signal set.
     * @details Iterates over all registered signals, selects those with
     *          isTracing == true, builds UDPSSignalDescriptor array, and sends
     *          a (possibly fragmented) UDPS CONFIG packet.  Also rebuilds the
     *          udpsSlots table and udpsWirePayload buffer.
     * @return true on success.
     */
    bool SendUDPSConfig();

    // -----------------------------------------------------------------------
    // TCP/UDP transport configuration
    // -----------------------------------------------------------------------
    uint16      controlPort;
    uint16      streamPort;
    uint16      logPort;
    StreamString streamIP;
    bool        isServer;
    bool        suppressTimeoutLogs;

    /** Optional authentication token (CR-5).  If set (non-empty), the first
     *  command from a new TCP client must be "AUTH <token>".  All other
     *  commands are rejected until the client authenticates.  If empty
     *  (default), no authentication is required (back-compat). */
    StreamString authToken;
    bool        clientAuthenticated;

    BasicTCPSocket tcpServer;
    UDPSServer     udpsServer;   ///< Handles fragmentation and multi-client sending

    // -----------------------------------------------------------------------
    // Server-thread helper class
    // -----------------------------------------------------------------------
    class ServiceBinder : public EmbeddedServiceMethodBinderI {
    public:
        enum ServiceType { ServerType, StreamerType };
        ServiceBinder(DebugService *parent, ServiceType type)
            : parent(parent), type(type) {}
        virtual ErrorManagement::ErrorType Execute(ExecutionInfo &info) {
            if (type == StreamerType) return parent->Streamer(info);
            return parent->Server(info);
        }
    private:
        DebugService *parent;
        ServiceType   type;
    };

    ServiceBinder       binderServer;
    ServiceBinder       binderStreamer;
    SingleThreadService threadService;
    SingleThreadService streamerService;

    ThreadIdentifier serverThreadId;
    ThreadIdentifier streamerThreadId;

    FastPollingMutexSem clientMutex;
    BasicTCPSocket     *activeClient;

    // -----------------------------------------------------------------------
    // TCP server rate limiting and idle detection
    // -----------------------------------------------------------------------
    static const uint32 CMD_RATE_LIMIT        = 100u;
    static const uint32 CLIENT_IDLE_TIMEOUT_MS = 120000u;
    static const uint32 INPUT_BUFFER_MAX       = 8192u;

    uint32 cmdCountInWindow;
    uint64 cmdWindowStartMs;
    uint64 lastDataTimeMs;

    StreamString inputBuffer;

    // -----------------------------------------------------------------------
    // UDPS streaming state (replaces the old custom-format streamer buffers)
    // -----------------------------------------------------------------------

    /**
     * @brief Per-slot descriptor for DATA packet assembly.
     * One slot per currently-traced signal; index mirrors CONFIG order.
     */
    struct UDPSSlot {
        uint32 internalID;   ///< DebugSignalInfo::internalID
        uint32 wireOffset;   ///< Byte offset in udpsDataPayload (after 8-byte HRT)
        uint32 wireSize;     ///< Bytes occupied by this signal in the DATA payload
        bool   everFilled;   ///< True once at least one sample has been placed
    };

    /** Maximum number of simultaneously traced signals. */
    static const uint32 UDPS_MAX_SLOTS = 512u;

    /** Maximum payload per UDP datagram (signal data bytes, excluding header). */
    static const uint32 UDPS_MAX_PAYLOAD = 1400u;

    /** Hard cap on a single signal's data: 65535 - header(17) - HRT(8). */
    static const uint32 UDPS_MAX_SAMPLE_BYTES = 65510u;

    UDPSSlot  udpsSlots[UDPS_MAX_SLOTS]; ///< Slot table (valid for [0, udpsNumSlots))
    uint32    udpsNumSlots;               ///< Number of active slots

    /** Heap-allocated DATA payload buffer: [HRT:8][sig0_data]...[sigN_data].
     *  Re-allocated by SendUDPSConfig(). NULL when no signals are traced. */
    uint8    *udpsDataPayload;
    uint32    udpsDataPayloadSize;

    /** Staging buffer: a single sample popped from traceBuffer. */
    uint8     udpsSampleBuf[65535u];

    /** Packet sequence counter (incremented per DATA/CONFIG datagram group). */
    uint32    udpsPacketCounter;

    /**
     * @brief Set to true by TraceSignal() to request a CONFIG re-send.
     * Read and cleared by the Streamer thread.
     */
    volatile bool udpsConfigPending;
};

} // namespace MARTe

#endif // DEBUGSERVICE_H
