#ifndef DEBUGSERVICE_H
#define DEBUGSERVICE_H

#include "MessageI.h"
#include "StreamString.h"
#include "BasicUDPSocket.h"
#include "BasicTCPSocket.h"
#include "ReferenceContainer.h"
#include "SingleThreadService.h"
#include "EmbeddedServiceMethodBinderI.h"
#include "Object.h"
#include "DebugCore.h"

namespace MARTe {

struct SignalAlias {
    StreamString name;
    uint32 signalIndex;
};

class DebugService : public ReferenceContainer, public MessageI, public EmbeddedServiceMethodBinderI {
public:
    CLASS_REGISTER_DECLARATION()

    DebugService();
    virtual ~DebugService();

    virtual bool Initialise(StructuredDataI & data);

    DebugSignalInfo* RegisterSignal(void* memoryAddress, TypeDescriptor type, const char8* name);
    void ProcessSignal(DebugSignalInfo* signalInfo, uint32 size);

    virtual ErrorManagement::ErrorType Execute(ExecutionInfo & info);

    bool IsPaused() const { return isPaused; }
    void SetPaused(bool paused) { isPaused = paused; }

    static bool GetFullObjectName(const Object &obj, StreamString &fullPath);

    uint32 ForceSignal(const char8* name, const char8* valueStr);
    uint32 UnforceSignal(const char8* name);
    uint32 TraceSignal(const char8* name, bool enable, uint32 decimation = 1);
    void Discover(BasicTCPSocket *client);
    void ListNodes(const char8* path, BasicTCPSocket *client);
    void InfoNode(const char8* path, BasicTCPSocket *client);

private:
    void HandleCommand(StreamString cmd, BasicTCPSocket *client);
    
    uint32 ExportTree(ReferenceContainer *container, StreamString &json);
    void PatchRegistry();
    
    ErrorManagement::ErrorType Server(ExecutionInfo & info);
    ErrorManagement::ErrorType Streamer(ExecutionInfo & info);

    uint16 controlPort;
    uint16 streamPort;
    StreamString streamIP;
    bool isServer; 
    bool suppressTimeoutLogs;
    volatile bool isPaused;

    BasicTCPSocket tcpServer;
    BasicUDPSocket udpSocket;
    
    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;

    static const uint32 MAX_SIGNALS = 4096;
    DebugSignalInfo signals[MAX_SIGNALS];
    uint32 numberOfSignals;

    static const uint32 MAX_ALIASES = 8192;
    SignalAlias aliases[MAX_ALIASES];
    uint32 numberOfAliases;

    FastPollingMutexSem mutex;
    TraceRingBuffer traceBuffer;

    static const uint32 MAX_CLIENTS = 16;
    BasicTCPSocket* activeClients[MAX_CLIENTS];
    FastPollingMutexSem clientsMutex;

    static DebugService* instance;
};

}

#endif
