#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);
}


/**
 * @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);
    }
}


void QSacnNode::rlpSend(const ACN::PDU::Stream stream,
                        const ACN::SDT::UDP::ipAddress& ip)
{
  QHostAddress addr;
  switch (ip.type) {
    case ACN::SDT::SDT_ADDR_IPV4:
      addr = QHostAddress(ip.address.ipv4.value);
      break;
    case ACN::SDT::SDT_ADDR_IPV6:
      return;
    default:
      return;
    }

  writeDatagram(reinterpret_cast<char*>(stream->base()), stream->size(),
                addr, ip.port);
}
