
#include "qgrexrtgsocket.h"

#include "RexExternalChannelFactory.h"


#include <qglogger.h>

#include <QUdpSocket>
#include <QNetworkDatagram>

RexRtgSocket::RexRtgSocket()
{
}

RexRtgSocket::~RexRtgSocket()
{
}

bool RexRtgSocket::send(unsigned int dst, const void* data, unsigned int len)
{
    return false;
}

class QgRexRtgSocket::Implementation
{
public:
    bool connected;

    QUdpSocket autoSocket;
};


QgRexRtgSocket::QgRexRtgSocket(const QString &name, QObject *parent):
   logger("REXSOCKET", this),
   s(parent),
   remotePort(0),
   m_connected(false),
   p_(*new Implementation)
{
    setObjectName(name);
    //s.bind(QHostAddress::LocalHost, 51001);
    //s.setSocketOption(QAbstractSocket::ReceiveBufferSizeSocketOption, 128*1024);
    //s.setSocketOption(QAbstractSocket::SendBufferSizeSocketOption, 128*1024);

    //s.connectToHost(QHostAddress::LocalHost, 50001);

    //connect(&s, &QUdpSocket::readyRead,this, &QgRexRtgSocket::onReceiveData);
    connect(&s, static_cast<void (QUdpSocket::*)(QAbstractSocket::SocketError socketError)>(&QUdpSocket::error),
            this, &QgRexRtgSocket::onError);
}

QgRexRtgSocket::~QgRexRtgSocket()
{
    s.close();
    p_.autoSocket.close();
    delete &p_;
}

bool QgRexRtgSocket::localBind(unsigned int port)
{
    bool ok=s.bind(QgRexNetwork().networkInterface(), port);

    QString localMsg=QString("Local Bind %1:%2").arg(objectName()).arg(port);
    if (ok)
    {
        int rx_size=128*1024;
        int tx_size=60*1024;

        //logger.success(QString("local Bind %1:%2").arg(objectName()).arg(port));
        s.setSocketOption(QAbstractSocket::ReceiveBufferSizeSocketOption, tx_size);
        s.setSocketOption(QAbstractSocket::SendBufferSizeSocketOption, rx_size);
        int rxsize=s.socketOption(QAbstractSocket::ReceiveBufferSizeSocketOption).toInt();
        int txsize=s.socketOption(QAbstractSocket::SendBufferSizeSocketOption).toInt();
        bool sizeok=(rxsize==rx_size) && (txsize==tx_size);
        if (sizeok)
            logger.info(localMsg, QString("rx=%1, tx=%2").arg(rxsize).arg(txsize));
        else
            logger.warning(localMsg, QString("Set size error. RX %1!=%2, TX %3!=%4")
                           .arg(rxsize).arg(rx_size)
                           .arg(txsize).arg(tx_size));
    }
    else
        logger.error(QString("local Bind %1:%2").arg(objectName()).arg(port), s.errorString());

    remoteAddress=QgRexNetwork().networkBroadcast();
    //remotePort=50001;
    return ok;
}

bool QgRexRtgSocket::remoteBind(const QHostAddress& ha, unsigned int port)
{
  QHostAddress a=ha;

  if (ha==QHostAddress::Any)
  {
      a=QgRexNetwork().networkBroadcast();
/*
    quint32 ip=s.localAddress().toIPv4Address();
    if (port==0)
        port=s.localPort();
    ip|=0xFF;   //TODO: depend on subnet mask!
    QHostAddress badr(ip);
    p_.autoSocket.bind(badr, port);
    connect(&p_.autoSocket, &QUdpSocket::readyRead, this,
            [this]()
            {
                QNetworkDatagram d=p_.autoSocket.receiveDatagram();
                remoteAddress=d.senderAddress();
                remotePort=d.senderPort();
                //s.connectToHost(d.senderAddress(), d.senderPort());
                //p_.autoSocket.disconnect(this);
                Q_EMIT connectedToHost(true, &d);
                m_connected=true;
            }

       );
*/
  }
  //else
  {
     if (a==QHostAddress::Null)
     {
         a=QgRexNetwork().networkInterface();
     }
     remoteAddress=a;
     remotePort=port;
     m_connected=true;
  }
  return true;
}

bool QgRexRtgSocket::send(unsigned int dst, const void* data, unsigned int len)
{
    QHostAddress a=remoteAddress;
    if (!m_connected)
    {
        a=QgRexNetwork().networkBroadcast(); //networkInterface();
        //return false;
    }
    //a=QgRexNetwork().networkInterface();
    if (dst==0)
        dst=52001; //remotePort;
    qint64 n=s.writeDatagram(reinterpret_cast<const char*>(data), len, a, /*remotePort*/dst);
    if (n!=len)
        logger.error(QString("send(%1)").arg(len));
    return true;
}

void QgRexRtgSocket::onError(QAbstractSocket::SocketError socketError)
{
    QString msg=QString("p: %1, err=%2 %3").arg(s.localPort()).arg(socketError).arg(s.errorString());
    logger.error(msg);
}

void QgRexRtgSocket::onReceiveData()
{
    while (s.hasPendingDatagrams())
    {
        QNetworkDatagram datagram = s.receiveDatagram();
        RexExternalChannelFactory::dispatcher(datagram.data().constData(), datagram.data().size());
    }
}

QUdpSocket* newSocket()
{
    QUdpSocket* s=new QUdpSocket;

    s->bind(QHostAddress::LocalHost, 51001);
    s->setSocketOption(QAbstractSocket::ReceiveBufferSizeSocketOption, 128*1024);

    return s;
}


bool RexRtgTransport::initialize(unsigned int src_id)
{
    return true;
}

//static bool RexRtgTransport::send(const void* hdr, unsigned int header_size, const void* payload, unsigned int payload_size);
//static bool RexRtgTransport::receive(void* buffer);

RexRtgSocket* RexRtgTransport::globalSocket()
{
    static QgRexRtgSocket gSocket("GSSOCKET");
    gSocket.localBind(0);
    return &gSocket;
}



//

static QHostAddress rexHostAddress;
static QHostAddress rexBroadcastAddress;

QgRexNetwork::QgRexNetwork(QObject* parent):
    QObject(parent)
{
}

QgRexNetwork::~QgRexNetwork()
{
}

void QgRexNetwork::setNetworkInterface(const QHostAddress& adr)
{
   rexHostAddress=adr;
   quint32 ip=adr.toIPv4Address();
   ip|=255;
   rexBroadcastAddress=QHostAddress(ip);
}

QHostAddress QgRexNetwork::networkInterface()
{
    return rexHostAddress;
}

QHostAddress QgRexNetwork::networkBroadcast()
{
    return rexBroadcastAddress;
}

QString QgRexNetwork::networkIP() const
{
    return rexHostAddress.toString();
}

void QgRexNetwork::setNetworkIP(const QString& str)
{
    rexHostAddress=QHostAddress(str);
}

unsigned int QgRexNetwork::networkMTU() const
{
    return 1536;
}
void QgRexNetwork::setNetworkMTU(unsigned int)
{
    return;
}

