
#include "RexExternalChannelFactory.h"

#include "RexIExternalChannel.h"
//#include "RexExternalChannelId.h"

//#include "rexrtgsocket.h"
#include "qgrexsocket.h"

#include "qgscopechannel.h"
#include "qgscopeworkbench.h"

#include <string.h>

//#include <winsock2.h>
//#include <Windows.h>

//#include "rtlog.h"
//#include "bsk_stdio.h"

//#include "mrk.h"
//#include "mrk_types.h"

#include <stdint.h>

#include "rex_udpmsg.h"

#include "grifo_inet_ports.h"

const char LID[]="REXSOCK";

enum { mailbox_number = 256 };

class ConcreteChannel: public QgScopeChannel, public RexIExternalChannel
{
public:
    QgRexSocket* s;

    struct generic_payload_t
    {
        uint8_t data[4*1024];
    };

    RexUdpMessageT<generic_payload_t> msg;

    ConcreteChannel(QgRexSocket* s_, const RexExternalChannelId& id, RexIEternalChannelReceiver* receiver=0):
        QgScopeChannel(id.name()),
        RexIExternalChannel(id),
        s(s_)
    {
        memset(&msg, 0, sizeof msg);
        msg.header.marker_value='!';
        msg.header.fmt=0;
        msg.header.op=0;
        msg.header.mailbox=63;
        msg.header.extsize_bytes=0;
        msg.header.payload_size_bytes=0;
        msg.header.src=250;
        msg.header.dst=1;

        if (receiver)
            attachReceiver(*receiver);

        QgScopeWorkbench::instance().addChannel(this);
    }

    void receive(void * rx_buffer, int max_len_bytes) override
    {

    }

    //## operation send(void *,int)
    void send(const void * tx_data, int tx_len_bytes) override
    {
        memcpy(msg.payload.data, tx_data, tx_len_bytes);
        msg.header.payload_size_bytes=tx_len_bytes;
        //s->send(0, &msg, tx_len_bytes+sizeof msg.header );
        s->sendMessage(msg.header.dst, msg.header.mailbox, tx_data, tx_len_bytes);
/*
        sockaddr_in service;
        service.sin_family = AF_INET;
        service.sin_addr.s_addr = inet_addr("127.0.0.1");
        service.sin_port = htons(50001);

        sendto(s, "PROVA-PROVA", 10,  0, (SOCKADDR *) &service, sizeof service);
*/
    }

    virtual void sendTo(int port, const void* tx_data, int tx_len_bytes) override
    {
        memcpy(msg.payload.data, tx_data, tx_len_bytes);
        msg.header.payload_size_bytes=tx_len_bytes;
        msg.header.total_message_size=tx_len_bytes;

        s->sendMessage(msg.header.dst, msg.header.mailbox, tx_data, tx_len_bytes);

        //s->send(0, &msg, tx_len_bytes+sizeof msg.header );
        //s->send(0, tx_data, tx_len_bytes);
    }

    virtual void dispatch(const void* data, unsigned long size_bytes, const time_t& timetag) override
    {
        RexIExternalChannel::dispatch(data, size_bytes, timetag);
    }


};

struct MailboxMap
{
    ConcreteChannel* ch;
};

class DummyChannel: public RexIExternalChannel
{
public:
    virtual void receive(void * rx_buffer, int max_len_bytes) override
    {
    }
    virtual void send(const void* tx_data, int tx_len_bytes) override
    {
    }
    virtual void sendTo(int port, const void* tx_data, int tx_len_bytes) override
    {
    }

};
static DummyChannel dummyChannel;

static MailboxMap mailboxMap[mailbox_number];



void RexExternalChannelFactory::initialize()
{
    //WinTransportLayer::Initialize();
    //RexRtgTransport::initialize(0);
}


static QgRexSocket* globalSocket;

RexIExternalChannel& RexExternalChannelFactory::createChannel(const RexExternalChannelId& id)
{
    //RexRtgTransport::initialize(0);
    if (globalSocket==0)
    {
        globalSocket=new QgRexSocket;
        globalSocket->setId(IidNodeId::TIMER+2, "T2");
        globalSocket->localBind(grifo_inet::rex_corefpga+1);
    }
    //WinTransportLayer::Initialize();
    unsigned int i=id.id();
    if (i>mailbox_number)
    {
        //rtlog_printf(LID, rtlog_fatal|rtlog_breaking_news, "createChannel(%d,%s): invalid mailbox", i, id.name());
        return dummyChannel;
    }
    if (mailboxMap[i].ch)
    {
        //rtlog_printf(LID, rtlog_error|rtlog_breaking_news, "createChannel(%d,%s): duplicated channel mailbox", i, id.name());
        return dummyChannel;
    }

    ConcreteChannel* ch=new ConcreteChannel(globalSocket, id); //RexRtgTransport::globalSocket(), id);

#if 0
    if (!ch)
        rtlog_printf(LID, rtlog_fatal|rtlog_breaking_news, "createChannel(%d,%s): allocation failed", i, id.name());
    else
        rtlog_printf(LID, rtlog_success|rtlog_breaking_news, "createChannel(%d,%s): ok", i, id.name());
#endif
    mailboxMap[i].ch=ch;

    return *ch;
}

RexIExternalChannel& RexExternalChannelFactory::createDebugChannel(int port)
{
/*
    WinTransportLayer::Initialize();
    RexExternalChannelId id(1000+port, "DBG");

    UdpSocket* sk=new UdpSocket;
    sk->create(port);
    ConcreteChannel* ch=new ConcreteChannel(sk->sk, id);
    return *ch;
*/
    RexExternalChannelId id(63, "DBG");

    return createChannel(id);
}

void RexExternalChannelFactory::mapChannel(mailbox_id_t mailbox, const RexExternalChannelId& id)
{
}


void RexExternalChannelFactory::dispatcher(const void* data, unsigned int size)
{
    //rtlog_const_message(LID, rtlog_warning|rtlog_breaking_news, "Listener running...");

    //errors=0;
    RexBatchTag btt;
    const RexUdpMessage::RexMsgDummyMessage* pkt=reinterpret_cast<const RexUdpMessage::RexMsgDummyMessage*>(data);

    if (pkt->header.marker_value=='!')
    {
        unsigned int psize=size<sizeof *pkt ? 0 : pkt->header.payload_size_bytes;
        unsigned int exp_size=psize+sizeof *pkt;
        if (size>=exp_size)
        {
            if (mailboxMap[pkt->header.mailbox].ch)
            {
                mailboxMap[pkt->header.mailbox].ch->dispatch(&(pkt->payload[0]), pkt->header.payload_size_bytes, btt);
            }
        }
        else
        {
            //rtlog_printf(LID, rtlog_trace|rtlog_breaking_news, "recvfrom()=%u: invalid size, expected %u+%u=%u", size,  exp_size-psize, psize, exp_size);
        }
    }
    else
    {
        //rtlog_printf(LID, rtlog_trace|rtlog_breaking_news, "recvfrom()=%u: invalid marker %u", size, pkt->marker);
    }
}


////
RexExternalChannelId::RexExternalChannelId(const id_t& id, const char* name):
       m_id(id),
        m_name(name)
{
}

RexExternalChannelId::RexExternalChannelId():
        m_id(0),
        m_name(0)
{
}

    //## auto_generated
    RexExternalChannelId::~RexExternalChannelId()
    {
    }

    ////    Operations    ////

    //## operation id() const
    RexExternalChannelId::id_t RexExternalChannelId::id() const
    {
        return m_id;
    }

    //## operation name() const
    const char* RexExternalChannelId::name() const
    {
        return m_name;
    }

    ////    Additional operations    ////


    //## auto_generated
    void RexExternalChannelId::setM_name(const char* p_m_name)
    {
        m_name=p_m_name;
    }
