#include "wledclient.h"
#include <QDebug>
#include <QColor>
#include <QByteArray>
#include <QtGlobal>

// DDP Port for WLED
const ushort WLED_DDP_PORT = 4048;
// DNRGB Protocol Type
const quint8 DDP_PROTOCOL_DNRGB = 4;
// Max UDP payload size (approx 508 bytes for safe transmission)
// 4 bytes for DDP header, so 504 bytes for LED data
const int MAX_LED_DATA_PER_PACKET = 504; // 504 bytes / 3 bytes per LED = 168 LEDs

WledClient::WledClient(QString host, ushort port, QString colorOrder, QObject *parent) :
    QObject(parent),
    _wledHost(host),
    _wledPort(port),
    _colorOrder(colorOrder)
{
    _udpSocket = new QUdpSocket(this);
    qDebug() << "WledClient initialized for host:" << _wledHost.toString() << "port:" << _wledPort << "color order:" << _colorOrder;
}

WledClient::~WledClient()
{
    _udpSocket->close();
    qDebug() << "WledClient destroyed.";
}

void WledClient::appendColor(QByteArray &datagram, const QColor &color)
{
    if (_colorOrder == "GRB") {
        datagram.append(color.green());
        datagram.append(color.red());
        datagram.append(color.blue());
    } else if (_colorOrder == "BGR") {
        datagram.append(color.blue());
        datagram.append(color.green());
        datagram.append(color.red());
    } else { // Default to RGB
        datagram.append(color.red());
        datagram.append(color.green());
        datagram.append(color.blue());
    }
}

void WledClient::sendImage(const QImage &image)
{
    if (image.isNull()) {
        qWarning() << "WledClient: Cannot send null image.";
        return;
    }

    // Ensure image is in RGB888 format for direct byte access
    QImage rgbImage = image.convertToFormat(QImage::Format_RGB888);

    int totalLeds = rgbImage.width() * rgbImage.height();
    int ledsPerPacket = MAX_LED_DATA_PER_PACKET / 3; // 3 bytes per LED (RGB)

    qDebug() << "WledClient: sendImage - totalLeds:" << totalLeds << "ledsPerPacket:" << ledsPerPacket;

    for (int i = 0; i < totalLeds; i += ledsPerPacket) {
        QByteArray datagram;
        datagram.reserve(4 + (ledsPerPacket * 3)); // Header + max LED data

        // Byte 0: Protocol type (DNRGB)
        datagram.append(DDP_PROTOCOL_DNRGB);
        // Byte 1: Timeout (5 seconds)
        datagram.append(5);

        // Bytes 2 & 3: Starting LED index (low byte, then high byte)
        quint16 startIndex = i;
        datagram.append(startIndex & 0xFF);         // Low byte
        datagram.append((startIndex >> 8) & 0xFF);  // High byte

        int currentLedsInPacket = qMin(ledsPerPacket, totalLeds - i);

        qDebug() << "WledClient: Sending packet (sendImage) - i:" << i << "startIndex:" << startIndex << "currentLedsInPacket:" << currentLedsInPacket;

        for (int j = 0; j < currentLedsInPacket; ++j) {
            int pixelIndex = i + j;
            int x = pixelIndex % rgbImage.width();
            int y = pixelIndex / rgbImage.width();

            QRgb pixel = rgbImage.pixel(x, y);
            appendColor(datagram, QColor(pixel));
        }

        qint64 bytesSent = _udpSocket->writeDatagram(datagram, _wledHost, _wledPort);
        if (bytesSent == -1) {
            qWarning() << "WledClient: Failed to send datagram:" << _udpSocket->errorString();
            emit error(_udpSocket->errorString());
        } else if (bytesSent != datagram.size()) {
            qWarning() << "WledClient: Sent fewer bytes than expected. Expected:" << datagram.size() << "Sent:" << bytesSent;
        }
    }
}

void WledClient::setLedsColor(const QVector<QColor> &colors, int timeout)
{
    if (colors.isEmpty()) {
        return;
    }

    int totalLeds = colors.size();
    int ledsPerPacket = MAX_LED_DATA_PER_PACKET / 3; // 3 bytes per LED (RGB)

    qDebug() << "WledClient: setLedsColor - totalLeds:" << totalLeds << "ledsPerPacket:" << ledsPerPacket;

    for (int i = 0; i < totalLeds; i += ledsPerPacket) {
        QByteArray datagram;
        datagram.reserve(4 + (ledsPerPacket * 3)); // Header + max LED data

        // Byte 0: Protocol type (DNRGB)
        datagram.append(DDP_PROTOCOL_DNRGB);
        // Byte 1: Timeout (in seconds)
        datagram.append(timeout);

        // Bytes 2 & 3: Starting LED index (low byte, then high byte)
        quint16 startIndex = i;
        datagram.append(startIndex & 0xFF);         // Low byte
        datagram.append((startIndex >> 8) & 0xFF);  // High byte

        int currentLedsInPacket = qMin(ledsPerPacket, totalLeds - i);

        qDebug() << "WledClient: Sending packet (setLedsColor) - i:" << i << "startIndex:" << startIndex << "currentLedsInPacket:" << currentLedsInPacket;

        for (int j = 0; j < currentLedsInPacket; ++j) {
            const QColor &color = colors.at(i + j);
            appendColor(datagram, color);
        }

        qint64 bytesSent = _udpSocket->writeDatagram(datagram, _wledHost, _wledPort);
        if (bytesSent == -1) {
            qWarning() << "WledClient: Failed to send datagram for setLedsColor:" << _udpSocket->errorString();
            emit error(_udpSocket->errorString());
        } else if (bytesSent != datagram.size()) {
            qWarning() << "WledClient: Sent fewer bytes than expected for setLedsColor. Expected:" << datagram.size() << "Sent:" << bytesSent;
        }
    }
}

void WledClient::flashLeds(int startIndex, int count, QColor color)
{
    if (count <= 0) {
        qWarning() << "WledClient: Count must be positive for flashLeds.";
        return;
    }

    // Max UDP payload size (approx 508 bytes for safe transmission)
    // 4 bytes for DDP header, so 504 bytes for LED data
    const int MAX_LED_DATA_PER_PACKET = 504; // 504 bytes / 3 bytes per LED (RGB)
    int ledsPerPacket = MAX_LED_DATA_PER_PACKET / 3; // 3 bytes per LED (RGB)

    for (int i = 0; i < count; i += ledsPerPacket) {
        QByteArray datagram;
        datagram.reserve(4 + (ledsPerPacket * 3)); // Header + max LED data

        // Byte 0: Protocol type (DNRGB)
        datagram.append(DDP_PROTOCOL_DNRGB);
        // Byte 1: Timeout (5 seconds)
        datagram.append(5);

        // Bytes 2 & 3: Starting LED index (low byte, then high byte)
        quint16 currentStartIndex = startIndex + i;
        datagram.append(currentStartIndex & 0xFF);         // Low byte
        datagram.append((currentStartIndex >> 8) & 0xFF);  // High byte

        int currentLedsInPacket = qMin(ledsPerPacket, count - i);

        for (int j = 0; j < currentLedsInPacket; ++j) {
            appendColor(datagram, color);
        }

        qDebug() << "WledClient: Sending flashLeds datagram (hex):" << datagram.toHex();
        qDebug() << "WledClient: Datagram size:" << datagram.size();

        qint64 bytesSent = _udpSocket->writeDatagram(datagram, _wledHost, _wledPort);
        if (bytesSent == -1) {
            qWarning() << "WledClient: Failed to send datagram for flashLeds:" << _udpSocket->errorString();
            emit error(_udpSocket->errorString());
        } else if (bytesSent != datagram.size()) {
            qWarning() << "WledClient: Sent fewer bytes than expected for flashLeds. Expected:" << datagram.size() << "Sent:" << bytesSent;
        }
    }
}
