/*
 * Copyright 2021 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 "gsettingwatcher.h"

GsettingWatcher::GsettingWatcher() {

    QString cpuType = getCpuInfo();
    m_isIntel = cpuType.contains("intel",Qt::CaseInsensitive);
    if (m_isIntel) {
        qDebug() << "use tlp:" << cpuType;
    } else {
        qDebug() << "don;t use tlp:" << cpuType;
    }

    m_ptlpTimer = new QTimer(this);
    m_ptlpTimer->setSingleShot(true);

    connect(m_ptlpTimer, &QTimer::timeout, this, &GsettingWatcher::setTlp);
}

GsettingWatcher::~GsettingWatcher() {}

void GsettingWatcher::initGsettingWatcher()
{
    mAcWatcher.initAcWatcher();

    initSettingsConnect();

    initLowPowerAutoSave();
}

void GsettingWatcher::initSettingsConnect()
{
    mPowerPolicyAc = m_powerManagerGsettings->get(POWER_POLICY_AC).toInt();
    mPowerPolicyBattery = m_powerManagerGsettings->get(POWER_POLICY_BATTERY).toInt();
    if (false == mAcWatcher.mPowerState) {
        policyAC();
    } else {
        policyBattery();
    }

    connect(m_powerManagerGsettings, &QGSettings::changed, this, [=](const QString &key) {
        if (key == POWER_POLICY_AC) {
            mPowerPolicyAc = m_powerManagerGsettings->get(POWER_POLICY_AC).toInt();
            if (false == mAcWatcher.mPowerState) {
                policyAC();
            }
        }
        if (key == POWER_POLICY_BATTERY) {
            mPowerPolicyBattery = m_powerManagerGsettings->get(POWER_POLICY_BATTERY).toInt();
            if (true == mAcWatcher.mPowerState) {
                policyBattery();
                if (EnergySaving != mPowerPolicyBattery && Unknow != m_powerPolicyBatteryBackup) {
                    m_powerPolicyBatteryBackup = Unknow;
                    m_powerManagerGsettings->set(POWER_POLICY_BATTERY_BACKUP, Unknow);
                }
            }
        }
    });

    connect(&mAcWatcher, SIGNAL(acChanged(bool)), this, SLOT(AcWatcherChanged(bool)));
}

void GsettingWatcher::AcWatcherChanged(const bool &onBattery)
{
    if (false == onBattery) {
        policyAC();
        setLowPowerState(false);
    } else {
        if (m_percentageLow >= m_batteryPercentage) {
            setLowPowerState(true);
        } else {
            policyBattery();
        }
    }
}

void GsettingWatcher::setLowPowerState(bool lowPowerState)
{
    if (mLowPrecentState == lowPowerState) {
        return;
    }
    if (true == lowPowerState) {
        if (1 == mLowBatteryAutoSave) {
            if (Unknow == m_powerPolicyBatteryBackup) {
                m_powerPolicyBatteryBackup = mPowerPolicyBattery;
                m_powerManagerGsettings->set(POWER_POLICY_BATTERY, EnergySaving);
                m_powerManagerGsettings->set(POWER_POLICY_BATTERY_BACKUP, m_powerPolicyBatteryBackup);
                qInfo() << "battery policy backup:" << m_powerPolicyBatteryBackup;
            }
        }
    } else {
        resetPowerPolicyBatteryBackup();
        qDebug() << "exit low battery mode";
    }

    mLowPrecentState = lowPowerState;
}

void GsettingWatcher::initLowPowerAutoSave()
{
    mLowBatteryAutoSave = m_powerManagerGsettings->get(LOW_BATTERY_ATUO_SAVE).toInt();
    m_powerPolicyBatteryBackup = m_powerManagerGsettings->get(POWER_POLICY_BATTERY_BACKUP).toInt();
    m_percentageLow = m_powerManagerGsettings->get(PERCENTAGE_LOW).toInt();

    QDBusInterface dBusInterface(UPOWER_SERVICE,
                                 UPOWER_DISPLAY_PATH,
                                 FREEDESKTOP_UPOWER,
                                 QDBusConnection::systemBus());
    QDBusReply<QVariant> reply = dBusInterface.call("Get", "org.freedesktop.UPower.Device", "Percentage");
    if (reply.isValid()) {
        m_batteryPercentage = reply.value().toDouble();
    } else {
        qDebug() << "get power percentage failed!";
        m_batteryPercentage = 0;
    }

    if (true == mAcWatcher.mPowerState) {
        if (m_percentageLow >= m_batteryPercentage) {
            setLowPowerState(true);
        } else {
            resetPowerPolicyBatteryBackup();
        }
    } else {
        resetPowerPolicyBatteryBackup();
    }

    connect(m_powerManagerGsettings, &QGSettings::changed, this, [=](const QString &key) {
        if (key == LOW_BATTERY_ATUO_SAVE) {
            mLowBatteryAutoSave = m_powerManagerGsettings->get(LOW_BATTERY_ATUO_SAVE).toInt();
            if (1 == mLowBatteryAutoSave) {
                if (true == mLowPrecentState) {
                    m_powerPolicyBatteryBackup = mPowerPolicyBattery;
                    m_powerManagerGsettings->set(POWER_POLICY_BATTERY, EnergySaving);
                }
            } else {
                if (true == mLowPrecentState) {
                    m_powerPolicyBatteryBackup = Unknow;
                }
            }
            m_powerManagerGsettings->set(POWER_POLICY_BATTERY_BACKUP, m_powerPolicyBatteryBackup);
        }
        if (key == PERCENTAGE_LOW) {
            m_percentageLow = m_powerManagerGsettings->get(PERCENTAGE_LOW).toInt();
            if (true == mAcWatcher.mPowerState) {
                if (m_percentageLow >= m_batteryPercentage) {
                    setLowPowerState(true);
                } else {
                    setLowPowerState(false);
                }
            }
        }
    });

    QDBusConnection::systemBus().connect(
                UPOWER_SERVICE, UPOWER_DISPLAY_PATH, FREEDESKTOP_UPOWER,
                "PropertiesChanged", this, SLOT(dealBatteryPropertiesMessage(QDBusMessage)));
}

void GsettingWatcher::resetPowerPolicyBatteryBackup()
{
    if (Unknow != m_powerPolicyBatteryBackup) {
        m_powerManagerGsettings->set(POWER_POLICY_BATTERY, m_powerPolicyBatteryBackup);
        m_powerPolicyBatteryBackup = Unknow;
        m_powerManagerGsettings->set(POWER_POLICY_BATTERY_BACKUP, m_powerPolicyBatteryBackup);
    }
}

void GsettingWatcher::dealBatteryPropertiesMessage(QDBusMessage msg)
{
    const QDBusArgument &dbusArgs = msg.arguments().at(1).value<QDBusArgument>();
    QMap<QString, QVariant> map;
    dbusArgs >> map;

    if (map.contains("Percentage")) {
        int batteryPercentage = map.value(QString("Percentage")).toDouble();
        if (m_batteryPercentage != batteryPercentage) {
            m_batteryPercentage = batteryPercentage;
            if (true == mAcWatcher.mPowerState) {
                if (m_percentageLow >= m_batteryPercentage) {
                    setLowPowerState(true);
                }
            }
        }
    }
}

void GsettingWatcher::setTlp()
{
    if (!m_isIntel) {
        return;
    }

    if (m_isTlp) {
        controlPowerManagement(ENTER_TLP);
    } else {
        controlPowerManagement(EXIT_TLP);
    }
}

QString GsettingWatcher::getCpuInfo()
{
    QFile file("/proc/cpuinfo");

    if (file.open(QIODevice::ReadOnly)) {
        QString buffer = file.readAll();
        QStringList modelLine = buffer.split('\n').filter(QRegularExpression("^model name"));
        QStringList modelLineWayland = buffer.split('\n').filter(QRegularExpression("^Hardware"));
        if (modelLine.isEmpty()) {
            if (modelLineWayland.isEmpty()) {
                return "Unknown";
            }
            modelLine = modelLineWayland;
        }

        QString result;
        result.append(modelLine.first().split(':').at(1));
        result = result.trimmed();
        return result;
    }

    return QString();
}

void GsettingWatcher::delayStartTlp(bool state)
{
    if (m_isTlp != state) {
        m_isTlp = state;
        m_ptlpTimer->start(TLP_TIMER);
    }
}

void GsettingWatcher::policyAC()
{
    delayStartTlp(false);

    controlPowerManagement(CPU_FREQENCY_MODULATION, mPowerPolicyAc);
    controlPowerManagement(GPU_FREQENCY_MODULATION, mPowerPolicyAc);
    controlPowerManagement(SET_AUDIO_MODE, mPowerPolicyAc);
    controlPowerManagement(SET_PCIE_ASPM_MODE, mPowerPolicyAc);
    m_powerManagerGsettings->set(POWER_POLICY_CURRENT, mPowerPolicyAc);
}

void GsettingWatcher::policyBattery()
{
    controlPowerManagement(CPU_FREQENCY_MODULATION, mPowerPolicyBattery);
    controlPowerManagement(GPU_FREQENCY_MODULATION, mPowerPolicyBattery);
    controlPowerManagement(SET_AUDIO_MODE, mPowerPolicyBattery);
    controlPowerManagement(SET_PCIE_ASPM_MODE, mPowerPolicyBattery);
    m_powerManagerGsettings->set(POWER_POLICY_CURRENT, mPowerPolicyBattery);

    if (mPowerPolicyBattery == EnergySaving) {
        delayStartTlp(true);
    } else {
        delayStartTlp(false);
    }
}
