#include "Widget.h"
#include "BoostLog.h"
#include "CdcUpdater.h"
#include "DeviceDiscovery.h"
#include "StringUtility.h"
#include <QDropEvent>
#include <QFileDialog>
#include <QHBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QMessageBox>
#include <QMimeData>
#include <QProgressBar>
#include <QPushButton>
#include <QSerialPortInfo>
#include <QTimer>
#include <QVBoxLayout>
#include <filesystem>

Widget::Widget(QWidget *parent) : QWidget(parent) {
    m_fileEditor = new QLineEdit();
    m_fileEditor->setPlaceholderText("请选择升级文件或将文件拖入工具中");

    m_selectButton = new QPushButton("选择");
    connect(m_selectButton, &QPushButton::clicked, this, &Widget::onSelectButtonClicked);

    auto fileLayout = new QHBoxLayout();
    fileLayout->addWidget(m_fileEditor);
    fileLayout->addWidget(m_selectButton);

    m_startButton = new QPushButton(this);
    m_startButton->setText("开始");
    connect(m_startButton, &QPushButton::clicked, this, &Widget::start);

    m_progressBar = new QProgressBar();
    m_progressBar->setRange(0, 100);
    m_progressBar->setValue(0);

    m_messageLabel = new QLabel();
    m_messageLabel->setText("选择升级文件,点击开始按钮升级模组");

    auto statusLayout = new QVBoxLayout();
    statusLayout->addWidget(m_progressBar);
    statusLayout->addWidget(m_messageLabel);

    auto controlLayout = new QHBoxLayout();
    controlLayout->addLayout(statusLayout);
    controlLayout->addWidget(m_startButton);

    auto layout = new QVBoxLayout(this);
    layout->addLayout(fileLayout);
    layout->addLayout(controlLayout);

    setAcceptDrops(true);
}

Widget::~Widget() {
}

void Widget::start() {
    m_progressBar->setValue(0);
    auto filePath = m_fileEditor->text();
#ifdef Q_OS_WIN
    auto path = Amass::StringUtility::UTF8ToGBK(filePath.toStdString());
#else
    auto path = filePath.toStdString();
#endif
    if (!std::filesystem::exists(path)) {
        QMessageBox::warning(this, "升级", "升级文件不存在!");
        return;
    }
    auto device = CdcUpdater::searchDevice();
    if (device) {
        LOG(info) << "device already in ota mode.";
    } else {
        auto discovery = std::make_shared<DeviceDiscovery>();

        std::error_code error;
        setMessage("尝试发现设备......");
        auto device = discovery->find(DeviceDiscovery::DeviceName, error);
        if (!device) {
            QMessageBox::warning(this, "升级", "未检测到模组,请尝试重新插入模组!");
            return;
        }
        setMessage("发现设备成功,进入BOOT模式......");
        discovery->enterOtaMode(device, error);
    }
    m_updater = std::make_shared<CdcUpdater>();
    connect(m_updater.get(), &CdcUpdater::deviceDiscovered, this, &Widget::onCdcDeviceDiscovered);
    connect(m_updater.get(), &CdcUpdater::updateFinished, this, &Widget::onUpdateFinished);
    connect(m_updater.get(), &CdcUpdater::progressChanged, this, &Widget::setProgress);
    connect(m_updater.get(), &CdcUpdater::message, this, &Widget::setMessage);
    m_updater->start(filePath, device ? *device : QSerialPortInfo());
    setControlsEnabled(false);
}

void Widget::onCdcDeviceDiscovered(const QSerialPortInfo &info) {
    auto status = m_updater->open(info);
    LOG(info) << "open cdc port: " << info.portName().toStdString() << ", status: " << status;
    if (!status) {
        QTimer::singleShot(0, this, [this]() { m_updater->startSearchDevice(); });
    }
}

void Widget::onSelectButtonClicked() {
    auto fileName = QFileDialog::getOpenFileName(this, "升级文件", "", tr("OTA文件 (*.Pkg)"));
    m_fileEditor->setText(fileName);
}

void Widget::onUpdateFinished() {
    QMessageBox::information(this, "升级", "升级成功!");
    setControlsEnabled(true);
}

static QString dragFilePath(const QMimeData *mimeData) {
    QString ret;
    if (mimeData->hasUrls()) {
        auto urls = mimeData->urls();
        for (auto &url : urls) {
            auto path = url.toLocalFile();
            if (path.isEmpty()) continue;
            ret = path;
            break;
        }
    }
    return ret;
}

void Widget::dragEnterEvent(QDragEnterEvent *event) {
    auto path = dragFilePath(event->mimeData());
    if (path.endsWith(".Pkg", Qt::CaseInsensitive)) {
        event->acceptProposedAction();
    } else {
        event->ignore();
    }
}

void Widget::dropEvent(QDropEvent *event) {
    auto path = dragFilePath(event->mimeData());
    if (path.endsWith(".Pkg", Qt::CaseInsensitive)) {
        m_fileEditor->setText(path);
    }
}

void Widget::setControlsEnabled(bool enabled) {
    m_startButton->setEnabled(enabled);
    m_selectButton->setEnabled(enabled);
    m_fileEditor->setEnabled(enabled);
}

void Widget::setProgress(int32_t progress) {
    m_progressBar->setValue(progress);
}

void Widget::setMessage(const QString &message) {
    m_messageLabel->setText(message);
}
