#include "MainWidget.h"

#include <QClipboard>
#include <QDateTime>
#include <QFile>
#include <QKeyEvent>
#include <QRegularExpression>
#include <QScreen>
#include <QSettings>
#include <filesystem>

#include "./ui_MainWidget.h"
#include "public_def.h"
#include "qformatset.h"
#include "src/data_edit.h"

constexpr std::size_t g_OnePage = 100;
namespace fs = std::filesystem;
MainWidget::MainWidget(QWidget* parent)
    : QWidget(parent), ui(new Ui::MainWidget)
{
    ui->setupUi(this);

    setWindowTitle(u8"OneLevelXmlOpr v1.3.7");
    setWindowIcon(QIcon("://resource/xml.ico"));

    QScreen* primaryScreen = QGuiApplication::primaryScreen();
    QRect screenGeometry = primaryScreen->geometry();

    setBaseSize(screenGeometry.width() * 0.6, screenGeometry.height() * 0.9);

    // setMinimumWidth(900);
    // setMinimumHeight(800);

    his_ = new CHistory();
    attri_edit_ = new CAttributeEdit();
    ini_oper_ = new CGroupIni();
    ini_oper_->load_ini();
    group_set_ = new CGroupSetting(this, ini_oper_);

    ui->edStatus->setReadOnly(true);
    ui->btnSave->setEnabled(false);
    ui->edAllPage->setEnabled(false);
    ui->cbCaseSensitive->setChecked(false);
    ui->btnImport->setEnabled(false);
    ui->btnExport->setEnabled(false);
    ui->btnBackup->setEnabled(false);
    ui->rbReplaceSelect->setChecked(true);
    ui->btnRead->setFixedWidth(100);
    ui->btnSave->setFixedWidth(100);
    ui->btnExit->setFixedWidth(100);
    ui->btnSearch->setFixedWidth(120);
    ui->btnBackup->setEnabled(false);
    ui->btnResort->setEnabled(false);

    connect(ui->btnSelectFile, &QPushButton::clicked, this, [&]() {
        QString file = CUtil::select_file(this, u8"请选择xml文件",
                                          u8"XML(*.xml);;所有文件 (*)");
        if (file.isEmpty()) {
            return;
        }
        ui->edStatus->setText(file);
    });
    connect(ui->btnSearch, &QPushButton::clicked, this,
            [&]() { search(ui->edSearchKey->text()); });
    connect(ui->btnBackup, &QPushButton::clicked, this,
            [&]() { backup_file(); });
    connect(ui->btnFormat, &QPushButton::clicked, this,
            [&]() { format_xml(); });
    connect(ui->btnRead, &QPushButton::clicked, this,
            [&]() { read(ui->edStatus->text().trimmed()); });
    connect(ui->btnSave, &QPushButton::clicked, this, [&]() { save(); });
    connect(ui->btnExit, &QPushButton::clicked, this,
            [&]() { QApplication::exit(0); });
    connect(ui->btnReset, &QPushButton::clicked, this, &MainWidget::reset);
    connect(ui->btnReplace, &QPushButton::clicked, this,
            [&]() { replace_content(true); });
    connect(ui->btnRxReplace, &QPushButton::clicked, this,
            [&]() { replace_content(false); });
    connect(ui->cbUnit, &QComboBox::currentTextChanged, this,
            [&](const QString& content) { unit_change(); });
    connect(ui->btnExport, &QPushButton::clicked, this,
            &MainWidget::copy_multi_data);
    connect(ui->btnPagePre, &QPushButton::clicked, this, [&]() {
        unsigned int cur = ui->edCurPage->text().toUInt();
        push_content(current_, cur - 1);
    });
    connect(ui->btnImport, &QPushButton::clicked, this, [&]() {
        CDataEdit edit;
        edit.is_import_ = true;
        edit.set_xml_opr(&xml_);
        edit.exec();

        if (!edit.is_import_sucess_) {
            return;
        }

        if (cur_config_.is_same) {
            xml_.get_all_elements(vec_,
                                  ui->cbUnit->currentText().toStdString());
        } else {
            xml_.get_all_elements(vec_);
        }

        current_.clear();
        current_ = vec_;
        search(ui->edSearchKey->text());
    });

    connect(ui->btnSet, &QPushButton::clicked, this, [&]() {
        group_set_->exec();
        base_init();
    });
    connect(ui->btnHis, &QPushButton::clicked, this, [&]() {
        CUIHistory his(this, his_);
        his.exec();

        if (his.cur_ != "") {
            ui->edStatus->setText(his.cur_);
        }
    });

    connect(ui->btnPageNext, &QPushButton::clicked, this, [&]() {
        unsigned int cur = ui->edCurPage->text().toUInt();
        push_content(current_, cur + 1);
    });
    connect(ui->btnJump, &QPushButton::clicked, this, [&]() {
        unsigned int cur = ui->edCurPage->text().toUInt();
        push_content(current_, cur);
    });
    connect(ui->btnResort, &QPushButton::clicked, this, [&]() {
        if (tab_widget_ == nullptr) {
            return;
        }

        if (!CUtil::affirm(this, u8"提示", u8"确认重新排序吗?")) {
            return;
        }

        sort_by_repeat(vec_);
        std::vector<Element_t*> nvec{};
        xml_.copy_and_del(vec_, nvec);
        vec_.clear();
        std::swap(vec_, nvec);
        current_ = vec_;
        push_content(current_);
    });

    QSettings settings;
    settings.beginGroup("xmlopr");
    restoreGeometry(settings.value("geometry").toByteArray());
    settings.endGroup();

    // QFile qss_file("://qss/lightblue.css");
    // QFile qss_file("://qss/flatgray.css");
    // if (qss_file.open(QFile::ReadOnly)) {
    //     qApp->setStyleSheet(qss_file.readAll());
    // }

    init_menu();
    // 基本处理
    base_init();
}

void MainWidget::copy_key()
{
    Element_t* target = get_current_select_key();
    if (target == nullptr) {
        return;
    }
    QClipboard* clip = QApplication::clipboard();
    clip->setText(QString(target->Attribute(keys_[0].c_str())));
    // CUtil::msg(this, u8"已复制");
}

void MainWidget::closeEvent(QCloseEvent* event)
{
    QSettings settings;
    settings.beginGroup("xmlopr");
    settings.setValue("geometry", saveGeometry());
    settings.endGroup();
    QWidget::closeEvent(event);
}

void MainWidget::keyPressEvent(QKeyEvent* event)
{
    switch (event->key()) {
        case Qt::Key_Return:
            search(ui->edSearchKey->text());
            break;
        default:
            break;
    }
    QWidget::keyPressEvent(event);
}

MainWidget::~MainWidget()
{
    delete ui;
    delete attri_edit_;
    delete ini_oper_;
    delete his_;
}

void MainWidget::set_work_exe(char* path)
{
    exe_path_.clear();
    exe_path_.append(path);
}

void MainWidget::show_custom_menu()
{
    if (tab_widget_ == nullptr) {
        return;
    }
    QModelIndexList indexList = tab_widget_->selectionModel()->selectedRows();

    if (indexList.size() == 1) {
        menu_simple_->exec(QCursor::pos());
    } else {
        menu_multi_->exec(QCursor::pos());
    }
}

void MainWidget::generate_table_widget()
{
    tab_widget_ = new QTableWidget();
    metrics_ = std::make_shared<QFontMetrics>(tab_widget_->font());
    tab_widget_->setContextMenuPolicy(Qt::CustomContextMenu);

    connect(tab_widget_, &QTableWidget::itemChanged, this,
            [&](QTableWidgetItem* item) { item_changed_handle(item); });
    connect(tab_widget_, &QTableWidget::customContextMenuRequested, this,
            &MainWidget::show_custom_menu);
    auto keys = CUtil::splitString(cur_config_.propertis, ",");
    keys_.clear();

    QStringList list;
    for (const auto& item : keys) {
        if (item.empty()) {
            continue;
        }
        keys_.push_back(item);
        list.append(QString::fromStdString(item));
    }

    col_with_.clear();
    for (auto i = 0; i < keys_.size(); ++i) {
        col_with_[i] = 0;
    }

    tab_widget_->setColumnCount(list.size());
    tab_widget_->setHorizontalHeaderLabels(list);
    tab_widget_->setSelectionBehavior(QAbstractItemView::SelectRows);
    // tab_widget_->setSelectionMode(QAbstractItemView::SelectionMode::SingleSelection);

    for (auto i = 0; i < keys.size(); ++i) {
        tab_widget_->setColumnWidth(i, col_with_[i]);
    }

    QHBoxLayout* lay = new QHBoxLayout();
    lay->addWidget(tab_widget_);
    ui->widget->setLayout(lay);
}

void MainWidget::push_content(const std::vector<tinyxml2::XMLElement*>& eles,
                              std::size_t page, bool auto_jump_pre)
{
    if (tab_widget_ == nullptr || page == 0) {
        return;
    }

    std::size_t all_size = eles.size();
    std::size_t max_show = g_OnePage * page;
    all_page_ = all_size / 100;

    if (all_size % 100 != 0) {
        ++all_page_;
    }

    if (auto_jump_pre && page > all_page_ && page > 1) {
        page -= 1;
    }

    if (page < 1 || page > all_page_) {
        if (eles.size() > 0) {
            CUtil::msg(this, u8"页码不在范围内");
            return;
        }
        tab_widget_->clearContents();
        tab_widget_->setRowCount(0);
        cur_page_ = 0;
        ui->edCurPage->setText(QString::number(cur_page_));
        ui->edAllPage->setText(QString::number(all_page_));
        return;
    }

    tab_widget_->clearContents();
    tab_widget_->setRowCount(0);

    auto_add_ = true;
    for (auto p = (page - 1) * g_OnePage; p < all_size && p < max_show; ++p) {
        int row_cnt = tab_widget_->rowCount();
        tab_widget_->insertRow(row_cnt);
        insert_one_line(eles[p], row_cnt);
    }
    ui->edCurPage->setText(QString::number(page));
    cur_page_ = page;
    ui->edAllPage->setText(QString::number(all_page_));
    judge_btn_page();
    auto_add_ = false;

    for (auto i = 0; i < keys_.size(); ++i) {
        tab_widget_->setColumnWidth(static_cast<int>(i), col_with_[i]);
    }
}

void MainWidget::judge_btn_page()
{
    int cur = ui->edCurPage->text().trimmed().toUInt();
    if (cur <= 1) {
        ui->btnPagePre->setEnabled(false);
    } else {
        ui->btnPagePre->setEnabled(true);
    }

    if (cur >= all_page_) {
        ui->btnPageNext->setEnabled(false);
    } else {
        ui->btnPageNext->setEnabled(true);
    }
}

bool MainWidget::read(const QString& file_path)
{
    // 获取配置
    cur_config_.name = ui->cbConfig->currentText().toStdString();
    if (!ini_oper_->get_item(cur_config_)) {
        CUtil::msg(this, u8"获取配置失败。");
        return false;
    }

    if (!xml_.open(file_path.toStdString())) {
        CUtil::msg(this, u8"打开xml失败。");
        return false;
    }
    xml_.set_baseinfo(cur_config_);
    if (!xml_.parse_xml(vec_)) {
        CUtil::msg(this, u8"解析xml失败。");
        return false;
    }

    if (cur_config_.max_blank_add >= 0) {
        blank_with_ = cur_config_.max_blank_add;
    }

    if (cur_config_.is_same) {
        std::vector<std::string> units;
        xml_.get_all_unit(units);
        QStringList list;
        for (const auto& unit : units) {
            list.append(QString::fromStdString(unit));
        }
        u_ = true;
        ui->cbUnit->clear();
        if (!list.isEmpty()) {
            ui->cbUnit->addItems(list);
            ui->cbUnit->setCurrentIndex(0);
        }
        u_ = false;
        ui->btnResort->setEnabled(false);
    } else {
        ui->cbUnit->setEnabled(false);
    }

    if (cur_config_.max_col_len < 0 || cur_config_.max_col_len > 1000) {
        allow_max_with_ = 100;
    } else {
        allow_max_with_ = cur_config_.max_col_len;
    }

    generate_table_widget();
    push_content(vec_);
    current_ = vec_;

    ui->edStatus->setText(file_path);
    ui->btnRead->setEnabled(false);
    ui->btnSave->setEnabled(true);
    ui->btnSelectFile->setEnabled(false);
    ui->btnImport->setEnabled(true);
    ui->btnExport->setEnabled(true);
    ui->btnBackup->setEnabled(true);
    ui->btnBackup->setEnabled(true);
    ui->btnResort->setEnabled(true);

    ui->cbConfig->setEnabled(false);
    ui->btnSet->setEnabled(false);
    ui->btnHis->setEnabled(false);

    his_->push(ui->edStatus->text().toStdString());

    return true;
}

void MainWidget::search(const QString& key)
{
    QString bkey = key;
    if (bkey.isEmpty()) {
        current_ = vec_;
        push_content(current_, ui->edCurPage->text().toUInt(), true);
        return;
    }
    if (tab_widget_ == nullptr) {
        return;
    }

    if (!ui->cbCaseSensitive->isChecked()) {
        bkey = bkey.toUpper();
    }

    current_.clear();
    for (const auto& item : vec_) {
        for (auto i = 0; i < keys_.size(); ++i) {
            const char* data = item->Attribute(keys_[i].c_str());
            QString qdata(data);
            if (!ui->cbCaseSensitive->isChecked()) {
                qdata = qdata.toUpper();
            }
            if (!qdata.contains(bkey)) {
                continue;
            }
            current_.push_back(item);
            break;
        }
    }
    push_content(current_, ui->edCurPage->text().toUInt(), true);
}

void MainWidget::item_changed_handle(QTableWidgetItem* item)
{
    if (auto_add_) {
        return;
    }
    if (item == nullptr) {
        return;
    }
    int row = item->row();
    int col = item->column();

    QString xml_key = tab_widget_->item(row, 0)->text();
    Element_t* result = get_element_by_key(xml_key);
    if (result == nullptr) {
        return;
    }
    result->SetAttribute(keys_[col].c_str(),
                         item->text().toStdString().c_str());
}

void MainWidget::save()
{
    if (xml_.save()) {
        CUtil::msg(this, u8"保存成功");
    } else {
        CUtil::msg(this, u8"保存失败");
    }
}

void MainWidget::copy_select_line()
{
    // if (!CUtil::affirm(this, u8"确认", u8"确认复制吗?")) {
    //     return;
    // }

    QTableWidgetItem* cur_item = get_current_select_item();
    if (cur_item == nullptr) {
        return;
    }

    Element_t* target = get_element_by_key(cur_item->text());
    if (target == nullptr) {
        return;
    }
    Element_t* newer = xml_.copy_element(target);

    if (!edit_property(newer, cur_item->row(), true)) {
        return;
    }

    xml_.insert_brother_node(target, newer);

    tab_widget_->insertRow(cur_item->row() + 1);
    insert_one_line(newer, cur_item->row() + 1);

    std::vector<Element_t*>::iterator it;
    for (it = vec_.begin(); it != vec_.end(); ++it) {
        if (*it == target) {
            break;
        }
    }
    int df = it - vec_.begin() + 1;
    vec_.insert(vec_.begin() + df, newer);
    search(ui->edSearchKey->text());
}

// 返回 true 表示确认编辑了, false 表示取消编辑了。
bool MainWidget::edit_property(Element_t* target, int row, bool is_copy)
{
    if (target == nullptr) {
        return false;
    }

    Property_t property;
    xml_.get_attributes(target, property);

    // 检测key值是否变化
    std::string value_pre = property[0].value;
    attri_edit_->set_attribute(property);
    attri_edit_->exec();

    if (!attri_edit_->is_ok_) {
        return false;
    }

    attri_edit_->get_attribute(property);
    // 如果是复制,或者编辑时更改了key值,检查重复性。
    if (property[0].value != value_pre || is_copy) {
        while (xml_.check_key_exists(property)) {
            CUtil::msg(attri_edit_, u8"不能有相同的key,请检查。");
            attri_edit_->exec();
            attri_edit_->get_attribute(property);
            if (!attri_edit_->is_ok_) {
                return false;
            }
        }
    }
    xml_.attributes_to_element(target, property);

    // 这里要同步到界面
    ele_update_gui(target, QString::fromStdString(value_pre));
    return true;
}

// ele_update_gui 的 row 参数暂时废弃,无意义。
void MainWidget::ele_update_gui(Element_t* target, const QString& pre_value)
{
    if (tab_widget_ == nullptr) {
        return;
    }

    QString search_key;

    if (pre_value.isEmpty()) {
        search_key =
            QString::fromLocal8Bit(target->Attribute(keys_[0].c_str()));
    } else {
        search_key = pre_value;
    }

    int target_row = -1;
    // 获取当前显示的内容,查找更改。
    int cur_show_cnt = tab_widget_->rowCount();
    for (int i = 0; i < cur_show_cnt; ++i) {
        if (tab_widget_->item(i, 0)->text() == search_key) {
            target_row = i;
            break;
        }
    }

    if (target_row < 0) {
        return;
    }

    for (auto i = 0; i < keys_.size(); ++i) {
        const char* v = target->Attribute(keys_[i].c_str());
        auto* qitem = tab_widget_->item(target_row, i);
        qitem->setText(QString(v));
    }
}

void MainWidget::init_menu()
{
    menu_simple_ = new QMenu();
    menu_multi_ = new QMenu();

    ac_edit_property_ = new QAction(u8"编辑");
    ac_copy_curline_ = new QAction(u8"复制项");
    ac_del_curline_ = new QAction(u8"删除项");
    ac_copy_key_ = new QAction(u8"复制key");

    menu_simple_->addAction(ac_edit_property_);
    menu_simple_->addAction(ac_copy_curline_);
    menu_simple_->addAction(ac_del_curline_);
    menu_simple_->addAction(ac_copy_key_);
    menu_multi_->addAction(ac_del_curline_);

    connect(ac_edit_property_, &QAction::triggered, this, [&]() {
        QTableWidgetItem* cur_item = get_current_select_item();
        if (cur_item == nullptr) {
            return;
        }

        Element_t* target = get_element_by_key(cur_item->text());
        if (target == nullptr) {
            return;
        }
        edit_property(target, cur_item->row(), false);
    });
    connect(ac_copy_curline_, &QAction::triggered, this,
            [&]() { copy_select_line(); });
    connect(ac_del_curline_, &QAction::triggered, this,
            [&]() { del_select_line(); });
    connect(ac_copy_key_, &QAction::triggered, this, [&]() { copy_key(); });
}

void MainWidget::insert_one_line(Element_t* ele, int row)
{
    if (ele == nullptr) {
        return;
    }
    for (auto i = 0; i < keys_.size(); ++i) {
        const char* data = ele->Attribute(keys_[i].c_str());
        QTableWidgetItem* wgItem = new QTableWidgetItem();

        if (i == 0) {
            wgItem->setFlags(wgItem->flags() & ~Qt::ItemIsEditable);
            // wgItem->setFlags(wgItem->flags() | Qt::ItemIsUserCheckable);
            // wgItem->setCheckState(Qt::Checked);
        }
        QString sda(data);
        int dwidth = metrics_->horizontalAdvance(sda) + blank_with_;
        int need_set_width =
            dwidth > allow_max_with_ ? allow_max_with_ : dwidth;
        col_with_[i] =
            col_with_[i] < need_set_width ? need_set_width : col_with_[i];
        wgItem->setText(sda);
        tab_widget_->setItem(row, i, wgItem);
    }
}

void MainWidget::del_select_line()
{
    if (!CUtil::affirm(this, u8"确认", u8"确认删除吗?")) {
        return;
    }

    QModelIndexList indexList = tab_widget_->selectionModel()->selectedRows();
    int size = indexList.size();
    std::vector<int> erase_row;
    for (auto i = size - 1; i > -1; --i) {
        Element_t* target = get_element_by_row(indexList[i].row());
        if (target == nullptr) {
            continue;
        }
        xml_.del_element(target);
        tab_widget_->removeRow(indexList[i].row());
        erase_row.push_back(indexList[i].row());
        std::vector<Element_t*>::iterator it;
        for (it = vec_.begin(); it != vec_.end(); ++it) {
            if (*it == target) {
                break;
            }
        }
        vec_.erase(it);
    }
    // std::sort(erase_row.begin(), erase_row.end(), std::greater<int>());
    // for (const auto& item : erase_row) {
    //     tab_widget_->removeRow(item);
    // }
    search(ui->edSearchKey->text());
}

Element_t* MainWidget::get_current_select_key()
{
    Element_t* ret = nullptr;
    QTableWidgetItem* item = get_current_select_item();
    if (item == nullptr) {
        return ret;
    }
    ret = get_element_by_key(item->text());
    return ret;
}

QTableWidgetItem* MainWidget::get_current_select_item()
{
    QTableWidgetItem* ret = nullptr;
    if (tab_widget_ == nullptr) {
        return ret;
    }
    QList<QTableWidgetItem*> selectedItems = tab_widget_->selectedItems();
    if (selectedItems.size() < 1) {
        CUtil::msg(this, u8"没有选中数据");
        return ret;
    }
    ret = selectedItems[0];
    return ret;
}

void MainWidget::reset()
{
    current_.clear();
    current_ = vec_;
    push_content(current_);
}

tinyxml2::XMLElement* MainWidget::get_element_by_key(const QString& key)
{
    Element_t* ret = nullptr;
    for (const auto& ele : current_) {
        const char* data = ele->Attribute(keys_[0].c_str());
        QString qdata(data);
        if (qdata != key) {
            continue;
        }
        ret = ele;
        break;
    }
    return ret;
}

Element_t* MainWidget::get_element_by_row(int row)
{
    Element_t* ret = nullptr;
    if (row < 0 || !tab_widget_ || row >= tab_widget_->rowCount()) {
        return ret;
    }
    QTableWidgetItem* item = tab_widget_->item(row, 0);
    ret = get_element_by_key(item->text());
    return ret;
}

void MainWidget::sort_by_repeat(std::vector<Element_t*>& vec)
{
    std::vector<SElement_t> turn_vec{};
    for (const auto& item : vec) {
        const char* str = item->Attribute(keys_[0].c_str());
        std::string d(str);
        turn_vec.emplace_back(item, d);
    }
    std::sort(turn_vec.begin(), turn_vec.end(),
              [&](const SElement_t& se1, const SElement_t& se2) {
                  return compare_by_prefix(se1, se2);
              });
    vec.clear();
    for (const auto& item : turn_vec) {
        vec.push_back(item.ele);
    }
}

void MainWidget::copy_multi_data()
{
    if (tab_widget_ == nullptr) {
        return;
    }
    std::vector<OperElement*> vec;

    if (ui->rbAllPage->isChecked()) {
        get_related_elements(vec, AREA_ALL_PAGE);
    } else if (ui->rbReplaceSelect->isChecked()) {
        get_related_elements(vec, AREA_SELECT);
    } else if (ui->rbRepCurPage->isChecked()) {
        get_related_elements(vec, AREA_CUR_PAGE);
    } else {
        get_related_elements(vec, AREA_ALL);
    }

    if (vec.size() < 1) {
        CUtil::msg(this, u8"无选择数据");
        return;
    }
    QString ret;
    for (auto& item : vec) {
        if (item == nullptr) {
            continue;
        }
        tinyxml2::XMLPrinter printer;
        item->element_->Accept(&printer);
        ret.append(printer.CStr());
        delete item;
    }

    CDataEdit edit;
    edit.set_data(ret);
    edit.exec();
}

void MainWidget::replace_content(bool is_common)
{
    if (tab_widget_ == nullptr) {
        return;
    }
    QString key = ui->edRepPre->text();
    QString after = ui->edRepAfter->text();
    if (key.isEmpty()) {
        CUtil::msg(this, u8"替换前数据为空。");
        return;
    }
    auto handle = [&](const std::vector<OperElement*>& vec, bool is_search) {
        // qDebug() << "要处理的数量为:" << vec.size();
        for (auto& item : vec) {
            if (is_common) {
                replace_str(key, after, item->element_);
            } else {
                replace_str(item->element_, key, after);
            }
            if (!is_search) {
                ele_update_gui(item->element_);
            }
            delete item;
        }
    };

    std::vector<OperElement*> vec;
    if (ui->rbReplaceSelect->isChecked()) {
        QModelIndexList indexList =
            tab_widget_->selectionModel()->selectedRows();
        if (indexList.size() < 1) {
            CUtil::msg(this, u8"无选择数据");
            return;
        }
        get_related_elements(vec, AREA_SELECT);
        handle(vec, false);
    } else if (ui->rbAllPage->isChecked()) {
        get_related_elements(vec, AREA_ALL_PAGE);
        handle(vec, false);
        xml_.get_all_elements(vec_);
        current_.clear();
        current_ = vec_;
        search(ui->edSearchKey->text());
    } else if (ui->rbRepCurPage->isChecked()) {
        get_related_elements(vec, AREA_CUR_PAGE);
        handle(vec, false);
    } else {
        if (!CUtil::affirm(this, u8"确认", u8"确认进行全局替换吗?")) {
            return;
        }
        get_related_elements(vec, AREA_ALL);
        handle(vec, true);
        xml_.get_all_elements(vec_);
        current_.clear();
        current_ = vec_;
        search(ui->edSearchKey->text());
    }
}

void MainWidget::replace_str(const QString& pre, const QString& after,
                             Element_t* ele)
{
    if (ele == nullptr) {
        return;
    }
    for (auto i = 1; i < keys_.size(); ++i) {
        auto* value = ele->Attribute(keys_[i].c_str());
        QString content(value);
        if (content.contains(pre)) {
            content.replace(pre, after);
            ele->SetAttribute(keys_[i].c_str(), content.toStdString().c_str());
        }
    }
}

void MainWidget::replace_str(Element_t* ele, const QString& rg,
                             const QString& after)
{
    QRegularExpression rx(rg);
    if (ele == nullptr) {
        return;
    }
    for (auto i = 0; i < keys_.size(); ++i) {
        auto* value = ele->Attribute(keys_[i].c_str());
        QString content(value);
        // qDebug() << content;
        content.replace(rx, after);
        // qDebug() << content;
        // qDebug() << "\n";
        ele->SetAttribute(keys_[i].c_str(), content.toStdString().c_str());
    }
}

void MainWidget::get_related_elements(std::vector<OperElement*>& out,
                                      ReplaceArea area)
{
    assert(tab_widget_);
    out.clear();
    switch (area) {
        case AREA_ALL_PAGE: {
            out.resize(current_.size());
            std::transform(
                current_.begin(), current_.end(), out.begin(),
                [](Element_t* ele) { return new OperElement(ele, 0); });
            break;
        }
        case AREA_CUR_PAGE: {
            int rows = tab_widget_->rowCount();
            for (int i = 0; i < rows; ++i) {
                out.emplace_back(new OperElement(get_element_by_row(i), i));
            }
            break;
        }
        case AREA_ALL: {
            out.resize(vec_.size());
            std::transform(
                vec_.begin(), vec_.end(), out.begin(),
                [](Element_t* ele) { return new OperElement(ele, 0); });
            break;
        }
        default: {
            QModelIndexList indexList =
                tab_widget_->selectionModel()->selectedRows();
            for (int i = 0; i < indexList.size(); ++i) {
                out.emplace_back(
                    new OperElement(get_element_by_row(indexList[i].row()),
                                    indexList[i].row()));
            }
            break;
        }
    }
}

void MainWidget::backup_file()
{
    if (tab_widget_ == nullptr) {
        return;
    }

    QString time = QDateTime::currentDateTime().toString("yyyy-MMdd-hhmmss");
    if (!xml_.backup_file(
            fs::path(exe_path_).parent_path().append("backup").string(),
            time.toStdString())) {
        CUtil::msg(this, u8"备份失败。");
    } else {
        CUtil::msg(this, u8"备份完成。");
    }
}

void MainWidget::base_init()
{
    StrVec_t vec;
    ini_oper_->get_all_node(vec);

    QStringList list;
    for (const auto& data : vec) {
        list.append(QString::fromStdString(data));
    }
    ui->cbConfig->clear();

    if (list.empty()) {
        return;
    }

    ui->cbConfig->addItems(list);
    ui->cbConfig->setCurrentIndex(0);
}

void MainWidget::unit_change()
{
    if (u_) {
        return;
    }
    std::string unit = ui->cbUnit->currentText().toStdString();
    if (!xml_.get_all_elements(vec_, unit)) {
        CUtil::msg(this, u8"获取单元信息失败:" + QString::fromStdString(unit));
        return;
    }
    push_content(vec_);
    current_ = vec_;
}

bool MainWidget::format_xml()
{
    QFormatSet set;
    set.exec();

    if (!set.isok_) {
        return false;
    }

    std::string xml_path = set.xml_path_;
    if (xml_path.empty()) {
        return false;
    }

    if (!CUtil::affirm(
            this, u8"确认",
            u8"重排版内容将会覆盖源文件,请确认是否需要备份,继续?")) {
        return false;
    }

    if (!xml_.handle_save(xml_path, set.values_)) {
        CUtil::msg(this, u8"重排版内容失败");
        return false;
    }
    CUtil::msg(this, u8"重排版内容结束");
    return true;
}

std::string MainWidget::extract_prefix(const std::string& name)
{
    auto pos = name.find('.');
    return (pos == std::string::npos) ? name : name.substr(0, pos);
}

bool MainWidget::compare_by_prefix(const SElement_t& se1, const SElement_t& se2)
{
    std::string a = extract_prefix(se1.str);
    std::string b = extract_prefix(se2.str);
    if (a != b) {
        return a < b;
    }
    return se1.str < se2.str;   // 按照原始顺序排序
}

OperElement::OperElement(Element_t* ele, int row)
{
    element_ = ele;
    row_ = row;
}

SElement_t::SElement_t(Element_t* e, std::string& s)
{
    ele = e;
    str = std::move(s);
}