//
// Created by ut003822@uos on 2022/6/20.
//

#include "UserDataBackupSelectWidget.h"
#include "utils/Utils.h"
#include <DLabel>
#include <DFontSizeManager>
#include <QHeaderView>
#include <DDialog>


UserDataBackupSelectWidget::UserDataBackupSelectWidget(DWidget *parent) : DWidget(parent)
{
    this->setAccessibleName("UserDataBackupSelectWidget");

    // 主题变换
    m_guiHelper = Dtk::Gui::DGuiApplicationHelper::instance();
    connect(m_guiHelper, &Dtk::Gui::DGuiApplicationHelper::themeTypeChanged,
            this, &UserDataBackupSelectWidget::onThemeChange);
    // 初始化UI
    initUI();
    onThemeChange();
}

void UserDataBackupSelectWidget::initUI()
{
    setFocusPolicy(Qt::NoFocus);
    QVBoxLayout *verticalLayout = new QVBoxLayout(this);
    verticalLayout->setContentsMargins(10, 20, 10, 10);
    verticalLayout->setAlignment(Qt::AlignHCenter);

    DLabel *title = new DLabel;
    title->setText(tr("Data Backup"));
    title->setAccessibleName("uos_UserDataBackupSelect_DataBackupTitle");
    title->setAlignment(Qt::AlignCenter);
    DFontSizeManager::instance()->bind(title, DFontSizeManager::T4);
    QFont font = title->font();
    font.setWeight(QFont::Bold);
    verticalLayout->addWidget(title);


    DLabel *subTitle = new DLabel;
    subTitle->setText(tr("Select the data to back up and customize a backup location."));
    subTitle->setAccessibleName("uos_UserDataBackupSelect_DataBackupSubTitle");
    subTitle->setAlignment(Qt::AlignCenter);
    verticalLayout->addWidget(subTitle);

    m_model = new QStandardItemModel;
    m_model->setObjectName("ID_UserDataBackupSelectWidget");
    m_treeView = new SelectFileTreeView;
    m_treeView->setAccessibleName("uos_UserDataBackupSelect_TreeView");
    m_treeView->setModel(m_model);
    m_treeView->setItemsExpandable(true);
    m_treeView->header()->setSortIndicatorShown(false);
    //m_treeView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
    m_treeView->setAlwaysChecked(false);
    verticalLayout->addWidget(m_treeView);
    verticalLayout->addSpacing(20);

    QHBoxLayout *hBoxlayout1 = new QHBoxLayout;
    DLabel *lbStoreDir = new DLabel;
    lbStoreDir->setText(tr("Backup directory"));
    lbStoreDir->setAccessibleName("uos_UserDataBackupSelect_BackupDirectory");
    lbStoreDir->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
    hBoxlayout1->addWidget(lbStoreDir);
    hBoxlayout1->addSpacing(8);
    m_comboBox = new DComboBox;
    m_comboBox->setAccessibleName("uos_UserDataBackupSelect_ComboBox");
    m_comboBox->setFixedWidth(734);
    hBoxlayout1->addWidget(m_comboBox);
    verticalLayout->addLayout(hBoxlayout1);
    verticalLayout->addSpacing(10);

    QHBoxLayout *loadingHboxLayout = new QHBoxLayout;
    m_spinner = new DSpinner(this);
    m_spinner->setAccessibleName("uos_UserDataBackupSelect_DSpinner");
    m_spinner->setFocusPolicy(Qt::NoFocus);
    m_spinner->setFixedSize(16,16);
    m_spinner->hide();
    loadingHboxLayout->addStretch();
    loadingHboxLayout->addWidget(m_spinner);
    loadingHboxLayout->addSpacing(10);

    m_tips= new DLabel;
    m_tips->setAccessibleName("uos_UserDataBackupSelect_Tips");
    m_tips->setAlignment(Qt::AlignCenter);
    m_tips->setFocusPolicy(Qt::NoFocus);
    m_tips->setStyleSheet("QLabel {"
                          "color: #FF5736;"
                          "}");
    loadingHboxLayout->addWidget(m_tips);
    loadingHboxLayout->addStretch();
    verticalLayout->addLayout(loadingHboxLayout);
    verticalLayout->addSpacing(10);

    QHBoxLayout *cancelAndNextHbox = new QHBoxLayout;
    cancelAndNextHbox->setAlignment(Qt::AlignHCenter);
    m_cancelBtn = new DPushButton;
    m_cancelBtn->setText(tr("Cancel", "button"));
    m_cancelBtn->setAccessibleName("uos_UserDataBackupSelect_Cancel");
    m_cancelBtn->setFixedSize(140, 36);

    m_nextBtn = new DSuggestButton;
    m_nextBtn->setText(tr("Next"));
    m_nextBtn->setAccessibleName("uos_UserDataBackupSelect_Next");
    m_nextBtn->setFixedSize(140, 36);
    cancelAndNextHbox->addWidget(m_cancelBtn);
    cancelAndNextHbox->addWidget(m_nextBtn);
    verticalLayout->addLayout(cancelAndNextHbox);

    connect(m_treeView, &QTreeView::clicked, this, &UserDataBackupSelectWidget::onItemClicked);
    connect(m_treeView, &SelectFileTreeView::checkAllChanged, this, &UserDataBackupSelectWidget::onCheckAllChanged);
    connect(m_model, &QStandardItemModel::itemChanged, this, &UserDataBackupSelectWidget::onTreeViewItemChanged);
    connect(m_cancelBtn, &QPushButton::clicked, this, &UserDataBackupSelectWidget::cancel);
    connect(m_nextBtn, &DSuggestButton::clicked, [this] {
        Q_EMIT next();
    });
    connect(m_comboBox, static_cast<void (DComboBox::*)(int)> (&DComboBox::currentIndexChanged),
            this, &UserDataBackupSelectWidget::onComboBoxCurrentIndexChanged);
}

void UserDataBackupSelectWidget::onComboBoxCurrentIndexChanged(int index)
{
    this->setTips("");
}

void UserDataBackupSelectWidget::startSpinner()
{
    if (m_spinner != nullptr) {
        m_spinner->show();
        m_spinner->start();
    }
}

void UserDataBackupSelectWidget::stopSpinner()
{
    if (m_spinner != nullptr) {
        m_spinner->stop();
        m_spinner->hide();
    }
}

void UserDataBackupSelectWidget::setTipsStyleSheet(const QString &styleSheet)
{
    if (m_tips != nullptr) {
        m_tips->setStyleSheet(styleSheet);
    }
}

void UserDataBackupSelectWidget::setTips(const QString &tips)
{
    if (m_tips != nullptr) {
        m_tips->setText(tips);
    }
}

void UserDataBackupSelectWidget::setNextBtnEnabled(bool enabled)
{
    if (m_nextBtn != nullptr) {
        m_nextBtn->setEnabled(enabled);
    }
}

void UserDataBackupSelectWidget::setDestDevice(const QJsonObject &jsonObject)
{
    m_comboBox->clear();
    m_partitionList.clear();
    QJsonArray disks = jsonObject.value("disks").toArray();
    for (auto a : disks) {
        QString diskName = a.toObject().value("disk").toString();
        QJsonArray partitions = a.toObject().value("partitions").toArray();
        for (auto p : partitions) {
            Partition partition;
            partition.unmarshal(p.toObject());
            m_partitionList.append(partition);
            m_comboBox->addItem(QString("%1(%2),  %3,  (%4/%5)").arg(diskName).arg(partition.fsType)
                                        .arg(partition.name).arg(Utils::byte2DisplaySize(partition.used)).arg(Utils::byte2DisplaySize(partition.size)));
        }
    }
}

QString UserDataBackupSelectWidget::getDestDeviceUUID()
{
    if (m_partitionList.isEmpty()) {
        DDialog dialog(this);
        dialog.setMessage(tr("Please insert a removable disk, supported format: ext4, btrfs, xfs"));
        dialog.setIcon(QIcon::fromTheme("dialog-warning"));
        QRect rect = geometry();
        dialog.move(rect.center());
        dialog.moveToCenter();
        dialog.exec();
        return "";
    } else if (m_comboBox->count() == m_partitionList.size()) {
        Partition selectPartition = m_partitionList.at(m_comboBox->currentIndex());
        if (!selectPartition.fsTypeSupportedDataBackup) {
            DDialog dlg(this);
            dlg.setMessage(::QObject::tr("The file system of the device is unsupported. Please select one in ext4, btrfs, xfs format."));
            dlg.setIcon(QIcon::fromTheme("dialog-warning"));
            QRect rect = geometry();
            dlg.move(rect.center());
            dlg.moveToCenter();
            dlg.exec();
            return "";
        }
        return selectPartition.uuid;
    }
    return "";
}

QString UserDataBackupSelectWidget::getCurComboBoxText()
{
    return m_comboBox->currentText();
}

void UserDataBackupSelectWidget::setFiles(const QList<FileItem> &fileItemList)
{
    m_model->clear();
    m_model->setHorizontalHeaderLabels({tr("Name"), tr("Type")});

    auto root = m_model->invisibleRootItem();
    for (auto &file : fileItemList) {
        auto nameItem = new QStandardItem(file.fileName);
        nameItem->setCheckable(true);
        nameItem->setCheckState(Qt::Checked);
        QString fileType = file.fileType == FileType::Dir ? tr("Directory") : tr("File");
        auto typeItem = new QStandardItem(fileType);
        if (file.fileType == FileType::Dir) {
            nameItem->setIcon(QIcon::fromTheme(":/resources/icons/folder.svg"));
        } else {
            nameItem->setIcon(QIcon::fromTheme(":/resources/icons/file.svg"));
        }

        int singleParentDir = 1;
        nameItem->setData(singleParentDir, Qt::UserRole + 10);
        root->appendRow({nameItem, typeItem});
        if (!children().isEmpty()) {
            int subDirs = 2;
            for (auto &child : file.children) {
                auto childNameItem = new QStandardItem(child.fileName);
                childNameItem->setCheckable(true);
                childNameItem->setCheckState(Qt::Checked);
                QString childFileType = child.fileType == FileType::Dir ? tr("Directory") : tr("File");
                auto childTypeItem = new QStandardItem(childFileType);
                if (child.fileType == FileType::Dir) {
                    childNameItem->setIcon(QIcon::fromTheme(":/resources/icons/folder.svg"));
                } else {
                    childNameItem->setIcon(QIcon::fromTheme(":/resources/icons/file.svg"));
                }
                nameItem->setData(subDirs, Qt::UserRole + 10);
                nameItem->appendRow({childNameItem, childTypeItem});
            }
        }
    }

    m_treeView->setColumnWidth(0, 650);
    QHeaderView * headerView = m_treeView->header();
    headerView->setSectionResizeMode(0, QHeaderView::ResizeMode::Fixed);
    headerView->setSectionResizeMode(1, QHeaderView::ResizeMode::Fixed);
    updateCheckAllState();
}

QList<FileItem> UserDataBackupSelectWidget::getExclude()
{
    QList<FileItem> result;
    auto root = m_model->invisibleRootItem();
    if (root == nullptr) {
        return result;
    }
    int row = 0;
    while (root->child(row) != nullptr) {
        FileItem fileItem;
        auto item = root->child(row, 0);
        if (item == nullptr) {
            continue;
        }

        auto siblingItem = m_model->itemFromIndex(item->index().siblingAtColumn(1));
        if (siblingItem == nullptr) {
            continue;
        }
        if (siblingItem->text() == tr("Directory")) {
            fileItem.fileType = FileType::Dir;
        } else {
            fileItem.fileType = FileType::File;
        }
#ifdef QT_DEBUG
        qInfo() << item->text();
#endif
        if (item->checkState() == Qt::Unchecked) {
            fileItem.fileName = item->text();
            result.append(fileItem);
        } else if (item->checkState() == Qt::PartiallyChecked) {
            fileItem.fileName = item->text();
            int childRow = 0;
            while (item->child(childRow, 0) != nullptr) {
                FileItem childFileItem;
                auto childItem = item->child(childRow, 0);
                if (childItem->checkState() == Qt::Unchecked) {
                    childFileItem.fileName = childItem->text();
                    auto childSiblingItem = m_model->itemFromIndex(childItem->index().siblingAtColumn(1));
                    if (childSiblingItem == nullptr) {
                        continue;
                    }
                    if (childSiblingItem->text() == tr("Directory")) {
                        childFileItem.fileType = FileType::Dir;
                    } else {
                        childFileItem.fileType = FileType::File;
                    }
                    fileItem.children.append(childFileItem);
                }
                childRow++;
            }
            result.append(fileItem);
        }
        row++;
    }
    return result;
}

void UserDataBackupSelectWidget::onThemeChange()
{
    if (DGuiApplicationHelper::instance()->themeType() == DGuiApplicationHelper::ColorType::LightType) {
        m_treeView->setStyleSheet("QTreeView::indicator:enabled:checked {"
                                  "image: url(:/resources/icons/Checked-Normal-Light.svg)"
                                  "}"
                                  "QTreeView::indicator:enabled:unchecked {"
                                  "image: url(:/resources/icons/UnChecked-Normal-Light.svg)"
                                  "}"
                                  "QTreeView::indicator:enabled:indeterminate {"
                                  "image: url(:/resources/icons/Mixed-Normal-Light.svg)"
                                  "}");
    }

}

void UserDataBackupSelectWidget::onItemClicked(const QModelIndex &index)
{
    if (!index.isValid()) {
        return;
    }

    m_itemClicked = true;

    Qt::CheckState checkState;
    auto clickItem = m_model->itemFromIndex(index.siblingAtColumn(0));
    if (m_itemChanged) {
        checkState = clickItem->checkState();
        m_itemChanged = false;
    } else {
        if (clickItem->checkState() == Qt::Checked || clickItem->checkState() == Qt::PartiallyChecked) {
            clickItem->setCheckState(Qt::Unchecked);
            checkState = Qt::Unchecked;
        } else {
            clickItem->setCheckState(Qt::Checked);
            checkState = Qt::Checked;
        }
    }

    if (clickItem->hasChildren()) {
        setChildState(clickItem, checkState);
    }
    if (clickItem->parent() != nullptr) {
        setParentState(clickItem, checkState);
    }

    updateCheckAllState();
    m_itemClicked = false;
}

void UserDataBackupSelectWidget::onTreeViewItemChanged(QStandardItem * item)
{
    if (!m_itemClicked) {
        m_itemChanged = true;
    }
}

void UserDataBackupSelectWidget::setChildState(QStandardItem *curItem, Qt::CheckState checkState)
{
    auto rowCount = curItem->rowCount();
    for (int i = 0; i < rowCount; ++i) {
        auto child = curItem->child(i);
#ifdef QT_DEBUG
        qInfo() << "curItem check state is : " << checkState;
#endif
        child->setCheckState(checkState);
        if (child->hasChildren()) {
            setChildState(child, checkState);
        }
    }
}

void UserDataBackupSelectWidget::setParentState(QStandardItem *curItem, Qt::CheckState checkState)
{
    auto parent = curItem->parent();
    if (parent == nullptr) {
        return;
    }

    auto rowCount = parent->rowCount();
    int checkCount = 0;
    for (int i = 0; i < rowCount; ++i) {
        auto child = parent->child(i);
        if (child->checkState() == Qt::Checked || child->checkState() == Qt::PartiallyChecked) {
            checkCount++;
        }
    }

    if (checkCount == rowCount) {
        parent->setCheckState(Qt::Checked);
    } else if (checkCount == 0) {
        parent->setCheckState(Qt::Unchecked);
    } else {
        parent->setCheckState(Qt::PartiallyChecked);
    }

    if (parent->parent() != nullptr) {
        setParentState(parent, checkState);
    }
}

void UserDataBackupSelectWidget::onCheckAllChanged(Qt::CheckState checkState)
{
    auto root = m_model->invisibleRootItem();
    if (root == nullptr) {
        return;
    }

    int row = 0;
    while (root->child(row) != nullptr) {
        auto item = root->child(row, 0);
        if (item == nullptr) {
            continue;
        }

        if (checkState == Qt::Checked) {
            item->setCheckState(Qt::Checked);
            m_nextBtn->setDisabled(false);
        } else if (checkState == Qt::Unchecked) {
            item->setCheckState(Qt::Unchecked);
            m_nextBtn->setDisabled(true);
        }

        int childRow = 0;
        while (item->child(childRow, 0) != nullptr) {
            auto childItem = item->child(childRow, 0);
            if (checkState == Qt::Checked) {
                childItem->setCheckState(Qt::Checked);
            } else if (checkState == Qt::Unchecked) {
                childItem->setCheckState(Qt::Unchecked);
            }
            ++childRow;
        }

        ++row;
    }
}

void UserDataBackupSelectWidget::updateCheckAllState()
{
    auto root = m_model->invisibleRootItem();
    if (root == nullptr) {
        return;
    }

    int row = 0;
    int checkedCount = 0;
    int rootRowCount = root->rowCount();
    while (root->child(row) != nullptr) {
        auto item = root->child(row, 0);
        if (item == nullptr) {
            continue;
        }

        if (item->checkState() == Qt::Checked) {
            ++checkedCount;
        } else if (item->checkState() == Qt::PartiallyChecked) {
            m_treeView->setCheckAllState(Qt::PartiallyChecked);
            m_nextBtn->setDisabled(false);
            return;
        }

        ++row;
    }

    m_nextBtn->setDisabled(false);
    if (checkedCount >= rootRowCount) {
        m_treeView->setCheckAllState(Qt::Checked);
    } else if (checkedCount > 0 && checkedCount < rootRowCount){
        m_treeView->setCheckAllState(Qt::PartiallyChecked);
    } else {
        m_treeView->setCheckAllState(Qt::Unchecked);
        m_nextBtn->setDisabled(true);
    }
}
