/***************************************************************************
 *   Copyright © 2009-2010 Jonathan Thomas <echidnaman@kubuntu.org>        *
 *   Copyright © 2026 Erich Eickmeyer <eeickmeyer@ubuntu.com>              *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or         *
 *   modify it under the terms of the GNU General Public License as        *
 *   published by the Free Software Foundation; either version 2 of        *
 *   the License or (at your option) version 3 or any later version        *
 *   accepted by the membership of KDE e.V. (or its successor approved     *
 *   by the membership of KDE e.V.), which shall act as a proxy            *
 *   defined in Section 14 of version 3 of the license.                    *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
 ***************************************************************************/

#include "installgui.h"

// Qt includes
#include <QDialog>
#include <QLabel>
#include <QProgressBar>
#include <QPushButton>
#include <QVBoxLayout>

// KDE includes
#include <KLocalizedString>
#include <KNotification>

// PackageKit
#include <PackageKit/Daemon>
#include <PackageKit/Transaction>

InstallGui::InstallGui(QObject* parent, const QString &application, const QMap<QString, QString> packageList)
        : QObject(parent)
        , m_dialog(nullptr)
        , m_applicationName(application)
        , m_currentTransaction(nullptr)
{
    m_dialog = new QDialog();
    m_dialog->setWindowIcon(QIcon::fromTheme(QStringLiteral("system-software-install")));
    m_dialog->setWindowTitle(i18n("Install Packages"));
    m_dialog->setMinimumWidth(420);
    QVBoxLayout *layout = new QVBoxLayout(m_dialog);

    QLabel *label = new QLabel(m_dialog);
    label->setWordWrap(true);
    label->setText(i18n("Select packages to be installed for extra functionality."
                        " These packages are not installed by default due to either patent"
                        " issues, restrictive licensing or a lack of space on the installation"
                        " media."));
    layout->addWidget(label);

    m_listWidget = new QListWidget(m_dialog);
    connect(m_listWidget, &QListWidget::itemChanged, this, &InstallGui::packageToggled);
    layout->addWidget(m_listWidget);

    m_progressBar = new QProgressBar(m_dialog);
    m_progressBar->setRange(0, 100);
    m_progressBar->setValue(0);
    m_progressBar->setVisible(false);
    layout->addWidget(m_progressBar);

    m_statusLabel = new QLabel(m_dialog);
    m_statusLabel->setVisible(false);
    layout->addWidget(m_statusLabel);

    m_buttonBox = new QDialogButtonBox(QDialogButtonBox::Cancel, m_dialog);
    m_installButton = new QPushButton(QIcon::fromTheme(QStringLiteral("download")),
                                      i18n("Install"), m_dialog);
    m_buttonBox->addButton(m_installButton, QDialogButtonBox::AcceptRole);
    connect(m_installButton, &QPushButton::clicked, this, &InstallGui::runPackageInstall);
    connect(m_buttonBox, &QDialogButtonBox::rejected, this, &InstallGui::cleanUpDialog);
    layout->addWidget(m_buttonBox);

    QMap<QString, QString>::const_iterator nameIter = packageList.constBegin();
    while (nameIter != packageList.constEnd()) {
        QListWidgetItem *item = new QListWidgetItem(nameIter.value());
        item->setData(Qt::UserRole, nameIter.key());
        item->setToolTip(nameIter.key());
        m_toInstallList << nameIter.key();
        item->setCheckState(Qt::Checked);
        m_listWidget->addItem(item);
        ++nameIter;
    }

    m_dialog->show();
}

InstallGui::~InstallGui()
{
    delete m_dialog;
}

void InstallGui::packageToggled(QListWidgetItem *item)
{
    QString packageName = item->data(Qt::UserRole).toString();
    if (item->checkState() == Qt::Checked) {
        if (!m_toInstallList.contains(packageName)) {
            m_toInstallList << packageName;
        }
    } else {
        m_toInstallList.removeAll(packageName);
    }
    m_installButton->setEnabled(!m_toInstallList.isEmpty());
}

void InstallGui::runPackageInstall()
{
    if (m_toInstallList.isEmpty()) {
        return;
    }

    setUiInstalling(true);
    m_statusLabel->setText(i18n("Resolving packages..."));
    m_resolvedPackageIDs.clear();

    m_currentTransaction = PackageKit::Daemon::resolve(m_toInstallList,
                                                       PackageKit::Transaction::FilterNotInstalled |
                                                       PackageKit::Transaction::FilterArch);

    connect(m_currentTransaction, &PackageKit::Transaction::package,
            this, &InstallGui::onPackageResolved);
    connect(m_currentTransaction, &PackageKit::Transaction::finished,
            this, &InstallGui::onResolveFinished);
    connect(m_currentTransaction, &PackageKit::Transaction::errorCode,
            this, &InstallGui::onInstallErrorCode);
}

void InstallGui::onPackageResolved(int info, const QString &packageID, const QString &summary)
{
    Q_UNUSED(info);
    Q_UNUSED(summary);
    m_resolvedPackageIDs << packageID;
}

void InstallGui::onResolveFinished(int status, uint runtime)
{
    Q_UNUSED(runtime);

    if (status != PackageKit::Transaction::ExitSuccess) {
        m_statusLabel->setText(i18n("Failed to resolve package names."));
        setUiInstalling(false);
        return;
    }

    if (m_resolvedPackageIDs.isEmpty()) {
        m_statusLabel->setText(i18n("No packages found to install."));
        setUiInstalling(false);
        return;
    }

    m_statusLabel->setText(i18n("Installing packages..."));
    m_progressBar->setValue(0);

    m_currentTransaction = PackageKit::Daemon::installPackages(m_resolvedPackageIDs);

    connect(m_currentTransaction, &PackageKit::Transaction::percentageChanged,
            this, [this]() { onInstallPercentage(m_currentTransaction->percentage()); });
    connect(m_currentTransaction, &PackageKit::Transaction::statusChanged,
            this, [this]() { onInstallStatusChanged(m_currentTransaction->status()); });
    connect(m_currentTransaction, &PackageKit::Transaction::finished,
            this, &InstallGui::onInstallFinished);
    connect(m_currentTransaction, &PackageKit::Transaction::errorCode,
            this, &InstallGui::onInstallErrorCode);
}

void InstallGui::onInstallPercentage(int percentage)
{
    if (percentage >= 0 && percentage <= 100) {
        m_progressBar->setValue(percentage);
    }
}

void InstallGui::onInstallStatusChanged(int status)
{
    m_statusLabel->setText(transactionStatusText(status));
}

void InstallGui::onInstallFinished(int status, uint runtime)
{
    Q_UNUSED(runtime);
    m_currentTransaction = nullptr;

    if (status == PackageKit::Transaction::ExitSuccess) {
        m_progressBar->setValue(100);
        m_statusLabel->setText(i18n("Installation complete."));

        KNotification *notify = new KNotification(QStringLiteral("Install"));
        notify->setComponentName(QStringLiteral("notificationhelper"));
        notify->setIconName(QStringLiteral("download"));
        notify->setText(i18n("Installation complete. You will need to restart %1"
                             " to use the new functionality", m_applicationName));
        notify->sendEvent();
    } else {
        m_statusLabel->setText(i18n("Installation failed."));
    }

    m_installButton->setVisible(false);
    m_buttonBox->setStandardButtons(QDialogButtonBox::Close);
    connect(m_buttonBox, &QDialogButtonBox::rejected, this, &InstallGui::cleanUpDialog);
}

void InstallGui::onInstallErrorCode(int code, const QString &details)
{
    Q_UNUSED(code);
    m_statusLabel->setText(i18n("Error: %1", details));
}

void InstallGui::setUiInstalling(bool installing)
{
    m_listWidget->setEnabled(!installing);
    m_installButton->setEnabled(!installing);
    m_progressBar->setVisible(installing);
    m_statusLabel->setVisible(true);
}

QString InstallGui::transactionStatusText(int status) const
{
    switch (status) {
    case PackageKit::Transaction::StatusWait:
    case PackageKit::Transaction::StatusWaitingForLock:
        return i18n("Waiting...");
    case PackageKit::Transaction::StatusSetup:
        return i18n("Setting up...");
    case PackageKit::Transaction::StatusRunning:
        return i18n("Installing...");
    case PackageKit::Transaction::StatusQuery:
        return i18n("Querying...");
    case PackageKit::Transaction::StatusRefreshCache:
        return i18n("Refreshing cache...");
    case PackageKit::Transaction::StatusDownload:
        return i18n("Downloading packages...");
    case PackageKit::Transaction::StatusInstall:
        return i18n("Installing packages...");
    case PackageKit::Transaction::StatusDepResolve:
        return i18n("Resolving dependencies...");
    case PackageKit::Transaction::StatusCommit:
        return i18n("Committing changes...");
    case PackageKit::Transaction::StatusCleanup:
        return i18n("Cleaning up...");
    case PackageKit::Transaction::StatusLoadingCache:
        return i18n("Loading...");
    case PackageKit::Transaction::StatusFinished:
        return i18n("Finished.");
    default:
        return i18n("Working...");
    }
}

void InstallGui::cleanUpDialog()
{
    if (m_dialog) {
        m_dialog->deleteLater();
        m_dialog = nullptr;
    }
}

#include "installgui.moc"
