#include "hgx11.h"
#include <QCoreApplication>
#include <QScreen>
#include <QGuiApplication>
#include <QBuffer>
#include <QImage>

// public

HyperionGrabber::HyperionGrabber(QHash<QString, QString> opts)
{
    QString addr = "localhost";
    unsigned short port = 19444;
    unsigned short scale = 8;
    unsigned short priority = 100;
    QString redAdjust = "", greenAdjust = "", blueAdjust = "";
    QString temperature = "", threshold = "", transform = "";

    _scale_m = scale;

    QHashIterator<QString, QString> i(opts);
    while (i.hasNext()) {
        i.next();
        if ((i.key() == "a" || i.key() == "address") && !(i.value().isNull() && i.value().isEmpty())) {
            addr = i.value();
        } else if ((i.key() == "p" || i.key() == "port") && i.value().toUShort()) {
            port = i.value().toUShort();
        } else if ((i.key() == "c" || i.key() == "priority") && (i.value().toUShort() && i.value().toUShort() <= 255)) {
            priority = i.value().toUShort();
        } else if ((i.key() == "s" || i.key() == "scale") && i.value().toUShort()) {
            scale = i.value().toUShort();
        } else if ((i.key() == "i" || i.key() == "inactive") && i.value().toInt()) {
            _inactiveTime_m = (i.value().toInt() * 1000);
        } else if (i.key() == "r" || i.key() == "redadjust") {
            redAdjust = _parseColorArr(i.value(), 1);
        } else if (i.key() == "g" || i.key() == "greenadjust") {
            greenAdjust = _parseColorArr(i.value(), 1);
        } else if (i.key() == "b" || i.key() == "blueadjust") {
            blueAdjust = _parseColorArr(i.value(), 1);
        } else if (i.key() == "t" || i.key() == "temperature") {
            temperature = _parseColorArr(i.value(), 1);
        } else if (i.key() == "d" || i.key() == "threshold") {
            threshold = _parseColorArr(i.value(), 0);
        } else if ((i.key() == "l" || i.key() == "transform") && _parseColorArr(i.value(), 0) != "") {
            transform = i.value();
        }
    }

    _hyperionPriority_m = QString::number(priority);
    _hclient_p = new hgx11net(addr, port, _hyperionPriority_m);
    _hclient_p->ledAdjustments(redAdjust, greenAdjust, blueAdjust, temperature, threshold, transform);

    _waylandGrabber_p = new WaylandGrabber(this);
    connect(_waylandGrabber_p, &WaylandGrabber::frameReady, this, &HyperionGrabber::_processFrame);
    _waylandGrabber_p->start();

    QScreen *screen = QGuiApplication::primaryScreen();
    if (screen) {
        _setImgSize(screen->size().width() / scale, screen->size().height() / scale);
    } else {
        qWarning() << "Could not get primary screen size for Wayland grabber.";
        _setImgSize(1920 / scale, 1080 / scale); // Default to 1080p if screen not found
    }

    if (_inactiveTime_m) {
        _timer_p = new QTimer(this);
        connect(_timer_p, &QTimer::timeout, this, &HyperionGrabber::_inActivity);
        _timer_p->start(_inactiveTime_m);
    }
}

HyperionGrabber::~HyperionGrabber()
{
    if (_timer_p) {
        _timer_p->stop();
        delete _timer_p;
    }
    delete _hclient_p;
}

// private

QString HyperionGrabber::_parseColorArr(QString value, bool isInt)
{
    QStringList values = value.split(',');
    if (values.size() != 3) {
        return "";
    }
    value = "[";
    for (int i = 0; i < 3; i++) {
        if (isInt && (values.at(i).toInt() < 0 || values.at(i).toInt() > 255)) {
            return "";
        }
        if (!isInt && (values.at(i).toDouble() < 0.0 || values.at(i).toDouble() > 1.0)) {
            return "";
        }
        value.append(values.at(i));
        value.append(",");
    }
    value.chop(1);
    value.append("]");
    return value;
}

// private slots

void HyperionGrabber::_inActivity()
{
    _hclient_p->clearLeds();
}

void HyperionGrabber::_processFrame(const QImage &frame)
{
    QImage scaledImage = frame.scaled(frame.width() / _scale_m, frame.height() / _scale_m, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);

    QByteArray ba;
    QBuffer buffer(&ba);
    buffer.open(QIODevice::WriteOnly);
    scaledImage.save(&buffer, "PNG"); // Save as PNG for now, can be optimized later
    _hclient_p->sendImage(QString(ba.toBase64()));
}

void HyperionGrabber::_setImgSize(int width, int height)
{
    _hclient_p->imgCmdBuf.clear();
    _hclient_p->imgCmdBuf.append("{\"command\":\"image\",\"priority\":").append(_hyperionPriority_m.toUtf8()).append(",\"imageheight\":");
    _hclient_p->imgCmdBuf.append(QString::number(height).toUtf8());
    _hclient_p->imgCmdBuf.append(",\"imagewidth\":");
    _hclient_p->imgCmdBuf.append(QString::number(width).toUtf8());
    _hclient_p->imgCmdBuf.append(",\"imagedata\":\"");
}
