#include <QCommandLineParser>
#include <signal.h>
#include <QFile>
#include <QTextStream>
#include <QDateTime>
#include <QJsonDocument>
#include <QJsonObject>
#include <QCoreApplication>
#include <QTimer>
#include <QApplication> // Added for QApplication

#include "hyperiongrabber.h"
#include "HyperionProcessor.h"
#include "wledclient.h"
#include "LedColorMapping.h"
#include "LinearColorSmoothing.h"

// Global pointers for cleanup
static HyperionGrabber *grabber = nullptr;
static QApplication *qapp = nullptr;
static HyperionProcessor *processor = nullptr;
static WledClient *wledClient = nullptr;
static QTimer *processTimer = nullptr;
static QImage currentImage; // Store the latest captured image

// Custom message handler for logging to file
static QFile *logFile = nullptr;
void customMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
    QByteArray localMsg = msg.toLocal8Bit();
    QString logMessage = QString("%1 %2 %3 %4 %5")
        .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"))
        .arg(context.category)
        .arg(context.function)
        .arg(context.line)
        .arg(localMsg.constData());

    switch (type) {
    case QtDebugMsg:
        logMessage = "Debug: " + logMessage;
        break;
    case QtInfoMsg:
        logMessage = "Info: " + logMessage;
        break;
    case QtWarningMsg:
        logMessage = "Warning: " + logMessage;
        break;
    case QtCriticalMsg:
        logMessage = "Critical: " + logMessage;
        break;
    case QtFatalMsg:
        logMessage = "Fatal: " + logMessage;
        break;
    }

    if (logFile && logFile->isOpen()) {
        QTextStream stream(logFile);
        stream << logMessage << Qt::endl;
        stream.flush();
    }

    if (type == QtCriticalMsg || type == QtFatalMsg || !logFile || !logFile->isOpen()) {
        fprintf(stderr, "%s\n", logMessage.toLocal8Bit().constData());
    }
}

// Signal handler for graceful shutdown
static void quit(int)
{
    if (processTimer) {
        processTimer->stop();
        delete processTimer;
        processTimer = nullptr;
    }
    if (grabber != nullptr) {
        delete grabber;
        grabber = nullptr;
    }
    if (processor != nullptr) {
        delete processor;
        processor = nullptr;
    }
    if (wledClient != nullptr) {
        delete wledClient;
        wledClient = nullptr;
    }
    if (logFile) {
        logFile->close();
        delete logFile;
        logFile = nullptr;
    }
    if (qapp != nullptr) {
        qapp->exit();
    }
}

// Slot to receive and store the latest image from HyperionGrabber
void onImageReady(const QImage &image)
{
    currentImage = image;
}

// Slot to process the current image and send to WLED (triggered by timer)
void processCurrentFrame()
{
    if (currentImage.isNull()) {
        qDebug() << "No image available yet for processing.";
        return;
    }

    // Process the image with HyperionProcessor
    QVector<QColor> ledColors = processor->process(currentImage);

    // Send colors to WLED
    wledClient->setLedsColor(ledColors);

    qDebug() << "Processed frame and sent to WLED.";
}

int main(int argc, char *argv[])
{
    // Initialize logging to file
    logFile = new QFile("output.log");
    if (!logFile->open(QFile::WriteOnly | QFile::Text | QFile::Truncate)) {
        fprintf(stderr, "Warning: Could not open log file output.log for writing.\n");
        delete logFile;
        logFile = nullptr;
    }
    qInstallMessageHandler(customMessageOutput);

    qapp = new QApplication(argc, argv);
    signal(SIGINT, quit);
    signal(SIGTERM, quit);

    QApplication::setApplicationName("HyperionGrabber");
    QApplication::setApplicationVersion("0.2");

    // Read configuration from environment variables
    QString wledAddress = qgetenv("HYPERION_GRABBER_WLED_ADDRESS");
    if (wledAddress.isEmpty()) {
        qCritical() << "Error: HYPERION_GRABBER_WLED_ADDRESS environment variable not set.";
        return 1;
    }

    quint16 wledPort = qgetenv("HYPERION_GRABBER_WLED_PORT").toUShort();
    if (wledPort == 0) wledPort = 21324; // Default WLED UDP Realtime port

    QHash<QString, QString> grabberOpts;
    grabberOpts.insert("scale", qgetenv("HYPERION_GRABBER_SCALE").isEmpty() ? "8" : qgetenv("HYPERION_GRABBER_SCALE"));
    grabberOpts.insert("frameskip", qgetenv("HYPERION_GRABBER_FRAMESKIP").isEmpty() ? "0" : qgetenv("HYPERION_GRABBER_FRAMESKIP"));
    grabberOpts.insert("changeThreshold", qgetenv("HYPERION_GRABBER_CHANGE_THRESHOLD").isEmpty() ? "100" : qgetenv("HYPERION_GRABBER_CHANGE_THRESHOLD"));

    LedLayout layout;
    layout.bottom = qgetenv("HYPERION_GRABBER_LEDS_BOTTOM").isEmpty() ? 71 : qgetenv("HYPERION_GRABBER_LEDS_BOTTOM").toInt();
    layout.right = qgetenv("HYPERION_GRABBER_LEDS_RIGHT").isEmpty() ? 20 : qgetenv("HYPERION_GRABBER_LEDS_RIGHT").toInt();
    layout.top = qgetenv("HYPERION_GRABBER_LEDS_TOP").isEmpty() ? 72 : qgetenv("HYPERION_GRABBER_LEDS_TOP").toInt();
    layout.left = qgetenv("HYPERION_GRABBER_LEDS_LEFT").isEmpty() ? 20 : qgetenv("HYPERION_GRABBER_LEDS_LEFT").toInt();

    // Black Border Detector Configuration
    QJsonObject blackBorderDetectorConfig;
    blackBorderDetectorConfig["enable"] = qgetenv("HYPERION_GRABBER_BB_ENABLE").isEmpty() ? true : (qgetenv("HYPERION_GRABBER_BB_ENABLE").toLower() == "true");
    blackBorderDetectorConfig["threshold"] = qgetenv("HYPERION_GRABBER_BB_THRESHOLD").isEmpty() ? 5 : qgetenv("HYPERION_GRABBER_BB_THRESHOLD").toInt();
    blackBorderDetectorConfig["unknownFrameCnt"] = qgetenv("HYPERION_GRABBER_BB_UNKNOWN_FRAME_CNT").isEmpty() ? 600 : qgetenv("HYPERION_GRABBER_BB_UNKNOWN_FRAME_CNT").toInt();
    blackBorderDetectorConfig["borderFrameCnt"] = qgetenv("HYPERION_GRABBER_BB_BORDER_FRAME_CNT").isEmpty() ? 50 : qgetenv("HYPERION_GRABBER_BB_BORDER_FRAME_CNT").toInt();
    blackBorderDetectorConfig["maxInconsistentCnt"] = qgetenv("HYPERION_GRABBER_BB_MAX_INCONSISTENT_CNT").isEmpty() ? 10 : qgetenv("HYPERION_GRABBER_BB_MAX_INCONSISTENT_CNT").toInt();
    blackBorderDetectorConfig["blurRemoveCnt"] = qgetenv("HYPERION_GRABBER_BB_BLUR_REMOVE_CNT").isEmpty() ? 1 : qgetenv("HYPERION_GRABBER_BB_BLUR_REMOVE_CNT").toInt();
    blackBorderDetectorConfig["mode"] = qgetenv("HYPERION_GRABBER_BB_MODE").isEmpty() ? QString("default") : QString(qgetenv("HYPERION_GRABBER_BB_MODE"));

    // Smoothing Configuration
    QJsonObject smoothingConfig;
    smoothingConfig["enable"] = qgetenv("HYPERION_GRABBER_SMOOTH_ENABLE").isEmpty() ? true : (qgetenv("HYPERION_GRABBER_SMOOTH_ENABLE").toLower() == "true");
    smoothingConfig["type"] = qgetenv("HYPERION_GRABBER_SMOOTH_TYPE").isEmpty() ? QString("linear") : QString(qgetenv("HYPERION_GRABBER_SMOOTH_TYPE"));
    smoothingConfig["time_ms"] = qgetenv("HYPERION_GRABBER_SMOOTH_TIME_MS").isEmpty() ? 150 : qgetenv("HYPERION_GRABBER_SMOOTH_TIME_MS").toInt();
    smoothingConfig["updateFrequency"] = qgetenv("HYPERION_GRABBER_SMOOTH_UPDATE_FREQUENCY").isEmpty() ? 25.0 : qgetenv("HYPERION_GRABBER_SMOOTH_UPDATE_FREQUENCY").toDouble();
    smoothingConfig["interpolationRate"] = qgetenv("HYPERION_GRABBER_SMOOTH_INTERPOLATION_RATE").isEmpty() ? 1.0 : qgetenv("HYPERION_GRABBER_SMOOTH_INTERPOLATION_RATE").toDouble();
    smoothingConfig["decay"] = qgetenv("HYPERION_GRABBER_SMOOTH_DECAY").isEmpty() ? 1.0 : qgetenv("HYPERION_GRABBER_SMOOTH_DECAY").toDouble();
    smoothingConfig["dithering"] = qgetenv("HYPERION_GRABBER_SMOOTH_DITHERING").isEmpty() ? true : (qgetenv("HYPERION_GRABBER_SMOOTH_DITHERING").toLower() == "true");
    smoothingConfig["updateDelay"] = qgetenv("HYPERION_GRABBER_SMOOTH_UPDATE_DELAY").isEmpty() ? 0 : qgetenv("HYPERION_GRABBER_SMOOTH_UPDATE_DELAY").toInt();

    QJsonObject processorConfig;
    processorConfig["blackborderdetector"] = blackBorderDetectorConfig;
    processorConfig["smoothing"] = smoothingConfig;
    processorConfig["colorAlgorithm"] = qgetenv("HYPERION_GRABBER_COLOR_ALGORITHM").isEmpty() ? QString("mean_sqrt") : QString(qgetenv("HYPERION_GRABBER_COLOR_ALGORITHM"));
    processorConfig["offset"] = qgetenv("HYPERION_GRABBER_OFFSET").isEmpty() ? 0 : qgetenv("HYPERION_GRABBER_OFFSET").toInt();

    QString wledColorOrder = qgetenv("HYPERION_GRABBER_WLED_COLOR_ORDER").isEmpty() ? QString("GRB") : QString(qgetenv("HYPERION_GRABBER_WLED_COLOR_ORDER"));

    // Instantiate components
    grabber = new HyperionGrabber(grabberOpts);
    processor = new HyperionProcessor(layout, processorConfig);
    wledClient = new WledClient(wledAddress, wledPort, wledColorOrder);

    // Connect grabber to image receiver slot
    QObject::connect(grabber, &HyperionGrabber::imageReady, &onImageReady);

    // Setup timer for processing frames
    processTimer = new QTimer();
    processTimer->setInterval(33); // Process every 1 second
    QObject::connect(processTimer, &QTimer::timeout, &processCurrentFrame);
    processTimer->start();

    qInfo() << "HyperionGrabber started. Sending LED colors to WLED device:" << wledAddress << ":" << wledPort;

    return qapp->exec();
}