#include "qsacnnode.h"

#include <QDebug>
#include <QNetworkDatagram>
#include <QNetworkInterface>


/**
 * @brief QSacnNode::QSacnNode
 * @param parent
 * @param cid
 */
QSacnNode::QSacnNode(QObject *parent, QUuid cid)
    : Component(UUID::uuid(cid.toString().toStdString()))
    , QUdpSocket(parent)
{
    fctn_ = "OpenLCP QSacnNode";

    bind(QHostAddress::AnyIPv4, SACN::ACN_SDT_MULTICAST_PORT);

    connect(this, &QUdpSocket::readyRead,
            this, &QSacnNode::udpReceive);

    onDiscovered(std::bind(&QSacnNode::emitFound, this,
                           std::placeholders::_1));
}


/**
 * @brief QSacnNode::subscribe
 * @param num
 */
void QSacnNode::subscribe(const uint16_t num)
{
  if (Receiver::universe(num))  // already subscribed
    return;
  if (num != SACN::E131_DISCOVERY_UNIVERSE) {
    qDebug() << "Subscribing to universe" << QString::number(num);
    Receiver::subscribe(num);
    universes_.insert(num, new QSacnUniverse(this, Receiver::universe(num)));
  }
  for (QNetworkInterface &iface : QNetworkInterface::allInterfaces())
    joinMulticastGroup(IPv4MulticastAddress(num), iface);
}


/**
 * @brief QSacnNode::unsubscribe
 * @param num
 */
void QSacnNode::unsubscribe(const uint16_t num)
{
  for (QNetworkInterface &iface : QNetworkInterface::allInterfaces())
    leaveMulticastGroup(IPv4MulticastAddress(num), iface);

  if (Receiver::universe(num)) {
    qDebug() << "Unsubscribing from universe " << QString::number(num);
    delete universes_.take(num);
    Receiver::unsubscribe(num);
  }
}


/**
 * @brief QSacnNode::UdpPayloadReceiver
 */
void QSacnNode::udpReceive()
{
    while (hasPendingDatagrams()) {
        QNetworkDatagram datagram = receiveDatagram();
        // Expecting IANA registered Session Data Transport traffic
        if (datagram.destinationPort() != SACN::ACN_SDT_MULTICAST_PORT)
          return;

        // wrap a PDU io stream around the QNetworkDatagram data buffer
        ACN::PDU::Stream stream(new ACN::PDU::pdu_stream(
                                   (uint8_t*)datagram.data().constData(),
                                   datagram.data().length()));
        UdpPayloadReceiver(stream);
    }
}


/**
 * @brief QSacnNode::emitFound
 * @param univ
 *
 *  wrap data change callback to a Qt signal
 */
void QSacnNode::emitFound(
        std::shared_ptr<SACN::EXTENDED::DISCOVERY::discoveredUniverse> univ)
{
    qDebug() << "found universe " << QString::number(univ->universe);
    emit foundUniverse(univ);
}

