/*
 * 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 <https://www.gnu.org/licenses/>.
 */
#include "processmanager.h"
#include <QThread>
#include <QDebug>
#include "appmanagercgroupd.h"
#include "pressurewatcher.h"
#include "systemresourcewatcher.h"
#include "processreclaimer.h"
#include "confmanager.h"
#include "common.h"

ProcessManager::ProcessManager(QObject *parent)
    : QObject{parent},
      m_appMgrCgroupd(new AppManagerCgroupd()),
      m_processReclaimer(new ProcessReclaimer(this))
{
    startWatchers();
}

void ProcessManager::startWatchers()
{
    QThread *pressureWatcherThread = new QThread(this);
    PressureWatcher *pressureWatcher = new PressureWatcher;
    startWatcherThread(pressureWatcher, pressureWatcherThread);
    if (common::Singleton<ConfManager>::GetInstance().features().contains("memory")) {
        QThread *memResourceWatcheThread = new QThread(this);
        SystemResourceWatcher *resourceWatcher = new SystemResourceWatcher;
        startWatcherThread(resourceWatcher, memResourceWatcheThread);
    }
}

void ProcessManager::thawAllProcesses(bool walk)
{
    m_appMgrCgroupd->thawAllProcesses(walk);
}

QMap<QString, QVariant> ProcessManager::InitCGroup(const QString &rootPath)
{
    return m_appMgrCgroupd->InitCGroup(rootPath);
}

QMap<QString, QVariant> ProcessManager::CreateProcessCGroup(const QString &appId, int pid )
{
    return m_appMgrCgroupd->CreateProcessCGroup(appId, pid);
}

QMap<QString, QVariant> ProcessManager::CreateProcessCGroup(const QString &appId, const QList<int> &pids)
{
    return m_appMgrCgroupd->CreateProcessCGroup(appId, pids);
}

QMap<QString, QVariant> ProcessManager::DeleteProcessCGroup(const QString &cgroupName)
{
    return m_appMgrCgroupd->DeleteProcessCGroup(cgroupName);
}

QMap<QString, QVariant> ProcessManager::SetProcessCGroupResourceLimit(const QString &cgroupName,
                                                                      const QString &attrName,
                                                                      int value)
{
    return m_appMgrCgroupd->SetProcessCGroupResourceLimit(cgroupName, attrName, value);
}

QMap<QString,QVariant> ProcessManager::CGroupNameWithPid(int pid)
{
    return m_appMgrCgroupd->CGroupNameWithPid(pid);
}

QMap<QString,QVariant> ProcessManager::ReclaimProcesses(const QList<int> &pids)
{
    return m_processReclaimer->reclaimProcesses(pids);
}

QMap<QString, QVariant> ProcessManager::ReclaimProcesses(const QString &cgroup)
{
    auto pids = m_appMgrCgroupd->pids(cgroup);
    return m_processReclaimer->reclaimProcesses(pids);
}

QMap<QString, QVariant> ProcessManager::PidsOfCGroup(const QString &cgroupName)
{
    return m_appMgrCgroupd->PidsOfCGroup(cgroupName);
}

QStringList ProcessManager::Features() const
{
    ConfManager &confManager = common::Singleton<ConfManager>::GetInstance();
    QStringList confFeatures = confManager.features();
    if (confFeatures.contains("reclaim") && !ProcessReclaimer::isAvailable()) {
        confFeatures.removeOne("reclaim");
    }
    return confFeatures;
}

void ProcessManager::startWatcherThread(ResourceWatcher *watcher, QThread *thread)
{
    if (!watcher || !thread) {
        return;
    }

    watcher->moveToThread(thread);

    connect(thread, &QThread::started, watcher, &ResourceWatcher::start);
    connect(thread, &QThread::finished, thread, &QThread::deleteLater);
    connect(thread, &QThread::finished, watcher, &ResourceWatcher::deleteLater);
    connect(watcher, &ResourceWatcher::finished, thread, &QThread::quit);

    connect(watcher, &ResourceWatcher::ResourceThresholdWarning, this, &ProcessManager::ResourceThresholdWarning);

    thread->start();
}
