
/*
 * Copyright (C) 2024, 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/>.
 *
 * Authors: hxf <hewenfei@kylinos.cn>
 *
 */

#include "start-menu-button.h"

#include <QDBusConnection>
#include <QDebug>
#include <QAction>
#include <QDBusReply>
#include <window-manager.h>
#include <QGSettings>
#include <app-launcher.h>
#include <dbus-connector.h>
#include <function-control.h>
#include <QJsonObject>


StartMenuButton &StartMenuButton::self()
{
    static StartMenuButton s_self;
    return s_self;
}

StartMenuButton::StartMenuButton(QObject *parent) : QObject(parent)
{
    qRegisterMetaType<QList<QAction*>>();
    loadUserAction();
    loadPowerAction();
    connectGlobalInterface();
    initFunctionControl();

    m_applicationInfo = new UkuiSearch::ApplicationInfo(this);
}

StartMenuButton::~StartMenuButton()
{
    UkuiQuick::FunctionControl::instance()->removeWatch(QStringLiteral("ukui-menu-starter-v11"));
}

void StartMenuButton::openStartMenu()
{
    QDBusInterface interface("org.ukui.menu", "/org/ukui/menu", "org.ukui.menu", QDBusConnection::sessionBus());
    interface.asyncCall(QStringLiteral("WinKeyResponse"));
}

void StartMenuButton::showDesktop()
{
    UkuiQuick::WindowManager::setShowingDesktop(!UkuiQuick::WindowManager::showingDesktop());
}

void StartMenuButton::execSessionAction(const QString &action)
{
    QDBusInterface sessionManager("org.gnome.SessionManager", "/org/gnome/SessionManager",
                                  "org.gnome.SessionManager", QDBusConnection::sessionBus());
    sessionManager.asyncCall(action);
}

void StartMenuButton::onUserCountChange()
{
    bool hasMultipleUsers {false}, canSwitch {false};

    QDBusInterface displaynManager("org.freedesktop.DisplayManager", "/org/freedesktop/DisplayManager/Seat0",
                                   "org.freedesktop.DBus.Properties", QDBusConnection::systemBus());
    QDBusReply<QVariant> reply = displaynManager.call("Get", "org.freedesktop.DisplayManager.Seat", "CanSwitch");
    canSwitch = (reply.isValid() && reply.value().toBool());

    QDBusInterface interface("org.freedesktop.Accounts", "/org/freedesktop/Accounts",
                             "org.freedesktop.Accounts", QDBusConnection::systemBus());
    QDBusReply<QList<QDBusObjectPath>> getReply = interface.call(QStringLiteral("ListCachedUsers"));
    hasMultipleUsers = getReply.isValid() && getReply.value().toVector().size() > 1;

    if(hasMultipleUsers && canSwitch){
        m_switchUserAction->setVisible(true);
    } else {
        m_switchUserAction->setVisible(false);
    }
}

void StartMenuButton::loadUserAction()
{
    // sessionManager
    QDBusInterface sessionManager("org.gnome.SessionManager", "/org/gnome/SessionManager",
                                  "org.gnome.SessionManager", QDBusConnection::sessionBus());

    QDBusReply<bool> reply = sessionManager.call(QStringLiteral("canLockscreen"));
    if (reply.isValid() && reply.value()) {
        m_userActions << new QAction(QIcon::fromTheme("system-lock-screen-symbolic"), tr("Lock Screen"), this);
        connect(m_userActions.last(), &QAction::triggered, this, [this] {
            UkuiQuick::AppLauncher::instance()->runCommand("ukui-screensaver-command -l");
        });
    }

    if(!m_switchUserAction){
        m_switchUserAction = new QAction(QIcon::fromTheme("system-users-symbolic"), tr("Switch User"), this);
        connect(m_switchUserAction, &QAction::triggered, this, [this] {
            execSessionAction(QStringLiteral("switchUser"));
        });
    }
    m_userActions << m_switchUserAction;

    reply = sessionManager.call(QStringLiteral("canLogout"));
    if (reply.isValid() && reply.value()) {
        m_userActions << new QAction(QIcon::fromTheme("system-log-out-symbolic"), tr("Log Out"), this);
        connect(m_userActions.last(), &QAction::triggered, this, [this] {
            execSessionAction(QStringLiteral("logout"));
        });
    }

    onUserCountChange();

    QDBusConnection::systemBus().connect(ACCOUNTS_SERVICE_NAME,
                                         ACCOUNTS_SERVICE_PATH,
                                         ACCOUNTS_SERVICE_INTERFACE,
                                         "UserAdded",this,SLOT(onUserCountChange()));
    QDBusConnection::systemBus().connect(ACCOUNTS_SERVICE_NAME,
                                         ACCOUNTS_SERVICE_PATH,
                                         ACCOUNTS_SERVICE_INTERFACE,
                                         "UserDeleted",this,SLOT(onUserCountChange()));
}

void StartMenuButton::loadPowerAction()
{
    QDBusInterface sessionManager("org.gnome.SessionManager", "/org/gnome/SessionManager",
                                  "org.gnome.SessionManager", QDBusConnection::sessionBus());

    QDBusReply<bool> reply = sessionManager.call(QStringLiteral("canSuspend"));
    if (reply.isValid() && reply.value()) {
        m_powerActions << new QAction(QIcon::fromTheme("ukui-sleep-symbolic"), tr("Suspend"), this);
        connect(m_powerActions.last(), &QAction::triggered, this, [this] {
            execSessionAction(QStringLiteral("suspend"));
        });
    }

    reply = sessionManager.call(QStringLiteral("canHibernate"));
    if (reply.isValid() && reply.value()) {
        m_powerActions << new QAction(QIcon::fromTheme("ukui-hibernate-symbolic"), tr("Hibernate"), this);
        connect(m_powerActions.last(), &QAction::triggered, this, [this] {
            execSessionAction(QStringLiteral("hibernate"));
        });
    }

    const QByteArray id("org.ukui.time-shutdown.settings");
    if (QGSettings::isSchemaInstalled(id)) {
        m_powerActions << new QAction(QIcon::fromTheme("timer-symbolic"), tr("Time Shutdown"), this);
        connect(m_powerActions.last(), &QAction::triggered, this, [this] {
            UkuiQuick::AppLauncher::instance()->runCommand("time-shutdown");
        });
    }

    //更新并关机
    bool isUpgradable = canUpgrade();
    //存在更新时是否隐藏关机和重启
    bool isNeedToHide = checkHideUpdate();
    if (!m_upgradeShutdownAct) {
        m_upgradeShutdownAct = new QAction(QIcon::fromTheme("system-upd-shutdown-symbolic"), tr("Upgrade and Shut Down"), this);
        m_upgradeShutdownAct->setVisible(isUpgradable);
        m_upgradeShutdownAct->setToolTip(minutesToHour(m_estimatedLeftMinutes));
        m_powerActions.append(m_upgradeShutdownAct);
        connect(m_upgradeShutdownAct, &QAction::triggered, this, [this] {
            execUpgradeAction("shutdown");
        });
    }
    //关机
    reply = sessionManager.call(QStringLiteral("canPowerOff"));
    if (reply.isValid() && reply.value() && !m_shutdownAct) {
        m_shutdownAct = new QAction(QIcon::fromTheme("system-shutdown-symbolic"), tr("Shut Down"), this);
        m_shutdownAct->setVisible(!(isUpgradable && isNeedToHide));
        m_powerActions.append(m_shutdownAct);
        connect(m_shutdownAct, &QAction::triggered, this, [this] {
            execSessionAction(QStringLiteral("powerOff"));
        });
    }

    //更新并重启
    if (!m_upgradeRebootAct) {
        m_upgradeRebootAct = new QAction(QIcon::fromTheme("system-upd-reboot-symbolic"), tr("Upgrade and Restart"), this);
        m_upgradeRebootAct->setVisible(isUpgradable);
        m_upgradeRebootAct->setToolTip(minutesToHour(m_estimatedLeftMinutes));
        m_powerActions.append(m_upgradeRebootAct);
        connect(m_upgradeRebootAct, &QAction::triggered, this, [this] {
            execUpgradeAction("reboot");
        });
    }
    //重启
    reply = sessionManager.call(QStringLiteral("canReboot"));
    if (reply.isValid() && reply.value() && !m_rebootAct) {
        m_rebootAct = new QAction(QIcon::fromTheme("system-reboot-symbolic"), tr("Restart"), this);
        m_rebootAct->setVisible(!(isUpgradable && isNeedToHide));
        m_powerActions.append(m_rebootAct);
        connect(m_rebootAct, &QAction::triggered, this, [this] {
            execSessionAction(QStringLiteral("reboot"));
        });
    }

    QDBusConnection::systemBus().connect(SYSTEMUPGRADE_SERVICE,
                                         SYSTEMUPGRADE_PATH,
                                         SYSTEMUPGRADE_INTERFACE,
                                         "UpdateDownloadFinished",this,SLOT(onUpdateDownloadFinished()));
}

void StartMenuButton::openGlobalSearch() {
    if (m_searchInterface) {
        m_searchInterface->asyncCall(QStringLiteral("mainWindowSwitch"));
    } else {
        qDebug() << "Can't connect to ukui-search DBus!";
        UkuiQuick::AppLauncher::instance()->launchApp("/usr/share/applications/ukui-search-menu.desktop");
    }
}

QString StartMenuButton::getAppLocalName(QString desktop)
{
    if (!m_applicationInfo)
        return {};

    QString fullPath;
    m_applicationInfo->desktopFilePathFromName(desktop, fullPath);
    QVariant localName = m_applicationInfo->getInfo(fullPath, UkuiSearch::ApplicationProperty::Property::LocalName);
    return localName.toString();
}

bool StartMenuButton::disableMouseLeftButton() const
{
    return m_disableMouseLeftButton;
}

bool StartMenuButton::disableMouseRightButton() const
{
    return m_disableMouseRightButton;
}

void StartMenuButton::connectGlobalInterface() {
    UkuiQuick::DbusConnector *dbusCon = new UkuiQuick::DbusConnector("com.ukui.search.service", "/",
                                                                     "org.ukui.search.service",
                                                                     QDBusConnection::sessionBus());
    connect(dbusCon, &UkuiQuick::DbusConnector::ready, this, [this](QDBusInterface *dbus) {
        m_searchInterface = dbus;
    });
    dbusCon->start();
}

void StartMenuButton::initFunctionControl()
{
    //管控配置
    UkuiQuick::FunctionControl::instance()->addWatch(QStringLiteral("ukui-menu-starter-v11"));
    onFunctionControlChanged(QStringLiteral("ukui-menu-starter-v11"), false);
    connect(UkuiQuick::FunctionControl::instance(), &UkuiQuick::FunctionControl::functionControlChanged, this, [this](const QString &name) {
        onFunctionControlChanged(name, false);
    });
    connect(UkuiQuick::FunctionControl::instance(), &UkuiQuick::FunctionControl::functionControlExit, this, [this]() {
        onFunctionControlChanged(QStringLiteral("ukui-menu-starter-v11"), true);
    });
}

void StartMenuButton::onFunctionControlChanged(const QString &name, bool exit)
{
    if (name != QStringLiteral("ukui-menu-starter-v11")) {
        return;
    }

    auto controlConfig = exit ? QJsonObject(): UkuiQuick::FunctionControl::instance()->functionControlObject(name).value(name).toObject();

    qDebug() << "StartMenuButton function control config:" << controlConfig;
    //是否禁用左键点击
    if (controlConfig.contains(QStringLiteral("disableMouseLeftButton"))) {
        bool disable = controlConfig.value(QStringLiteral("disableMouseLeftButton")).toBool();
        if (disable != m_disableMouseLeftButton) {
            m_disableMouseLeftButton = disable;
            Q_EMIT disableMouseLeftButtonChanged();
        }
    } else {
        m_disableMouseLeftButton = false;
        Q_EMIT disableMouseLeftButtonChanged();
    }
    //是否禁用右键点击
    if (controlConfig.contains(QStringLiteral("disableMouseRightButton"))) {
        bool disable = controlConfig.value(QStringLiteral("disableMouseRightButton")).toBool();
        if (disable != m_disableMouseRightButton) {
            m_disableMouseRightButton = disable;
            Q_EMIT disableMouseRightButtonChanged();
        }
    } else {
        m_disableMouseRightButton = false;
        Q_EMIT disableMouseRightButtonChanged();
    }
}

bool StartMenuButton::canUpgrade()
{
    QDBusMessage msg = QDBusMessage::createMethodCall(SYSTEMUPGRADE_SERVICE,
                                                      SYSTEMUPGRADE_PATH,
                                                      SYSTEMUPGRADE_INTERFACE,
                                                      CHECK_INSTALL_REQUIRED);
    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();
    }
    return false;
}

QString StartMenuButton::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 {};
}

bool StartMenuButton::checkHideUpdate()
{
    QDBusMessage msg = QDBusMessage::createMethodCall(SYSTEMUPGRADE_SERVICE,
                                                      SYSTEMUPGRADE_PATH,
                                                      SYSTEMUPGRADE_INTERFACE,
                                                      CHECK_HIDE_UPDATE);
    QDBusMessage res = QDBusConnection::systemBus().call(msg);
    if (res.type() == QDBusMessage::ReplyMessage) {
        return res.arguments().at(0).toBool();
    }
    qWarning() << "Call 'com.kylin.systemupgrade' interface method 'CheckHideUpdate' failed" << res.errorMessage();
    return false;
}

void StartMenuButton::execUpgradeAction(const QString &action)
{
    QDBusInterface interface(SYSTEMUPGRADE_SERVICE,
                             SYSTEMUPGRADE_PATH,
                             SYSTEMUPGRADE_INTERFACE,
                             QDBusConnection::systemBus());

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

void StartMenuButton::onUpdateDownloadFinished()
{
    qDebug() << "Update download finished. Add two actions in Power Option";
    bool isUpgradable = canUpgrade();
    if (m_upgradeRebootAct) {
        m_upgradeRebootAct->setVisible(isUpgradable);
        m_upgradeRebootAct->setToolTip(minutesToHour(m_estimatedLeftMinutes));
    }
    if (m_upgradeShutdownAct) {
        m_upgradeShutdownAct->setVisible(isUpgradable);

        m_upgradeShutdownAct->setToolTip(minutesToHour(m_estimatedLeftMinutes));
    }

    bool isNeedToHide = checkHideUpdate();
    qDebug() << "Update download finished. Hide shutdown and reboot or not: " << isNeedToHide;
    if (m_rebootAct)
        m_rebootAct->setVisible(isNeedToHide);
    if (m_shutdownAct)
        m_shutdownAct->setVisible(isNeedToHide);
}
