#include "MainWidget.h"
#include "./ui_MainWidget.h"
#include "adddesktop.h"
#include <QTextStream>
#include <fstream>

MainWidget::MainWidget(QWidget* parent) : QWidget(parent), ui(new Ui::MainWidget)
{
    ui->setupUi(this);
    setWindowTitle("packqt v1.0.4");
    connect_operator();
    control_init();
}

MainWidget::~MainWidget() { delete ui; }

void MainWidget::connect_operator()
{
    connect(ui->btnSelcetBinary, &QPushButton::clicked, this, [=]() { SelectFile(this, ui->edBinary, "请选择Qt二进制文件", "所有文件 (*)"); });
    connect(ui->btnSelectOut, &QPushButton::clicked, this, [=]() { SelectDirectory(this, ui->edOutDir); });
    connect(ui->btnSelectQt, &QPushButton::clicked, this, [=]() { SelectDirectory(this, ui->edQtDir); });
    connect(ui->btnAddEnv, &QPushButton::clicked, this, [=]() { add_env_btn(); });
    connect(ui->btnDelEnv, &QPushButton::clicked, this, [=]() { del_env_btn(); });
    connect(ui->btnGenerate, &QPushButton::clicked, this, [=]() { generate(); });
    connect(ui->btnInstall, &QPushButton::clicked, this, [=]() { install_bin2menu(); });
    connect(ui->btnExit, &QPushButton::clicked, this, [=]() { QApplication::exit(); });
}

void MainWidget::control_init()
{
    ui->plainTextEdit->setReadOnly(true);
    ui->edQtDir->setText(QDir::homePath() + "/Qt5.14.2/5.14.2/gcc_64");
    ui->edOutDir->setText(QDir::homePath() + "/program");
#if !defined(NDEBUG)
#endif
}

void MainWidget::add_env_btn()
{
    QString env = SelectDirectory(this, nullptr);
    if (env.isEmpty()) {
        return;
    }
    ui->listWidget->addItem(env);
}

void MainWidget::simple_log(const QString& info, bool enter)
{
    // dd
    if (enter) {
        ui->plainTextEdit->appendPlainText(info + "\n");

    } else {
        ui->plainTextEdit->appendPlainText(info);
    }
}

// 将二进制文件安装到菜单栏
void MainWidget::install_bin2menu()
{
    // QString bin = SelectFile(this, ui->edBinary, "请选择二进制可执行文件", "所有文件 (*)");
    // if (bin.isEmpty()) {
    //     return;
    // }
    AddDesktop add(this);
    add.exec();
}

// 将某个文件夹拷贝到制定目录下
bool MainWidget::copy_dir(const std::string& source_dir, const std::string& des_dir)
{
    if (!fs::exists(source_dir) || !fs::exists(des_dir)) {
        return false;
    }

    fs::path des_parent_dir = fs::path(des_dir).append(fs::path(source_dir).filename());
    if (!fs::exists(des_parent_dir)) {
        fs::create_directories(des_parent_dir);
    }
    std::list<fs::path> paths{};
    for (const auto& entry : fs::directory_iterator(source_dir)) {
        paths.push_back(entry);
    }

    while (!paths.empty()) {
        fs::path path = paths.front();
        paths.pop_front();

        fs::path destination(boost::replace_all_copy(path.string(), source_dir, des_parent_dir.string()));
        if (fs::is_directory(path)) {
            if (!fs::exists(destination)) {
                fs::create_directories(destination);
            }
            for (const auto& entry : fs::directory_iterator(path)) {
                paths.push_back(entry);
            }
            continue;
        }
        fs::copy_file(path, destination, fs::copy_options::overwrite_existing);
    }
    return true;
}

std::vector<std::string> MainWidget::get_depend_on(const std::string& name, const std::vector<std::string>& env)
{
    std::vector<std::string> vec;
    std::string              cmd{};

    if (!env.empty()) {
        cmd.append("export LD_LIBRARY_PATH=$LD_LIBRARY_PATH");
        for (const auto& data : env) {
            cmd.append(":" + data);
        }
    }
    cmd.append(" && ldd " + name);
    char  buffer[1024]{};
    FILE* pf = nullptr;
    if ((pf = popen(cmd.c_str(), "r")) == nullptr) {
        return vec;
    }
    std::string result{};
    while (std::fgets(buffer, sizeof(buffer), pf)) {
        result.append(buffer);
    }

    boost::split(vec, result, boost::is_any_of("\t"));
    return vec;
}

std::list<std::string> MainWidget::handle_result(const std::vector<std::string>& vec)
{
    std::list<std::string> ret;
    for (const auto& data : vec) {
        if (data.empty()) {
            continue;
        }

        if (boost::contains(data, "not found")) {
            simple_log(QString::fromStdString("未找到依赖:" + data), false);
            continue;
        }

        std::string              tdata = boost::replace_all_copy(data, "=>", "");
        std::vector<std::string> vt;
        boost::split(vt, tdata, boost::is_any_of(" "));
        if (vt.size() != 4) {
            // QString info = "原始数据:" + QString::fromStdString(data);
            // simple_log(info, false);
            continue;
        }
        if (boost::starts_with(vt[2], "/lib")) {
            continue;
        }
        ret.push_back(vt[2]);
        // simple_log(QString::fromStdString(vt[2]), false);
    }
    return ret;
}

void MainWidget::del_env_btn()
{
    QListWidgetItem* item = ui->listWidget->currentItem();
    if (!item) {
        return;
    }
    delete ui->listWidget->takeItem(ui->listWidget->currentRow());
}

QString MainWidget::SelectDirectory(QWidget* parent, QLineEdit* pEdit, const QString& pre_path)
{
    QString start_path = pre_path;
    QDir    folder(start_path);
    if (start_path.isEmpty() || !folder.exists()) {
        start_path = QDir::homePath();
    }
    QString selectedDirectory = QFileDialog::getExistingDirectory(parent, u8"选择文件夹", start_path);

    if (selectedDirectory.isEmpty()) {
        return "";
    }
    if (pEdit) {
        pEdit->setText(selectedDirectory);
    }
    return selectedDirectory;
}

QString MainWidget::SelectFile(QWidget* parent, QLineEdit* pEdit, const QString& info, const QString& filter)
{
    QString filePath = QFileDialog::getOpenFileName(parent, info, QDir::homePath(), filter);
    if (!filePath.isEmpty() && pEdit) {
        pEdit->setText(filePath);
    }
    return filePath;
}

bool MainWidget::isOk(QWidget* parent, const QString& title, const QString& content)
{
    QMessageBox questionBox(parent);
    questionBox.setText(content);
    questionBox.setWindowTitle(title);
    questionBox.setIcon(QMessageBox::Question);
    questionBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
    int result = questionBox.exec();

    if (result != QMessageBox::Yes) {
        return false;
    } else {
        return true;
    }
}

void MainWidget::message(QWidget* parent, const QString& content) { QMessageBox::information(parent, "提示", content); }

void MainWidget::cmd_exec(const std::string& cmd)
{
    int r = system(cmd.c_str());
    (void)r;
}

void MainWidget::generate()
{
    ui->plainTextEdit->clear();
    envs_.clear();
    sos_.clear();

    binary_ = ui->edBinary->text().trimmed();
    fs::path binary_dir = fs::path(binary_.toStdString()).parent_path();
    envs_.push_back(binary_dir.string());
    std::string filename = fs::path(binary_.toStdString()).filename().string();

    int env_cnt = ui->listWidget->count();
    for (int i = 0; i < env_cnt; ++i) {
        envs_.push_back(ui->listWidget->item(i)->text().toStdString());
    }

    auto retPrevious = get_depend_on(binary_.toStdString(), envs_);
    auto result = handle_result(retPrevious);

    std::vector<std::string> dynamic_so{};
    std::copy(result.begin(), result.end(), std::back_inserter(dynamic_so));

    // 处理 platform文件夹
    std::list<fs::path> platform{};
    fs::path            qt_platform(ui->edQtDir->text().trimmed().toStdString());
    qt_platform.append("plugins/platforms");
    for (const auto& entry : fs::directory_iterator(qt_platform)) {
        result.push_back(entry.path().string());
    }

    while (!result.empty()) {
        std::string bin = result.front();
        result.pop_front();
        auto tmp_retPrevious = get_depend_on(bin, envs_);
        auto tmp_result = handle_result(tmp_retPrevious);
        for (const auto& data : tmp_result) {
            auto iter = std::find(dynamic_so.begin(), dynamic_so.end(), data);
            if (iter != dynamic_so.end()) {
                continue;
            }
            dynamic_so.push_back(data);
            result.push_back(data);
        }
    }

    fs::path out_dir(ui->edOutDir->text().trimmed().toStdString());
    out_dir.append(filename);
    if (!fs::exists(out_dir)) {
        fs::create_directories(out_dir);
    }

    for (const auto& data : dynamic_so) {
        std::string purpose = fs::path(out_dir).append(fs::path(data).filename()).string();
        fs::copy_file(data, purpose, fs::copy_options::overwrite_existing);
    }

    // 复制platform文件夹
    copy_dir(qt_platform.string(), out_dir.string());

    // 复制主体文件
    fs::path out_binary = fs::path(out_dir).append(fs::path(binary_.toStdString()).filename());
    fs::copy_file(binary_.toStdString(), out_binary, fs::copy_options::overwrite_existing);
    if (!add_run_sh(out_dir.string(), filename)) {
        return;
    }
    message(this, "完成");
}


bool MainWidget::add_run_sh(const std::string& out_dir, const std::string& exe_name)
{
    // 生成一个启动文件夹
    QFile file("://resource/run.sh");
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        return false;
    }
    QTextStream in(&file);
    // 读取文件内容
    QString content = in.readAll();
    file.close();

    std::string run_sh = content.toStdString();
    boost::replace_all(run_sh, "replace_string", exe_name);

    std::string   out_sh = fs::path(out_dir).append(exe_name + ".sh").string();
    std::ofstream out(out_sh, std::ios::out);
    if (!out.is_open()) {
        return false;
    }
    out << run_sh;
    out.close();
    cmd_exec(std::string("chmod +x " + out_sh));
    return true;
}