#include "processmanager.h"

#include "eventbus.h"
#include "loghelper.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_sceneManager(new SceneManager(m_dataContext))
    , app_whitelist(new AppWhitelist(*m_dataContext->configManager()))
    , app_whitelist_service(new AppWhitelistService(app_whitelist.get()))
    , app_launch_manager(m_dataContext->appLaunchManager())
    , app_launch_manager_service(new AppLaunchManagerService(app_launch_manager.get()))
{
    initWatcher();

    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()) {
            PRINT_NOTICE("session scope path is empty\n");
            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::connectAppLauncherSignals()
{
    QObject::connect(
        app_launch_manager.get(), &AppLaunchManager::AppLaunched,
        app_launch_manager_service.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;
}
