#include "mainwindow.h"
#include <QVBoxLayout>
#include <QDir>
#include <QElapsedTimer>
#include "appcontext.h"
#include "canvas.h"
#include "logger.h"
#include "utils.h"
#include "abstractwidget.h"
#include <QSettings>
#include <QDateTime>
#include <time.h>
#include "defs.h"

MainWindow::MainWindow(const int &frequency, QWidget *parent)
    : QMainWindow(parent)
    , m_pStackedWidget(nullptr)
    , m_pErrStrLabel(nullptr)
    , m_broadcast(Broadcast(BROADCAST_PORT))
    , m_frequency(frequency)
    , m_packageCount("0")
{
    setWindowFlags(Qt::FramelessWindowHint | Qt::Window);
    init_ui();

    Network &network = AppContext::instance().network();
    connect(&network, &Network::updateScreen, this, &MainWindow::onUpdateScreen);

    SerialPort &serialPort = AppContext::instance().serialPort();
    connect(&serialPort, &SerialPort::pageSwitch, this, &MainWindow::onPageSwitdh);

    HttpServer &httpServer = AppContext::instance().httpServer();
    connect(&httpServer, &HttpServer::monitorConfigureUpdate, this, &MainWindow::onConfigureUpdate);
    connect(&httpServer, &HttpServer::reloadCanvas, this, &MainWindow::onReloadCanvas);

    DataFifo &dataFifo = AppContext::instance().dataFifo();
    connect(&dataFifo, &DataFifo::updateScreen, this, &MainWindow::onUpdateScreen);

    connect(&m_reportTimer, &QTimer::timeout, this, &MainWindow::onReport);
    m_reportTimer.start(1000);

    connect(&m_timer, &QTimer::timeout, this, [this](){
        DataFifo &dataFifo = AppContext::instance().dataFifo();
        qint64 recvTimestamp = dataFifo.recvTimestamp();
        qint64 timestamp = QDateTime::currentMSecsSinceEpoch();
        if((timestamp - recvTimestamp) >= 5000) {
            emit disconnect();
            LOG_WARN("data transfer interrupted");
            if(!m_pErrStrLabel) {
                QFont font;
                font.setPixelSize(40);
                font.setBold(true);
                m_pErrStrLabel = new QLabel("数据传输中断！", this);
                m_pErrStrLabel->resize(this->size());
                m_pErrStrLabel->setFont(font);
                m_pErrStrLabel->setAlignment(Qt::AlignCenter);
                m_pErrStrLabel->setStyleSheet("QLabel { background-color: #FFD6D6; color: #D8000C; padding: 4px; border-radius: 4px; }");
                m_pErrStrLabel->move((this->width() - m_pErrStrLabel->width()) / 2, (this->height() - m_pErrStrLabel->height()) / 2);
                m_pErrStrLabel->show();
            }
        } else {
            if(m_pErrStrLabel) {
                delete m_pErrStrLabel;
                m_pErrStrLabel = nullptr;
            }
        }

        // LOG_INFO("{} {}", timestamp, recvTimestamp);
    });

    m_timer.start(5000);

    // connect(&m_updateScreenTimer, &QTimer::timeout, this, &MainWindow::onUpdateScreen);
    // m_updateScreenTimer.start(1000 / m_frequency);
}

MainWindow::~MainWindow()
{

}

void MainWindow::init_ui()
{
    QWidget *pCentralWidget = new QWidget(this);
    setCentralWidget(pCentralWidget);
    QVBoxLayout *pMainLayout = new QVBoxLayout(pCentralWidget);
    m_pStackedWidget = new QStackedWidget(pCentralWidget);
    pMainLayout->addWidget(m_pStackedWidget);
    pMainLayout->setSpacing(0);
    pMainLayout->setContentsMargins(0, 0, 0, 0);

    if(reload()) {
        setCurrentCanvas();
    }
}

bool MainWindow::reload()
{
    ProjectManager &projectMgr = AppContext::instance().projectManager();
    QString fileName = QString("%1/%2").arg(QDir::currentPath(), "conf/monitor.xml");
    bool result = projectMgr.openProject(fileName);
    if(result) {
        LOG_INFO("open monitor config xml success: {}, canvas count: {}", fileName.toUtf8().data(), projectMgr.canvasCount());
    } else {
        LOG_ERROR("open monitor config xml failed: {}", fileName.toUtf8().data());
        return false;
    }

    LOG_INFO("start reload monitor configure");
    int canvasCount = projectMgr.canvasCount();
    for(int i = 0; i < canvasCount; i++)
    {
        LOG_DEBUG("----------------canvas {}----------------", i);
        Canvas *pCanvas = new Canvas(projectMgr.canvas(i), m_pStackedWidget);
        pCanvas->resize(1280, 720);
        m_pStackedWidget->insertWidget(i, pCanvas);
    }
    LOG_INFO("reload monitor configure complete");
    return true;
}

void MainWindow::clear()
{
    LOG_INFO("begin release exist canvas");
    {
        QList<QWidget*> widgets;
        for (int i = 0; i < m_pStackedWidget->count(); ++i) {
            widgets << m_pStackedWidget->widget(i);
        }

        for (QWidget *w : widgets) {
            Canvas *pCanvas = static_cast<Canvas *>(w);
            pCanvas->clear();
            m_pStackedWidget->removeWidget(w);
            delete w;
        }
    }
    ProjectManager &projectMgr = AppContext::instance().projectManager();
    projectMgr.closeProject();
    LOG_INFO("release exist canvas complete, widget count: {}", m_pStackedWidget->count());
}

void MainWindow::setCurrentCanvas(bool isNewComfigure)
{
    int index = 0;
    if(isNewComfigure) {
        int canvasCount = m_pStackedWidget->count();
        for(int i = 0; i < canvasCount; i++)
        {
            QWidget *pWidget = m_pStackedWidget->widget(i);
            Canvas *pCanvas = static_cast<Canvas *>(pWidget);
            if(pCanvas->isDefault()) {
                index = i;
                break;
            }
        }
    } else {
        QSettings settings("storage/conf/settings.ini", QSettings::IniFormat);
        settings.setIniCodec("UTF-8");
        settings.beginGroup("CANVAS");
        if(settings.contains("INDEX")) {
            index = settings.value("INDEX").toInt();
            LOG_INFO("find saved canvas index, set index {}", index);
        } else {
            LOG_INFO("not find saved canvas index, set index 0");
            settings.setValue("INDEX", 0);
        }
        settings.endGroup();
    }

    m_pStackedWidget->setCurrentIndex(index);
    ProjectManager &projectMgr = AppContext::instance().projectManager();
    projectMgr.setCurrentCanvasIndex(index);
}

void MainWindow::onUpdateScreen(const QJsonObject &obj)
{
    // QElapsedTimer timer;
    // timer.start(); // 开始计时
    LOG_DEBUG("update screen");

    m_packageCount = obj["pkg_index"].toString();

    ProjectManager &projectMgr = AppContext::instance().projectManager();
    // DataDict &dataDict = AppContext::instance().dataDict();
    // const QJsonObject obj = dataDict.data();

    int canvasCount = projectMgr.canvasCount();
    QString timestamp = obj["timestamp"].toString();
    LOG_DEBUG("timestamp {}", timestamp.toStdString());
    timestamp = QString("%1").arg(timestamp.toULongLong() / 1000000);
    QList<QWidget *> widgets;
    QList<QStringList> parameters;

    for(int i = 0; i < canvasCount; i++)
    {
        Canvas *pCanvas = static_cast<Canvas *>(m_pStackedWidget->widget(i));
        if(!pCanvas) {
            LOG_ERROR("error current canvas");
            return;
        }

        widgets = pCanvas->widgetList();
        parameters = pCanvas->parameterList();

        if(widgets.count() != parameters.count()) {
            LOG_ERROR("error widget count and parameters count");
            return;
        }

        int count = widgets.count();

        for(int j = 0; j < count; j++)
        {
            AbstractWidget *pAbstractWidget = static_cast<AbstractWidget *>(widgets[j]);
            QStringList widgetParameters;
            QMetaObject::invokeMethod(pAbstractWidget, "parameters",
                                        Qt::AutoConnection, Q_RETURN_ARG(QStringList, widgetParameters));

            for(auto parameter : widgetParameters)
            {
                if(obj.contains(parameter)) {
                    QMetaObject::invokeMethod(
                        pAbstractWidget,
                        "setValue",
                        Qt::AutoConnection,
                        Q_ARG(QString, parameter),
                        Q_ARG(QVariant, obj[parameter].toVariant()),
                        Q_ARG(QString, timestamp));
                }
            }

            if(pCanvas == m_pStackedWidget->currentWidget()) {
                QMetaObject::invokeMethod(pAbstractWidget, "redraw", Qt::AutoConnection);
            }
        }

    }

    // qint64 duration = timer.elapsed(); // 获取经过的毫秒数

    qint64 startTime = obj["time_arrived"].toString().toLongLong();
    qint64 endTime = Utils::currentNanoTimestamp();
    LOG_DEBUG("startTime: {}, endTime: {}", startTime, endTime);
    LOG_DEBUG("update screen duration: {}ns", endTime - startTime);
}

void MainWindow::onPageSwitdh(int state)
{
    ProjectManager &projectMgr = AppContext::instance().projectManager();
    int canvasCount = projectMgr.canvasCount();
    if (canvasCount <= 0)
        return;

    int currentIndex = m_pStackedWidget->currentIndex();
    int nextIndex = currentIndex;

    if (state < 0) {
        // 上一页
        nextIndex = (currentIndex - 1 + canvasCount) % canvasCount;
        LOG_INFO("page up: {}", nextIndex);
    } else if (state > 0) {
        // 下一页
        nextIndex = (currentIndex + 1) % canvasCount;
        LOG_INFO("page down: {}", nextIndex);
    } else {
        return; // state == 0 不翻页
    }

    m_pStackedWidget->setCurrentIndex(nextIndex);
    projectMgr.setCurrentCanvasIndex(nextIndex);

    QSettings settings("storage/conf/settings.ini", QSettings::IniFormat);
    settings.setIniCodec("UTF-8");
    settings.beginGroup("CANVAS");
    settings.setValue("INDEX", nextIndex);
    settings.endGroup();
}

void MainWindow::onConfigureUpdate(const QString &filename)
{
    clear();

    QString srcFileName = filename;
    QString dstFileName = QString("%1/%2").arg(QDir::currentPath(), "conf/monitor.xml");
    bool ret;

    LOG_INFO("begin delete current monitor configure...");
    QFile dstFile = QFile(dstFileName);
    if(dstFile.exists())
    {
        ret = dstFile.remove();
        if(ret) {
            LOG_INFO("remove current monitor configure successfully");
        } else {
            LOG_ERROR("remove current monitor configure failed");
            return;
        }
    } else {
        QFileInfo fileInfo(dstFileName);
        QDir dir = fileInfo.absoluteDir();
        if(!dir.exists())
            dir.mkpath(dir.path());
    }

    LOG_INFO("begin copy new monitor configure...");
    QFile srcFile = QFile(srcFileName);
    if(srcFile.exists()) {
        ret = QFile::copy(srcFileName, dstFileName);
        if(ret) {
            LOG_INFO("update monitor configure successfully");
        } else {
            LOG_ERROR("update monitor configure failed");
            return;
        }
    } else {
        LOG_ERROR("update monitor configure file not exist");
        return;
    }


    if(reload()) {
        setCurrentCanvas(true);
    }

    LOG_INFO("update: default canvas index is {}", m_pStackedWidget->currentIndex());
    QSettings settings("storage/conf/settings.ini", QSettings::IniFormat);
    settings.setIniCodec("UTF-8");
    settings.beginGroup("CANVAS");
    settings.setValue("INDEX", m_pStackedWidget->currentIndex());
    settings.endGroup();
}

void MainWindow::onReloadCanvas()
{
    // reloadCanvas();
    clear();
    if(reload()) {
        setCurrentCanvas();
    }
}

void MainWindow::onReport()
{
    ProjectManager &projectMgr = AppContext::instance().projectManager();
    QJsonObject obj = AppContext::instance().sysState().sysStateInfo();
    obj.insert("curcanvas", projectMgr.currentCanvasIndex());
    obj.insert("canvascount", projectMgr.canvasCount());
    obj.insert("parametercount", 0);
    obj.insert("packageCount", m_packageCount);

    if(projectMgr.canvasCount() > 0) {
        QWidget *pWidget = m_pStackedWidget->currentWidget();
        Canvas *pCanvas = static_cast<Canvas *>(pWidget);
        if(pCanvas) {
            obj["parametercount"] = pCanvas->parameterCount();
        }
    }

    m_broadcast.write(obj);
}
