#ifndef DEBUGBROKERWRAPPER_H
#define DEBUGBROKERWRAPPER_H

#include "DebugService.h"
#include "BrokerI.h"
#include "MemoryMapBroker.h"
#include "ObjectRegistryDatabase.h"
#include "ReferenceT.h"
#include "ObjectBuilder.h"
#include "HighResolutionTimer.h"

// Original broker headers
#include "MemoryMapInputBroker.h"
#include "MemoryMapOutputBroker.h"
#include "MemoryMapSynchronisedInputBroker.h"
#include "MemoryMapSynchronisedOutputBroker.h"
#include "MemoryMapInterpolatedInputBroker.h"
#include "MemoryMapMultiBufferInputBroker.h"
#include "MemoryMapMultiBufferOutputBroker.h"
#include "MemoryMapSynchronisedMultiBufferInputBroker.h"
#include "MemoryMapSynchronisedMultiBufferOutputBroker.h"

namespace MARTe {

/**
 * @brief Base implementation for all debug brokers.
 */
class DebugBrokerHelper {
public:
    static void Process(BrokerI* broker, DebugService* service, DebugSignalInfo** signalInfoPointers, uint32 numSignals) {
        if (service == NULL_PTR(DebugService*)) return;

        while (service->IsPaused()) {
            Sleep::MSec(10);
        }

        if (signalInfoPointers != NULL_PTR(DebugSignalInfo**)) {
            for (uint32 i = 0; i < numSignals; i++) {
                DebugSignalInfo *s = signalInfoPointers[i];
                if (s != NULL_PTR(DebugSignalInfo*)) {
                    if (s->isTracing || s->isForcing) {
                        uint32 size = broker->GetCopyByteSize(i);
                        service->ProcessSignal(s, size);
                    }
                }
            }
        }
    }

    static void InitSignals(MemoryMapBroker* broker, DataSourceI &dataSourceIn, DebugService* &service, DebugSignalInfo** &signalInfoPointers, uint32 &numSignals, MemoryMapBrokerCopyTableEntry* copyTable, const char8* functionName, SignalDirection direction) {
        numSignals = broker->GetNumberOfCopies();
        if (numSignals > 0) {
            signalInfoPointers = new DebugSignalInfo*[numSignals];
            for (uint32 i=0; i<numSignals; i++) signalInfoPointers[i] = NULL_PTR(DebugSignalInfo*);
        }

        ReferenceContainer *root = ObjectRegistryDatabase::Instance();
        Reference serviceRef = root->Find("DebugService");
        if (serviceRef.IsValid()) {
            service = dynamic_cast<DebugService*>(serviceRef.operator->());
        }

        if (service && (copyTable != NULL_PTR(MemoryMapBrokerCopyTableEntry*))) {
            StreamString dsPath;
            DebugService::GetFullObjectName(dataSourceIn, dsPath);

            for (uint32 i = 0; i < numSignals; i++) {
                void *addr = copyTable[i].dataSourcePointer;
                TypeDescriptor type = copyTable[i].type;
                
                uint32 dsIdx = broker->GetDSCopySignalIndex(i);
                StreamString signalName;
                if (!dataSourceIn.GetSignalName(dsIdx, signalName)) signalName = "Unknown";
                
                // 1. Register canonical DataSource name (Absolute)
                StreamString dsFullName;
                dsFullName.Printf("%s.%s", dsPath.Buffer(), signalName.Buffer());
                service->RegisterSignal(addr, type, dsFullName.Buffer()); 

                // 2. Also register absolute GAM alias
                if (functionName != NULL_PTR(const char8*)) {
                    StreamString gamFullName;
                    const char8* dirStr = (direction == InputSignals) ? "In" : "Out";
                    Reference gamRef = ObjectRegistryDatabase::Instance()->Find(functionName);
                    if (gamRef.IsValid()) {
                        StreamString absGamPath;
                        DebugService::GetFullObjectName(*(gamRef.operator->()), absGamPath);
                        gamFullName.Printf("%s.%s.%s", absGamPath.Buffer(), dirStr, signalName.Buffer());
                    } else {
                        gamFullName.Printf("Root.%s.%s.%s", functionName, dirStr, signalName.Buffer());
                    }
                    signalInfoPointers[i] = service->RegisterSignal(addr, type, gamFullName.Buffer());
                } else {
                    signalInfoPointers[i] = service->RegisterSignal(addr, type, dsFullName.Buffer());
                }
            }
        }
    }
};

#define DECLARE_DEBUG_BROKER_COMMON(BaseClass) \
    Debug##BaseClass() : BaseClass() { \
        service = NULL_PTR(DebugService*); \
        signalInfoPointers = NULL_PTR(DebugSignalInfo**); \
        numSignals = 0; \
    } \
    virtual ~Debug##BaseClass() { \
        if (signalInfoPointers) delete[] signalInfoPointers; \
    } \
    virtual bool Execute() { \
        bool ret = BaseClass::Execute(); \
        if (ret) DebugBrokerHelper::Process(this, service, signalInfoPointers, numSignals); \
        return ret; \
    } \
private: \
    DebugService *service; \
    DebugSignalInfo **signalInfoPointers; \
    uint32 numSignals;

#define DECLARE_DEBUG_BROKER(BaseClass) \
class Debug##BaseClass : public BaseClass { \
public: \
    DECLARE_DEBUG_BROKER_COMMON(BaseClass) \
    virtual bool Init(SignalDirection direction, DataSourceI &ds, const char8 *const name, void *gamMem) { \
        bool ret = BaseClass::Init(direction, ds, name, gamMem); \
        if (ret) DebugBrokerHelper::InitSignals(this, ds, service, signalInfoPointers, numSignals, this->copyTable, name, direction); \
        return ret; \
    } \
    virtual bool Init(SignalDirection direction, DataSourceI &ds, const char8 *const name, void *gamMem, const bool optim) { \
        bool ret = BaseClass::Init(direction, ds, name, gamMem, optim); \
        if (ret) DebugBrokerHelper::InitSignals(this, ds, service, signalInfoPointers, numSignals, this->copyTable, name, direction); \
        return ret; \
    } \
}; \
class Debug##BaseClass##Builder : public ObjectBuilder { \
public: \
    virtual Object *Build(HeapI* const heap) const { return new (heap) Debug##BaseClass(); } \
};

#define DECLARE_DEBUG_BROKER_NO_OPTIM(BaseClass) \
class Debug##BaseClass : public BaseClass { \
public: \
    DECLARE_DEBUG_BROKER_COMMON(BaseClass) \
    virtual bool Init(SignalDirection direction, DataSourceI &ds, const char8 *const name, void *gamMem) { \
        bool ret = BaseClass::Init(direction, ds, name, gamMem); \
        if (ret) DebugBrokerHelper::InitSignals(this, ds, service, signalInfoPointers, numSignals, this->copyTable, name, direction); \
        return ret; \
    } \
}; \
class Debug##BaseClass##Builder : public ObjectBuilder { \
public: \
    virtual Object *Build(HeapI* const heap) const { return new (heap) Debug##BaseClass(); } \
};

DECLARE_DEBUG_BROKER(MemoryMapInputBroker)
DECLARE_DEBUG_BROKER(MemoryMapOutputBroker)
DECLARE_DEBUG_BROKER(MemoryMapSynchronisedInputBroker)
DECLARE_DEBUG_BROKER(MemoryMapSynchronisedOutputBroker)
DECLARE_DEBUG_BROKER_NO_OPTIM(MemoryMapInterpolatedInputBroker)
DECLARE_DEBUG_BROKER(MemoryMapMultiBufferInputBroker)
DECLARE_DEBUG_BROKER(MemoryMapMultiBufferOutputBroker)
DECLARE_DEBUG_BROKER(MemoryMapSynchronisedMultiBufferInputBroker)
DECLARE_DEBUG_BROKER(MemoryMapSynchronisedMultiBufferOutputBroker)

}

#endif
