#ifndef DEBUGSERVICEI_H
#define DEBUGSERVICEI_H

/**
 * @file DebugServiceI.h
 * @brief Abstract interface for the MARTe2 debug/instrumentation service.
 *
 * All broker wrappers (DebugBrokerWrapper) depend solely on this interface,
 * decoupling them from the concrete TCP/UDP implementation.  Alternative
 * transports — TTY, WebSocket, shared-memory ring, etc. — can be plugged in
 * by providing a new concrete implementation without touching any broker or
 * application code.
 *
 * The interface is split into two logical groups:
 *
 *   RT-path API
 *     Called from broker threads on every RT cycle.  Implementations must
 *     keep these methods as cheap as possible; the only permissible
 *     synchronisation primitive is a fast spinlock (FastPollingMutexSem).
 *
 *   Control-path API
 *     Called from server / command-handler threads (TCP, TTY, Web …).
 *     Latency here is acceptable; correctness and thread-safety matter.
 *
 * Concrete implementations register themselves during Initialise() via
 * SetInstance().  Broker wrappers retrieve the active instance via
 * GetInstance(); there is no ORD search on the hot path.
 */

#include "DebugCore.h"
#include "FastPollingMutexSem.h"
#include "Object.h"
#include "StreamString.h"
#include "TypeDescriptor.h"

namespace MARTe {

// Forward declarations — concrete types are only needed in the implementation.
class MemoryMapBroker;

struct SignalAlias {
    StreamString name;
    uint32 signalIndex;
};

struct BrokerInfo {
    DebugSignalInfo **signalPointers;
    uint32 numSignals;
    MemoryMapBroker *broker;
    volatile bool *anyActiveFlag;
    Vector<uint32> *activeIndices;
    Vector<uint32> *activeSizes;
    FastPollingMutexSem *activeMutex;
    volatile bool *anyBreakFlag;
    Vector<uint32> *breakIndices;
    StreamString gamName;
    bool isOutput;
};

/**
 * @brief Abstract debug-service interface.
 */
class DebugServiceI {
public:
    // -------------------------------------------------------------------------
    // Static instance registry
    // -------------------------------------------------------------------------

    /**
     * @brief Return the currently registered debug-service instance, or NULL.
     *
     * Called on every broker Init() path and from OutpautPauseAndStep().
     * Returns NULL when no debug service has been initialised, in which case
     * all instrumentation is a no-op.
     */
    static DebugServiceI *GetInstance() { return instance; }

    /**
     * @brief Register @p inst as the global debug-service.
     *
     * Concrete implementations call this from their Initialise() method.
     * Passing NULL deregisters the current instance (called from the
     * destructor so dangling pointers are never visible to broker threads).
     */
    static void SetInstance(DebugServiceI *inst) { instance = inst; }

    virtual ~DebugServiceI() {}

    // =========================================================================
    // RT-path API  (called from broker execute threads every cycle)
    // =========================================================================

    /**
     * @brief Register a signal memory region with the debug service.
     *
     * Called once per signal during broker Init().  Returns a pointer to the
     * internal DebugSignalInfo that the broker caches for use on the hot path.
     * Thread-safe; must not be called after the RT loop has started.
     */
    virtual DebugSignalInfo *RegisterSignal(void       *memoryAddress,
                                             TypeDescriptor type,
                                             const char8   *name,
                                             uint8          numberOfDimensions = 0,
                                             uint32         numberOfElements   = 1) = 0;

    /**
     * @brief Process one signal on the RT path.
     *
     * Applies forced values (memcpy into signal memory) and, when tracing is
     * enabled and the decimation counter fires, pushes a sample to the trace
     * ring buffer.  Called under the broker's activeMutex; implementations
     * must not acquire any lock that is also held by the Server thread.
     */
    virtual void ProcessSignal(DebugSignalInfo *signalInfo,
                                uint32           size,
                                uint64           timestamp) = 0;

    /**
     * @brief Register a broker so the service can push active/break index
     *        updates to it without iterating every signal.
     */
    virtual void RegisterBroker(DebugSignalInfo    **signalPointers,
                                 uint32               numSignals,
                                 MemoryMapBroker     *broker,
                                 volatile bool       *anyActiveFlag,
                                 Vector<uint32>         *activeIndices,
                                 Vector<uint32>         *activeSizes,
                                 FastPollingMutexSem *activeMutex,
                                 volatile bool       *anyBreakFlag,
                                 Vector<uint32>         *breakIndices,
                                 const char8         *gamName  = NULL_PTR(const char8 *),
                                 bool                 isOutput = false) = 0;

    /** @brief Return true if the RT loop is currently held at a pause/breakpoint. */
    virtual bool IsPaused() const = 0;

    /** @brief Set or clear the paused state (called by break-condition logic). */
    virtual void SetPaused(bool paused) = 0;

    /** @brief Return true if a step count is pending (stepRemaining > 0). */
    virtual bool IsStepPending() const = 0;

    /**
     * @brief Consume one step credit for the current output-broker cycle.
     *
     * Called by every output broker after Execute().  No-op when stepRemaining
     * is zero (the common case); only acquires a mutex when stepping is active.
     */
    virtual void ConsumeStepIfNeeded(
        const char8 *gamName,
        const char8 *threadName = NULL_PTR(const char8 *)) = 0;

    // =========================================================================
    // Control-path API  (called from server / command-handler threads)
    // =========================================================================

    virtual uint32 ForceSignal  (const char8 *name, const char8 *valueStr)          = 0;
    virtual uint32 UnforceSignal(const char8 *name)                                  = 0;
    virtual uint32 TraceSignal  (const char8 *name, bool enable, uint32 decimation = 1) = 0;
    virtual uint32 SetBreak     (const char8 *name, uint8 op, float64 threshold)    = 0;
    virtual uint32 ClearBreak   (const char8 *name)                                  = 0;
    virtual bool   IsInstrumented(const char8 *fullPath,
                                   bool &traceable, bool &forcable)                  = 0;
    virtual uint32 RegisterMonitorSignal(const char8 *path, uint32 periodMs)        = 0;
    virtual uint32 UnmonitorSignal      (const char8 *path)                          = 0;

    // =========================================================================
    // Utility  (implementation-agnostic, defined in DebugService.cpp)
    // =========================================================================

    /**
     * @brief Resolve the fully-qualified ORD path of @p obj into @p fullPath.
     *
     * Static so it can be called without a service instance.  All concrete
     * implementations (and InitSignals in DebugBrokerWrapper.h) use this
     * to build canonical signal names.  The definition lives in
     * DebugService.cpp alongside FindPathInContainer().
     */
    static bool GetFullObjectName(const Object &obj, StreamString &fullPath);

protected:
    /** Pointer to the single active debug-service instance. */
    static DebugServiceI *instance;
};

} // namespace MARTe

#endif // DEBUGSERVICEI_H
