/*
 * Copyright 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 <http://www.gnu.org/licenses/>.
 */

#include "sysdbusregister.h"

#include <QDebug>

SysdbusRegister::SysdbusRegister()
{
    getCpuInfo();

    m_powerConfig = new PowerConfig(this);

    if (true == m_powerConfig->isFirstRun()) {
        m_powerConfig->updatePowerConfig(GSETTINGS_KEY_BRIGHTNESS_AC,
                                         QString::number(DEFAULE_BRIGHTNESS_PERCNET));
    }
    int brightnessPercent = m_powerConfig->getPowerConfig(GSETTINGS_KEY_BRIGHTNESS_AC).toInt();
    m_brightnessNode = new BrightnessNode(brightnessPercent, this);

    m_susThenHibSet = new QSettings(QString("/etc/systemd/sleep.conf"), QSettings::IniFormat, this);
    m_susThenHibSet->setIniCodec("UTF-8");

    m_devPowerControl = new DevPowerControl(this);
}

SysdbusRegister::~SysdbusRegister()
{
    delete m_susThenHibSet;
}

void SysdbusRegister::getCpuInfo()
{
    m_cpuCoreNum = executeLinuxCmd("grep -c 'processor' /proc/cpuinfo").toInt();

    QString result = executeLinuxCmd("cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors");
    if (result.contains("schedutil")) {
        m_cpuFreqMode[Balance] = "schedutil";
    } else if (result.contains("ondemand")) {
        m_cpuFreqMode[Balance] = "ondemand";
    } else {
        m_cpuFreqMode[Balance] = "powersave";
    }
}

QString SysdbusRegister::executeLinuxCmd(QString strCmd)
{
    QProcess process;
    //调用linux终端命令
    process.start("bash", QStringList() << "-c" << strCmd);
    process.waitForFinished();
    QString strResult = process.readAllStandardOutput() + process.readAllStandardError();
    return strResult;
}

QString SysdbusRegister::canControl(const QString control)
{
    QDBusInterface interface(
        "org.freedesktop.login1",
        "/org/freedesktop/login1",
        "org.freedesktop.login1.Manager",
        QDBusConnection::systemBus());
    QDBusReply<QString> reply = interface.call(control);
    if (reply.isValid()) {
        if ("yes" == reply.value()) {
            return reply;
        } else {
            QDBusMessage message = QDBusMessage::createSignal("/", "org.ukui.powermanagement.interface", "CanControl");
            message << reply.value();
            QDBusConnection::systemBus().send(message);
            return reply;
        }
    } else {
        return "error";
    }
}

void SysdbusRegister::controlLogin1Connection(QString type)
{
    bool ctrl = 1;
    QDBusInterface interface(
        "org.freedesktop.login1",
        "/org/freedesktop/login1",
        "org.freedesktop.login1.Manager",
        QDBusConnection::systemBus());
    interface.call(type, ctrl);
}

void SysdbusRegister::blockLidAction()
{
    QDBusInterface interface(
        QStringLiteral("org.freedesktop.login1"),
        QStringLiteral("/org/freedesktop/login1"),
        QStringLiteral("org.freedesktop.login1.Manager"),
        QDBusConnection::systemBus());
    QDBusReply<QDBusUnixFileDescriptor> reply = interface.call(
        QStringLiteral("Inhibit"),
        QStringLiteral("handle-lid-switch"),
        QStringLiteral("powermanagment"),
        QStringLiteral("ukui-powermanagement blocked LidSwitch!"),
        QStringLiteral("block"));
    if (reply.isValid()) {
        m_login1Lock = reply.value();
        m_blockLidAction = true;
        qDebug() << "Inhibit got:" << m_login1Lock.fileDescriptor();
    } else {
        qDebug() << "Error from inhibit:" << reply.error();
    }
}

void SysdbusRegister::releaseLidAction()
{
    m_blockLidAction = false;
}

void SysdbusRegister::UserLogin(const QString userName)
{
    if (false == m_userMap.contains(userName)) {
        m_userMap.insert(userName, true);
    }
    if (false == m_blockLidAction) {
        blockLidAction();
    }
    UserActive(userName);
}

void SysdbusRegister::UserLogout(const QString userName)
{
    if (true == m_userMap.contains(userName)) {
        m_userMap.remove(userName);
        if (0 == m_userMap.size()) {
            if (true == m_blockLidAction) {
                releaseLidAction();
            }
        }
    }
}

void SysdbusRegister::UserActive(const QString userName)
{
    QList<QString> keys = m_userMap.keys();

    for (int i = 0; i < keys.size(); ++i) {
        QString key = keys.at(i);
        if (key == userName) {
            m_userMap.insert(key, true);
        } else {
            m_userMap.insert(key, false);
        }
    }
    emit ActiveUserChanged(userName);
}

bool SysdbusRegister::CanSetBrightness()
{
    return m_brightnessNode->canSetBrightness();
}

void SysdbusRegister::SetBrightnessPercent(const int percent)
{
    m_brightnessNode->setBrightnessPercent(percent);
}

int SysdbusRegister::GetBrightnessPercent()
{
    return m_brightnessNode->getBrightnessPercent();
}

int SysdbusRegister::GetMaxBrightness()
{
    return m_brightnessNode->getMaxBrightness();
}

void SysdbusRegister::SetReduceBacklightSwitch(const bool value)
{
    m_brightnessNode->setReduceMaxBrightness(value);
}

void SysdbusRegister::SetPcPolicy(const int policy)
{
    SetCpuPolicy(policy);
    SetGpuPolicy(policy);
    SetAudioPolicy(policy);
    SetAspmPolicy(policy);

    if (Performance == policy) {
        m_devPowerControl->setAllDevicePerformance();
    } else if (EnergySaving == policy) {
        m_devPowerControl->setAllDeviceSave();
    } else {
        m_devPowerControl->setAllDeviceBalance();
    }
}
void SysdbusRegister::SetCpuPolicy(const int policy)
{
    if (policy < Performance || policy > EnergySaving) {
        return ;
    }

    for (int var = 0; var < m_cpuCoreNum; ++var) {
        executeLinuxCmd(
            QString("echo %1 | tee /sys/devices/system/cpu/cpu%2/cpufreq/scaling_governor")
                    .arg(m_cpuFreqMode[policy]).arg(var));
    }
}

void SysdbusRegister::SetGpuPolicy(const int policy)
{
    if (policy < Performance || policy > EnergySaving) {
        return ;
    }

    QFile radeonFile("/sys/class/drm/card0/device/power_dpm_state");
    QFile amdgpuFile("/sys/class/drm/card0/device/power_dpm_force_performance_level");
    QStringList radeonPolicy = {"performance", "balanced", "battery"};
    QStringList amdGpuPolicy = {"high", "auto", "low"};
    QString cmd;
    if (radeonFile.exists()) {
        cmd = QString("echo %1 | tee /sys/class/drm/card0/device/power_dpm_state").arg(radeonPolicy[policy]);
        executeLinuxCmd(cmd);
    }
    if (amdgpuFile.exists()) {
        cmd = QString("echo %1 | tee /sys/class/drm/card0/device/power_dpm_force_performance_level").arg(amdGpuPolicy[policy]);
        executeLinuxCmd(cmd);
    }
}

void SysdbusRegister::SetAudioPolicy(const int policy)
{
    if (Performance == policy) {
        executeLinuxCmd(QString("echo 0 | tee /sys/module/snd_hda_intel/parameters/power_save"));
    } else {
        executeLinuxCmd(QString("echo 1 | tee /sys/module/snd_hda_intel/parameters/power_save"));
    }
}

void SysdbusRegister::SetAspmPolicy(const int policy)
{
    if (Performance == policy) {
        executeLinuxCmd(QString("echo default | tee /sys/module/pcie_aspm/parameters/policy"));
    } else {
        executeLinuxCmd(QString("echo powersave | tee /sys/module/pcie_aspm/parameters/policy"));
    }
}

void SysdbusRegister::LockScreen()
{
    //锁屏目前由后台处理
}

void SysdbusRegister::Hibernate()
{
    if ("yes" == canControl("CanHibernate")) {
        controlLogin1Connection("Hibernate");
    }
}

void SysdbusRegister::Suspend()
{
    if ("yes" == canControl("CanSuspend")) {
        controlLogin1Connection("Suspend");
    }
}

void SysdbusRegister::PowerOff()
{
    if ("yes" == canControl("CanPowerOff")) {
        controlLogin1Connection("PowerOff");
    }
}

void SysdbusRegister::SuspendThenHibernate()
{
    if ("yes" == canControl("CanSuspendThenHibernate")) {
        m_susThenHibSet->beginGroup("Sleep");
        m_susThenHibSet->setValue("HibernateDelaySec", 7200);
        m_susThenHibSet->endGroup();
        m_susThenHibSet->sync();
        controlLogin1Connection("SuspendThenHibernate");
    }
}

void SysdbusRegister::SetSystemConfig(const QString userName, const QString key, const QString value)
{
    m_powerConfig->updatePowerConfig(key, value);
    if (m_userMap.size() > 1) {
        emit SystemConfigChanged(userName, key, value);
    }
}

QString SysdbusRegister::GetSystemConfig(const QString key)
{
    return m_powerConfig->getPowerConfig(key);
}

