/*
 * Copyright (C) 2023, KylinSoft Co., Ltd.
 *
 * 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 3 of the License, or
 * (at your option) any later version.
 *
 * 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 <https://www.gnu.org/licenses/>.
 *
 */

#include <QProcess>
#include <QQmlEngine>
#include <QMenu>
#include <QDBusReply>

#include "power-button.h"
#include "app-manager.h"

#define KYLIN_POWER_NORMAL_PATH_MAJOR "qrc:///res/icon/pad_mainpower.svg"

using namespace UkuiMenu;

PowerButton::PowerButton(QObject *parent) : QObject(nullptr)
{

}

void PowerButton::defineModule(const char *uri, int versionMajor, int versionMinor)
{
    qmlRegisterType<PowerButton>("org.ukui.menu.utils", versionMajor, versionMinor, "PowerButton");
}

PowerButton::~PowerButton()
= default;

QString PowerButton::getIcon()
{
    return "exit-symbolic";
}

QString PowerButton::getToolTip()
{
    return {tr("Power")};
}

void PowerButton::clicked(bool leftButtonClicked, int mouseX, int mouseY, bool isFullScreen)
{
    if (leftButtonClicked) {
        QProcess::startDetached("ukui-session-tools", {});
    } else {
        openMenu(mouseX, mouseY, isFullScreen);
    }
}

void PowerButton::openMenu(int menuX, int menuY, bool isFullScreen)
{
    if (m_contextMenu) {
        m_contextMenu.data()->close();
        return;
    }

    auto powerMenu = new QMenu;
    QDBusReply<bool> reply;
    QDBusInterface qDBusInterface("org.gnome.SessionManager", "/org/gnome/SessionManager",
                                  "org.gnome.SessionManager", QDBusConnection::sessionBus());

    if (canSwitch() && hasMultipleUsers()) {
        QAction *action = new QAction(tr("Switch user"), powerMenu);
        connect(action, &QAction::triggered, powerMenu, [] {
            AppManager::instance()->launchBinaryApp("ukui-session-tools", "--switchuser");
        });
        powerMenu->addAction(action);
    }

    reply = qDBusInterface.call("canHibernate");
    if (reply.isValid() && reply.value()) {
        QAction *action = new QAction(tr("Hibernate"), powerMenu);
        action->setToolTip(tr("<p>Turn off the computer, but the applications "
                              "will remain open. When you turn on the computer again, "
                              "you can return to the state you were in before</p>"));
        connect(action, &QAction::triggered, powerMenu, [] {
            AppManager::instance()->launchBinaryApp("ukui-session-tools", "--hibernate");
        });
        powerMenu->addAction(action);
    }

    reply = qDBusInterface.call("canSuspend");
    if (reply.isValid() && reply.value()) {
        QAction *action = new QAction(tr("Suspend"), powerMenu);
        action->setToolTip(tr("<p>The computer remains on but consumes less power, "
                              "and the applications will remain open. You can quickly wake "
                              "up the computer and return to the state you left</p>"));
        connect(action, &QAction::triggered, powerMenu, [] {
            AppManager::instance()->launchBinaryApp("ukui-session-tools", "--suspend");
        });
        powerMenu->addAction(action);
    }

    QAction *lockAction = new QAction(tr("Lock Screen"), powerMenu);
    connect(lockAction, &QAction::triggered, powerMenu, [] {
        AppManager::instance()->launchBinaryApp("ukui-screensaver-command", "-l");
    });
    powerMenu->addAction(lockAction);

    reply = qDBusInterface.call("canLogout");
    if (reply.isValid() && reply.value()) {
        QAction *action = new QAction(tr("Log Out"), powerMenu);
        action->setToolTip(tr("<p>The current user logs out of the system, ending "
                              "their session and returning to the login screen</p>"));
        connect(action, &QAction::triggered, powerMenu, [] {
            AppManager::instance()->launchBinaryApp("ukui-session-tools", "--logout");
        });
        powerMenu->addAction(action);
    }

    reply = qDBusInterface.call("canReboot");
    if (reply.isValid() && reply.value()) {
        QAction *action = new QAction(tr("Restart"), powerMenu);
        action->setToolTip(tr("<p>Close all applications, turn off the computer, and then turn it back on</p>"));
        connect(action, &QAction::triggered, powerMenu, [] {
            AppManager::instance()->launchBinaryApp("ukui-session-tools", "--reboot");
        });
        powerMenu->addAction(action);
    }

    reply = qDBusInterface.call("canPowerOff");
    if (reply.isValid() && reply.value()) {
        QAction *action = new QAction(tr("Shut Down"), powerMenu);
        action->setToolTip(tr("<p>Close all applications, and then turn off the computer</p>"));
        connect(action, &QAction::triggered, powerMenu, [] {
            AppManager::instance()->launchBinaryApp("ukui-session-tools", "--shutdown");
        });
        powerMenu->addAction(action);
    }
    //更新
    if (canUpgrade()) {
        QAction *upgradeReboot = new QAction(tr("Upgrade and Restart"), powerMenu);
        upgradeReboot->setToolTip(tr("Close all applications, upgrade the computer") +
                                     minutesToHour(m_estimatedLeftMinutes).toUtf8() + tr(", and then restart the computer"));
        connect(upgradeReboot, &QAction::triggered, this, [this] {
            execUpgradeAction("reboot");
        });
        powerMenu->addAction(upgradeReboot);

        QAction *upgradeshutdown = new QAction(tr("Upgrade and Shut Down"), powerMenu);
        upgradeshutdown->setToolTip(tr("Close all applications, upgrade the computer") +
                                     minutesToHour(m_estimatedLeftMinutes).toUtf8() +  tr(", and then turn off it"));
        connect(upgradeshutdown, &QAction::triggered, this, [this] {
            execUpgradeAction("shutdown");
        });
        powerMenu->addAction(upgradeshutdown);
    }

    m_contextMenu = powerMenu;
    powerMenu->setToolTipsVisible(true);
    powerMenu->setAttribute(Qt::WA_DeleteOnClose);

    if (isFullScreen) {
        m_contextMenu.data()->popup(QPoint(menuX - powerMenu->sizeHint().width(), menuY - powerMenu->sizeHint().height()));
    } else {
        m_contextMenu.data()->popup(QPoint(menuX, menuY - powerMenu->sizeHint().height()));
    }
}

bool PowerButton::hasMultipleUsers()
{
    QDBusInterface interface("org.freedesktop.Accounts", "/org/freedesktop/Accounts",
                             "org.freedesktop.DBus.Properties", QDBusConnection::systemBus());

    if (!interface.isValid()) {
        return false;
    }

    QDBusReply<QVariant> reply = interface.call("Get", "org.freedesktop.Accounts", "HasMultipleUsers");
    return reply.value().toBool();
}

bool PowerButton::canSwitch()
{
    QDBusInterface interface("org.freedesktop.DisplayManager", "/org/freedesktop/DisplayManager/Seat0",
                             "org.freedesktop.DBus.Properties", QDBusConnection::systemBus());

    if (!interface.isValid()) {
        return false;
    }

    QDBusReply<QVariant> reply = interface.call("Get", "org.freedesktop.DisplayManager.Seat", "CanSwitch");
    return reply.value().toBool();
}

bool PowerButton::canUpgrade()
{
    QDBusMessage msg = QDBusMessage::createMethodCall("com.kylin.systemupgrade", "/com/kylin/systemupgrade",
                                                      "com.kylin.systemupgrade.interface", "CheckInstallRequired");
    QDBusMessage res = QDBusConnection::systemBus().call(msg);
    if (res.type() == QDBusMessage::ReplyMessage && res.arguments().count() > 1) {
        m_estimatedLeftMinutes = res.arguments().at(1).toInt();
        return res.arguments().at(0).toBool();
    }
    if (res.type() == QDBusMessage::InvalidMessage || res.type() == QDBusMessage::ErrorMessage) {
        qCritical() << "Can't creat method call 'CheckInstallRequired'. Error: " << res.errorMessage();
        return false;
    }
    return false;
}

void PowerButton::execUpgradeAction(const QString &action)
{
    QDBusInterface interface("com.kylin.systemupgrade", "/com/kylin/systemupgrade",
                             "com.kylin.systemupgrade.interface", QDBusConnection::systemBus());

    if (interface.isValid()) {
        interface.asyncCall("TriggerInstallOnShutdown", action);
    } else {
        qCritical() << "'com.kylin.systemupgrade' interface is invalid!" <<  QDBusConnection::systemBus().lastError().message();
    }
}

QString PowerButton::minutesToHour(int estmateTime)
{
    if (estmateTime > 60) {
        int hour = estmateTime / 60;
        int min = estmateTime % 60;
        return tr("(Estimated %1 hour %2 minutes)").arg(hour).arg(min);
    } else if (estmateTime > 0) {
        return tr("(Estimated %1 minutes)").arg(estmateTime);
    }
    return {};
}
