#include "processmanager.h"

#include "eventbus.h"

namespace {
    const char *systemd_cgroup_path = "/sys/fs/cgroup/systemd";
}

ProcessManager::ProcessManager()
    : m_dataContext(std::make_shared<DataContext>())
    , m_eventWatcher(new EventWatcher(m_dataContext))
    , m_exceptionGroupWatcher(new ExceptionGroupProcessWatcher(m_dataContext->processInfoManager()))
    , m_appWhitelist(new AppWhitelist(*m_dataContext->configManager()))
    , m_appWhitelistService(new AppWhitelistService(m_appWhitelist.get()))
    , m_appLaunchManager(m_dataContext->appLaunchManager())
    , m_appLaunchManagerService(new AppLaunchManagerService(m_appLaunchManager.get()))
    , m_sceneManager(new SceneManager(m_dataContext))
{
    initWatcher();

    registerWhitelistDbusService();
    registerAppLauncherDbusService();

    connectAppLauncherSignals();
}

ProcessManager::~ProcessManager()
{

}

void ProcessManager::ThawProcess(int pid)
{
    if (!canFrozenProcess()) {
        return;
    }

    bool ret = m_dataContext->processInfoManager()->forceChangeAppStateByPid(pid, sched_policy::AppState::Focus);
    if (!ret) {
        sendErrorReply(QDBusError::InvalidArgs,
                       QString("Can not find the application by the pid %1.").arg(pid));
    }
}

void ProcessManager::ThawFrozenProcesses()
{
    if (!canFrozenProcess()) {
        return;
    }

    m_dataContext->processInfoManager()->forceChangCachedStateTo(sched_policy::AppState::Background);
}

void ProcessManager::initWatcher()
{
    QObject::connect(EventBus::instance(), &EventBus::exceptionGroupAddPath, [this](){
        auto sessionScopePath = m_dataContext->groupManager()->groupPath(sched_policy::GroupType::SessionScopeGroup);
        
        if (sessionScopePath.empty()) {
            qWarning() << "session scope path is empty";
            return;
        }
        sessionScopePath = std::string(systemd_cgroup_path) + "/" + sessionScopePath;
        m_exceptionGroupWatcher->addPath(sessionScopePath, sched_policy::GroupType::SessionScopeGroup);
    });

    QObject::connect(EventBus::instance(), &EventBus::startWatcher, m_exceptionGroupWatcher.get(), &ExceptionGroupProcessWatcher::startWatcher);
    QObject::connect(EventBus::instance(), &EventBus::startWatcher, m_eventWatcher.get(), &EventWatcher::startWatcher);
    QObject::connect(EventBus::instance(), &EventBus::startFreezerWatcher, m_eventWatcher.get(), &EventWatcher::startFreezerWatcher);
    QObject::connect(EventBus::instance(), &EventBus::startWindowWatcher, m_eventWatcher.get(), &EventWatcher::startWindowWatcher);
    QObject::connect(EventBus::instance(), &EventBus::startScreenWatcher, m_eventWatcher.get(), &EventWatcher::startScreenWatcher);
    QObject::connect(EventBus::instance(), &EventBus::startBatteryEstimate, m_eventWatcher.get(), &EventWatcher::startBatteryEstimate);

    QObject::connect(EventBus::instance(), &EventBus::stopWatcher, m_exceptionGroupWatcher.get(), &ExceptionGroupProcessWatcher::stopWatcher);
    QObject::connect(EventBus::instance(), &EventBus::stopWatcher, m_eventWatcher.get(), &EventWatcher::stopWatcher);
    QObject::connect(EventBus::instance(), &EventBus::stopFreezerWatcher, m_eventWatcher.get(), &EventWatcher::stopFreezerWatcher);
    QObject::connect(EventBus::instance(), &EventBus::stopWindowWatcher, m_eventWatcher.get(), &EventWatcher::stopWindowWatcher);
    QObject::connect(EventBus::instance(), &EventBus::stopScreenWatcher, m_eventWatcher.get(), &EventWatcher::stopScreenWatcher);
    QObject::connect(EventBus::instance(), &EventBus::stopBatteryEstimate, m_eventWatcher.get(), &EventWatcher::stopBatteryEstimate);
}

void ProcessManager::registerWhitelistDbusService()
{
    QDBusConnection connection = QDBusConnection::sessionBus();
    if (!connection.registerService("com.kylin.ProcessManager") ||
        !connection.registerObject("/com/kylin/ProcessManager/AppWhitelist", m_appWhitelist.get())) {
        qWarning() << "Register whitelist dbus service failed:" << connection.lastError();
    }
}

void ProcessManager::registerAppLauncherDbusService()
{
    QDBusConnection connection = QDBusConnection::sessionBus();
    if (!connection.registerService("com.kylin.ProcessManager") || !connection.registerObject("/com/kylin/ProcessManager/AppLauncher", m_appLaunchManager.get())) {
        qWarning() << "Register app launcher dbus service failed:" << connection.lastError();
    }
}

void ProcessManager::connectAppLauncherSignals()
{
    QObject::connect(
        m_appLaunchManager.get(), &AppLaunchManager::AppLaunched,
        m_appLaunchManagerService.get(), &AppLaunchManagerService::AppLaunched);
}

bool ProcessManager::canFrozenProcess()
{
    if (!m_dataContext->configManager()->reousrceLimitEnabled()) {
        return false;
    }

    if (m_dataContext->deviceModeManager()->currentDeviceMode() == sched_policy::DeviceMode::PC && !m_dataContext->configManager()->softFreezeModeEnabled()) {
        return false;
    }

    return true;
}
