/*
 * 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 "upmcpucontrol.h"

#include <QDebug>
#include <QThread>

UpmCpuControl::UpmCpuControl(QObject *parent) : QObject(parent)
{
    m_cpuCoreNum = QThread::idealThreadCount();
    qDebug() << "m_cpuCoreNum :" << m_cpuCoreNum;

    m_maxCpuOnlineCore = m_cpuCoreNum - 1;
    m_cpuOnlineCore = m_maxCpuOnlineCore;

    for (int cpuCoreNum = 0; cpuCoreNum < m_cpuCoreNum; ++cpuCoreNum) {
        m_allCpuCoreInfo.push_back(new UpmCpuCoreInfo(cpuCoreNum, this));
    }

    initBalanceModeLimitCpuFreq();

    initAutoSwitchCpuCore();
}

void UpmCpuControl::initBalanceModeLimitCpuFreq()
{
    QString config;
    m_balanceModeSetMaxFreq =
            UpmCustomHardwareConfig::self()->getExtendConfig(
                UPM_KEY_EXTEND_BALANCE_MODE_MAX_FREQUENCY, config);
    if (true == m_balanceModeSetMaxFreq) {
        m_balanceMaxCpuFrequency = config.toInt();
    }
}

void UpmCpuControl::initAutoSwitchCpuCore()
{
    m_cpuOccupy = new UpmCpuOccupy(this);
    connect(m_cpuOccupy, &UpmCpuOccupy::cpuCoreSwitchSignal,
            this, &UpmCpuControl::dealCpuSwitchSignal);

    for (int policy = system_policy_performance; policy < system_policy_max; ++policy) {
        initAutoSwitchCpuCoreConfig(policy);
    }
}

void UpmCpuControl::initAutoSwitchCpuCoreConfig(int cpuPolicy)
{
    if (cpuPolicy < system_policy_performance
     || cpuPolicy > system_policy_powersave) {
        return ;
    }

    QStringList configKeys = {UPM_KEY_EXTEND_PERFORMANCE_AUTO_SWITCH_CPU_CORE,
                              UPM_KEY_EXTEND_BALANCE_AUTO_SWITCH_CPU_CORE,
                              UPM_KEY_EXTEND_POWERSAVE_AUTO_SWITCH_CPU_CORE};
    QString config;
    m_configs[cpuPolicy].configActive =
            UpmCustomHardwareConfig::self()->getExtendConfig(configKeys[cpuPolicy], config);
    if (true == m_configs[cpuPolicy].configActive) {
        QStringList configs = config.split("-");
        if (max_auto_switch_cpu_core_config == configs.size()) {
            m_configs[cpuPolicy].cpuSamplingInterval = configs[cpu_sampling_interval].toInt();
            m_configs[cpuPolicy].offlineCpuOccupy = configs[offline_cpu_occupy].toInt();
            m_configs[cpuPolicy].onlineCpuOccupy = configs[online_cpu_occupy].toInt();
            m_configs[cpuPolicy].reserveCoreNum = configs[reserve_core_num].toInt();
            if (0 == m_configs[cpuPolicy].reserveCoreNum) {
                m_configs[cpuPolicy].reserveCoreNum = 1;
            }
        } else {
            m_configs[cpuPolicy].configActive = false;
        }
    }
}

int UpmCpuControl::getCpuCoreNum()
{
    return m_cpuCoreNum;
}

void UpmCpuControl::setCorePolicy(int cpuCoreId, int cpuPolicy)
{
    if (cpuCoreId >= m_cpuCoreNum) {
        return ;
    }
    m_allCpuCoreInfo[cpuCoreId]->setCorePolicy(cpuPolicy);
}

void UpmCpuControl::setCoreMaxFreq(int cpuCoreId, int cpuFrequency)
{
    if (cpuCoreId >= m_cpuCoreNum) {
        return ;
    }
    m_allCpuCoreInfo[cpuCoreId]->setCoreMaxFreq(cpuFrequency);
}

int UpmCpuControl::getDefaultCoreMaxFreq(int cpuCoreId)
{
    return m_allCpuCoreInfo[cpuCoreId]->getDefaultCoreMaxFreq();
}

void UpmCpuControl::setCoreDefaultMaxFreq(int cpuCoreId)
{
    if (cpuCoreId >= m_cpuCoreNum) {
        return ;
    }
    m_allCpuCoreInfo[cpuCoreId]->setDefaultCoreMaxFreq();
}

void UpmCpuControl::setAllCorePolicy(int cpuPolicy)
{
    if (cpuPolicy < system_policy_performance
     || cpuPolicy > system_policy_powersave) {
        return ;
    }

    m_cpuPolicy = cpuPolicy;

    m_cpuOccupy->stopCalcCpuOccupy();

    for (int cpuCoreNum = 0; cpuCoreNum < m_cpuCoreNum; ++cpuCoreNum) {
        setCpuCoreOnlineState(cpuCoreNum, cpu_core_online);
        if (true == m_balanceModeSetMaxFreq) {
            if (system_policy_balance == m_cpuPolicy) {
                setCoreMaxFreq(cpuCoreNum, m_balanceMaxCpuFrequency);
            } else {
                setCoreDefaultMaxFreq(cpuCoreNum);
            }
        }
        m_allCpuCoreInfo[cpuCoreNum]->setCorePolicy(m_cpuPolicy);
    }
    m_cpuOnlineCore = m_maxCpuOnlineCore;

    if (true == m_configs[m_cpuPolicy].configActive) {
        m_cpuOccupy->startCalcCpuOccupy(m_configs[m_cpuPolicy].cpuSamplingInterval,
                                        m_configs[m_cpuPolicy].offlineCpuOccupy,
                                        m_configs[m_cpuPolicy].onlineCpuOccupy);
    }
}

void UpmCpuControl::setAllCoreMaxFreq(int cpuFrequency)
{
    for (int cpuCoreNum = 0; cpuCoreNum < m_cpuCoreNum; ++cpuCoreNum) {
        m_allCpuCoreInfo[cpuCoreNum]->setCoreMaxFreq(cpuFrequency);
    }
}

void UpmCpuControl::setAllCoreDefaultMaxFreq()
{
    for (int cpuCoreNum = 0; cpuCoreNum < m_cpuCoreNum; ++cpuCoreNum) {
        m_allCpuCoreInfo[cpuCoreNum]->setDefaultCoreMaxFreq();
    }
}

int UpmCpuControl::getCpuCoreOnlineState(int cpuCoreId)
{
    if (cpuCoreId >= m_cpuCoreNum) {
        return cpu_core_error;
    }
    return m_allCpuCoreInfo[cpuCoreId]->getCpuCoreOnlineState();
}

void UpmCpuControl::setCpuCoreOnlineState(int cpuCoreId, int online)
{
    if (cpuCoreId >= m_cpuCoreNum) {
        return ;
    }
    if (0 == cpuCoreId) {
        return ;
    }

    m_allCpuCoreInfo[cpuCoreId]->setCpuCoreOnlineState(online);
}

void UpmCpuControl::configCpuOccupyParameter(
        int cpuPolicy,
        int cpuSamplingInterval, int offlineCpuOccupy,
        int onlineCpuOccupy, int reserveCoreNum)
{
    m_configs[cpuPolicy].cpuSamplingInterval = cpuSamplingInterval;
    m_configs[cpuPolicy].offlineCpuOccupy = offlineCpuOccupy;
    m_configs[cpuPolicy].onlineCpuOccupy = onlineCpuOccupy;
    m_configs[cpuPolicy].reserveCoreNum = reserveCoreNum;
    m_configs[cpuPolicy].configActive = true;
}

void UpmCpuControl::dealCpuSwitchSignal(bool coreOnline)
{
    if (true == coreOnline) {
        if (m_cpuOnlineCore < m_maxCpuOnlineCore) {
            m_cpuOnlineCore++;
            setCpuCoreOnlineState(m_cpuOnlineCore, cpu_core_online);
        }
    } else {
        if (m_cpuOnlineCore >= m_configs[m_cpuPolicy].reserveCoreNum) {
            setCpuCoreOnlineState(m_cpuOnlineCore, cpu_core_offline);
            m_cpuOnlineCore--;
        }
    }
}



