/*
 * Copyright 2022 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 2 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 thi1s program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "applauncher.h"

//extern "C" {
#include <unistd.h>
//}

AppLauncher::AppLauncher(QObject *parent)
    : QObject(parent)
    , m_isTabletMode(false)
#ifdef INVISIBLE_ENABLE
    , m_timer(new QTimer(this))
    , currentCounter(0)
#endif
    , m_powerSavingEnabled(false)
    , m_cgroupInterface(new com::kylin::CGroup::AppManager("com.kylin.CGroup", "/com/kylin/CGroup/AppManager",
                                                            QDBusConnection::systemBus(), this))
    , m_kwinInterface(new org::ukui::KWin("org.ukui.KWin", "/KWin",
                                          QDBusConnection::sessionBus(), this))
    , m_statusManagerInterface(new com::kylin::statusmanager::interface("com.kylin.statusmanager.interface", "/",
                                                                        QDBusConnection::sessionBus(), this))
    , m_appLauncherDaemon(new AppLauncherDaemon(this))
    , m_whiteListManager(new WhiteListManager(this))
{     
    if (!m_statusManagerInterface->isValid()) {
        m_isTabletMode = false;
        qWarning() << "com.kylin.statusmanager.interface is not valid.";
    } else {
        m_isTabletMode = m_statusManagerInterface->get_current_tabletmode();
    }

#ifdef QT_DEBUG
//    m_isTabletMode = true;
#endif
    qDebug() << "current tablet mode is: " << m_isTabletMode;
    m_isMulTaskView = false;
    initConnections();
}

bool AppLauncher::LaunchApp(const QString &desktopFile)
{
    return doLaunchApp(desktopFile);
}

bool AppLauncher::ActiveProcessByWid(const WId &wid)
{
    return doActiveProcessByWid(wid);
}

bool AppLauncher::LaunchAppWithArguments(const QString &desktopFile, const QStringList &args)
{
    return doLaunchApp(desktopFile, args);
}

bool AppLauncher::LaunchXdgFile(const QString &desktopFile , const QString &fileName)
{
    return doLaunchApp(desktopFile, QStringList() << fileName);
}

bool AppLauncher::LaunchDefaultAppWithUrl(const QString &url)
{
    qDebug ()<<"---AppLauncher::LaunchDefaultAppWithUrl---";
    QUrl url_(url);
    if (url_.isEmpty() || !url_.isValid()) {
        return false;
    }
    QProcess p;
    if (url_.isLocalFile()) {
        QString fileName = url_.toLocalFile();
        qDebug ()<<"---fileName---" << fileName;
        p.start("xdg-mime", QStringList() << "query" << "filetype" << fileName);
        p.waitForFinished(3000);
        if (p.error() != QProcess::UnknownError) {
            qWarning() << __FUNCTION__ << "query filetype for "
                       << fileName << "failed!" << p.errorString();
            return false;
        }
        QString fileType = p.readAll();
        if (fileType.endsWith("\n")) {
            fileType.remove("\n");
        }
        qDebug ()<<"---fileType---" << fileType;
        p.start("xdg-mime", QStringList() << "query" << "default" << fileType);
        p.waitForFinished(3000);
        if (p.error() != QProcess::UnknownError) {
            qWarning() << __FUNCTION__ << "query default for "
                       << fileType << "failed!" << p.errorString();
            return false;
        }

        QString desktopFile = p.readAll();
        qDebug() << "---desktopFile---" << desktopFile;
        if (desktopFile.endsWith("\n")) {
            desktopFile.remove("\n");
        }
        return doLaunchApp(desktopFile, QStringList() << fileName);
    } else {
        QString scheme = url_.scheme();
        QString mimeType = "x-scheme-handler/" + scheme;
        qDebug ()<<"---mimeType---" << mimeType;
        p.start("xdg-mime", QStringList() << "query" << "default" << mimeType);
        p.waitForFinished(3000);
        if (p.error() != QProcess::UnknownError) {
            qWarning() << __FUNCTION__ << "query default for "
                       << mimeType << "failed!" << p.errorString();
            return false;
        }
        QString desktopFile = p.readAll();
        qDebug() << "------desktopFile---" << desktopFile;
        if (desktopFile.endsWith("\n")) {
            desktopFile.remove("\n");
        }
        return doLaunchApp(desktopFile, QStringList() << url);
    }
    return true;
}

QString AppLauncher::AppDesktopFileNameByPid(qint64 pid)
{
    auto it = m_appsInfo.constBegin();
    while (it != m_appsInfo.constEnd()) {
        auto processIt = it->constBegin();
        while (processIt != it->constEnd()) {
            if (processIt->getPid() == pid) {
                return it.key();
            }
            ++ processIt;
        }
        ++ it;
    }
    return QString();
}

QString AppLauncher::AppDesktopFileNameByWid(qint64 wid)
{
    qDebug() << __FUNCTION__ << "--" << wid;
    auto it = m_appsInfo.constBegin();
    while (it != m_appsInfo.constEnd()) {
        auto processIt = it->constBegin();
        while (processIt != it->constEnd()) {
            for (auto &appWid : processIt->getWids()) {
                if (appWid == wid) {
                    return it.key();
                }
            }
            ++ processIt;
        }
        ++ it;
    }
    return QString();
}

#if 0
QList<uint> AppLauncher::RunningApplications()
{
    QList<uint> runningWids;
    auto it = m_appsInfo.constBegin();
    while (it != m_appsInfo.constEnd()) {
        if (!it->isEmpty()) {
            quint32 wid = m_kwinInterface->getWidFromPid(it->last().pid);
            runningWids.push_back(wid);
        }
        ++ it;
    }
    return runningWids;
}
#endif

/// 当 \param enable 设为 true (即开启省电模式)时，
/// 将 \memberof status 为 'InvisibleProcess' 的进程移动至 'CachedProcess' cgroup 分组
/// 关闭省电模式时，将所有的 'CachedProcess' 分组的应用移动至 'InvisibleProcess' 分组
void AppLauncher::setPowerSavingModeEnable(bool enable)
{
    if (m_powerSavingEnabled == enable) {
        return;
    }

    m_powerSavingEnabled = enable;
#if 0
    for (auto appInfo : m_appsInfo) {
        if (enable) {
            if (appInfo.status == InvisibleProcess) {
//                updateAppStatus(appInfo.pid, CachedProcess);
            }
        } else {
            if (appInfo.status == CachedProcess) {
////                updateAppStatus(appInfo.pid, InvisibleProcess);
//                updateAppStatus(appInfo.pid, CachedProcess);
            }
        }
    }
#endif
}

void AppLauncher::killCachedApp()
{
#if 0
    for (const auto &desktopFile : m_appsInfo.keys()) {
        if (m_appsInfo[desktopFile].status == CachedProcess) {
            qint64 pid = m_appsInfo[desktopFile].pid;
            // 先移动至 InvisibleProcess 分组，才能将其 kill 掉,cached组中进程被冻结
//            updateAppStatus(pid, InvisibleProcess);
            bool ret = QProcess::startDetached("kill", QStringList() << "-9" << QString::number(pid));
            if (ret) {
                m_appsInfo.remove(desktopFile);
            }
        }
    }
#endif
}

void AppLauncher::tabletModeChanged(bool tabletMode)
{
    qDebug() << "tablet changed: " << tabletMode;
    if (m_isTabletMode == tabletMode) {
        return;
    }
    m_isTabletMode = tabletMode;

#ifdef INVISIBLE_ENABLE
    if (tabletMode) {
        m_timer->start(1000);
    } else {
        m_timer->stop();
        m_timerContainer.clear();
        currentCounter = 0;
    }
#endif
    updateAppStatus();
}

void AppLauncher::activeWindowChanged(const WindowId &window)
{
    if (!m_isTabletMode) {
        return;
    }

    quint32 wid = window.toUInt();
    quint32 pid = WindowManager::getPid(window);

    qDebug() << "active wid changed and pid: " << wid << "pid: "<< pid;

#if 0
    KWindowInfo winfo(wid, NET::WMPid);
    quint32 pid = winfo.pid();
#endif
//    if (pid == preActivePid) {
//        moveAllProcessesToGroup(ForegroundProcess);
//        return;
//    }
    ProcInfo procInfo(pid);
    if (!procInfo.isTencentApp(pid)) {
        isTencentApp = false;
    } else {
        isTencentApp = true;
    }
    qDebug() << "active pid changed: " << pid << " wid: " << wid;

    if (ProcInfo::isXorg(pid)) {
        moveAllProcessesToGroup(ProcessInfo::ForegroundProcess);
        return;
    }
    updateAppStatus(pid, ProcessInfo::ForegroundProcess);

    // 如果此时恰好因为进程退出导致的活动窗口改变
    // 会导致即将关闭的应用的pid会被移动至缓存分组中去
    // 进程不会被真正的关闭掉，所以等待两秒之后再进行状态更新
    // 对于应用本身已经实现单实例的应用来说，可能会导致打开关闭后无法再次启动的问题
    if(m_isMulTaskView == false){
       QTimer::singleShot(2000, this, QOverload<>::of(&AppLauncher::updateAppStatus));
    }
}

#ifdef INVISIBLE_ENABLE
void AppLauncher::timeOut()
{
    if (m_timerContainer.isEmpty()) {
        return;
    }
    ++ currentCounter;

    handleTimeOut();

    if (currentCounter == UINT64_MAX) {
        currentCounter = 0;
    }
}

void AppLauncher::handleTimeOut()
{
    if (m_timerContainer.isEmpty()) {
        return;
    }
    if (m_timerContainer.first().deadCounter == currentCounter) {
        QString desktopFile = m_timerContainer.first().desktopFile;
        m_timerContainer.removeFirst();
        qDebug() << "move process to CachedProcess group: " << desktopFile;
//        updateAppStatus(desktopFile, CachedProcess);
        handleTimeOut();
    } else {
        return;
    }
}
#endif

void AppLauncher::backendChildFinished(pid_t pid)
{
    auto appInfoIt = m_appsInfo.begin();
    while (appInfoIt != m_appsInfo.end()) {
        auto processIt = appInfoIt->begin();
        while (processIt != appInfoIt->end()) {
            if (processIt->getPid() == pid) {
                appInfoIt->removeOne(*processIt);
                if (appInfoIt->isEmpty()) {
                    m_appsInfo.remove(appInfoIt.key());
                    return;
                }
            } else {
                ++ processIt;
            }
        }
        ++ appInfoIt;
    }
}

void AppLauncher::aboutToQuitApp()
{
    qDebug() << "about to quit app";
    moveCachePidToGroup(ProcessInfo::ForegroundProcess);
    auto appInfoIt = m_appsInfo.constBegin();
    while (appInfoIt != m_appsInfo.constEnd()) {
        auto processIt = appInfoIt->constBegin();
        while (processIt != appInfoIt->constEnd()) {
            if (processIt->getStatus() == ProcessInfo::CachedProcess) {
                moveTasksAndpidToGroup(processIt->getPid(), ProcessInfo::ForegroundProcess);
                m_cgroupInterface->MoveProcess(processIt->getPid(), ProcessInfo::ForegroundProcess);
            }
            Q_EMIT aboutToQuitMainApp();
            QProcess::startDetached("kill", QStringList() << "-9" << QString::number(processIt->getPid()));
            ++ processIt;
        }
        ++ appInfoIt;
    }
    m_cgroupInterface->DeleteCGroup();
}

void AppLauncher::windowAdded(const WindowId &window)
{
    windowChanged(window, true);
}

void AppLauncher::windowRemoved(const WindowId &window)
{
    windowChanged(window, false);
}

void AppLauncher::initConnections()
{
    connect(m_statusManagerInterface, &com::kylin::statusmanager::interface::mode_change_signal,
            this, &AppLauncher::tabletModeChanged);

    connect(m_appLauncherDaemon, &AppLauncherDaemon::childProcessFinished,
            this, &AppLauncher::backendChildFinished);
#ifdef INVISIBLE_ENABLE
    connect(m_timer, &QTimer::timeout, this, &AppLauncher::timeOut);
#endif

//    connect(m_kwinInterface, &org::ukui::KWin::activeWindowChanged,
//            this, &AppLauncher::activeWindowChanged);
    connect(WindowManager::self(), &WindowManager::activeWindowChanged,
            this, &AppLauncher::activeWindowChanged);

    connect(m_appLauncherDaemon, &AppLauncherDaemon::aboutToQuitAppManager,
            this, &AppLauncher::aboutToQuitApp);

//    connect(KWindowSystem::self(), &KWindowSystem::windowAdded,
//            this, &AppLauncher::windowAdded);

//    connect(KWindowSystem::self(), &KWindowSystem::windowRemoved,
//            this, &AppLauncher::windowRemoved);
    connect(WindowManager::self(), &WindowManager::windowAdded,
            this, &AppLauncher::windowAdded);

    connect(WindowManager::self(), &WindowManager::windowRemoved,
            this, &AppLauncher::windowRemoved);

}

void AppLauncher::updateAppStatus(const QString &desktopFile, qint64 pid, ProcessInfo::AppStatus status)
{
    qDebug() << "-3-updateAppStatus---" << desktopFile << " " << pid;
    if (!m_appsInfo.contains(desktopFile)) {
        return;
    }
//    qDebug() << "-3-updateAppStatus" << pid << "-wid: " << wid;
#ifdef INVISIBLE_ENABLE
    m_appsInfo[desktopFile].status = status;
    if (status == InvisibleProcess) {
        addTimer(desktopFile);
    }
    m_cgroupInterface->MoveProcess(m_appsInfo[desktopFile].pid, status);
#else
    auto processInfo = m_appsInfo[desktopFile];
    auto processIt = processInfo.begin();
    while (processIt != processInfo.end()) {
        if (processIt->getPid() == pid) {
            if (status == ProcessInfo::CachedProcess) {
                // whitelist中包含的desktop文件，不移动至CachedProcess分组
                bool isExist = m_whiteListManager->isExists(desktopFile, "AppManager");
                if (isExist == false) {
                    qDebug() << __FUNCTION__ << "-3-MoveProcess " << status << pid;
                    moveTasksAndpidToGroup(processIt->getPid(), status);
                    m_cgroupInterface->MoveProcess(pid, status);
                    processIt->setStatus(status);
                    return;
                }
            } else {
                qDebug() << __FUNCTION__ << "-3-MoveProcess " << status << pid;
                moveTasksAndpidToGroup(processIt->getPid(), status);
                m_cgroupInterface->MoveProcess(pid, status);
                processIt->setStatus(status);
                return;
            }
        }
        ++ processIt;
    }
#endif
}

void AppLauncher::updateAppStatus(const qint64 &pid, ProcessInfo::AppStatus status)
{
    qDebug() << "-2-updateAppStatus" << pid << " " << status;
    auto appInfoIt = m_appsInfo.begin();
    while (appInfoIt != m_appsInfo.end()) {
        auto processIt = appInfoIt->begin();
        while (processIt != appInfoIt->end()) {
            if (processIt->getPid() == pid) {
                if (status == ProcessInfo::CachedProcess) {
                    // whitelist中包含的desktop文件，不移动至CachedProcess分组
                    bool isExist = m_whiteListManager->isExists(appInfoIt.key(), "AppManager");
                    if (isExist == false) {
                        qDebug() << __FUNCTION__ << "-2-MoveProcess " << status << pid;
                        moveTasksAndpidToGroup(processIt->getPid(), status);
                        m_cgroupInterface->MoveProcess(pid, status);
                        processIt->setStatus(status);
                        return;
                    }
                } else {
                    qDebug() << __FUNCTION__ << "-2-MoveProcess " << status << pid;
                    moveTasksAndpidToGroup(processIt->getPid(), status);
                    m_cgroupInterface->MoveProcess(pid, status);
                    processIt->setStatus(status);
                    return;
                }
            }
            ++ processIt;
        }
        ++ appInfoIt;
    }
}

void AppLauncher::updateAppStatus()
{
    if (m_isTabletMode && m_isMulTaskView == false) {
        WindowId window = WindowManager::currentActiveWindow();
        uint wid = window.toUInt();
        qDebug()<<"-1--updateAppStatus---"<<"wid: "<< wid;
        uint pid = WindowManager::getPid(window);
        qDebug()<<"-1--updateAppStatus---"<<"pid: "<< pid;

#if 0
        uint wid = KWindowSystem::activeWindow();
        KWindowInfo winfo(wid, NET::WMPid);
        quint32 pid = winfo.pid();
#endif
        if (m_appsInfo.isEmpty()) {
            qDebug() << "-1-" << "m_appsInfo.isEmpty()" << pid;
        }
        auto appInfoIt = m_appsInfo.begin();
        while (appInfoIt != m_appsInfo.end()) {
            auto processIt = appInfoIt->begin();
            while (processIt != appInfoIt->end()) {
                if (processIt->getPid() == pid) {
                    qDebug() << __FUNCTION__ << "-1-MoveProcess " << ProcessInfo::ForegroundProcess << pid;
                    m_cgroupInterface->MoveProcess(pid, ProcessInfo::ForegroundProcess);
                    QList<quint64> childrePidList = ProcInfo::getChildrenPid(processIt->getPid());
                    for (int i=0; i<childrePidList.count(); ++i) {
                        qint64 childrenPid = childrePidList.at(i);
                        m_cgroupInterface->MoveProcess(childrenPid, ProcessInfo::ForegroundProcess);
                    }
                    QList<quint64> tasksList = ProcInfo::getTasksFromPid(processIt->getPid());
                    for (int i=0; i<tasksList.count(); ++i) {
                        qint64 tasks = tasksList.at(i);
                        m_cgroupInterface->MoveProcess(tasks, ProcessInfo::ForegroundProcess);
                    }
                    processIt->setStatus(ProcessInfo::ForegroundProcess);
                    ++ processIt;
                    continue;
                }

                qDebug() << "processIt->getWids().count()" << processIt->getWids().count();
                if (!processIt->getWids().isEmpty()) {
                    // whitelist中包含的desktop文件，不移动至CachedProcess分组
                    bool isExist = m_whiteListManager->isExists(appInfoIt.key(), "AppManager");
                    if (isExist == false) {
                        qDebug() << __FUNCTION__ << "-1-MoveProcess "<< ProcessInfo::CachedProcess << processIt->getPid();
                        m_cgroupInterface->MoveProcess(processIt->getPid(), ProcessInfo::CachedProcess);
                        moveTasksAndpidToGroup(processIt->getPid(), ProcessInfo::CachedProcess);
                        processIt->setStatus(ProcessInfo::CachedProcess);
                    } else {
                        moveTasksAndpidToGroup(processIt->getPid(), ProcessInfo::ForegroundProcess);
                        m_cgroupInterface->MoveProcess(processIt->getPid(), ProcessInfo::ForegroundProcess);
                        processIt->setStatus(ProcessInfo::ForegroundProcess);
                    }
                }
                ++ processIt;
            }
            ++ appInfoIt;
        }
    } else {
        moveAllProcessesToGroup(ProcessInfo::ForegroundProcess);
        moveCachePidToGroup(ProcessInfo::ForegroundProcess);
    }
}

bool AppLauncher::isRuning(const QString &desktopFile)
{
    // 针对腾讯应用自身单实例处理
    if (isSelfSingleInstance(desktopFile)) {
        return false;
    }

    if(!m_appsInfo.contains(desktopFile)){
        qDebug() << "---m_appsInfo--不包含：" << desktopFile;
        return false;
    }

    auto it = m_appsInfo.value(desktopFile);
    for(int i=0; i<it.size(); i++){
        if(!it.at(i).getWids().isEmpty()){
            qDebug()<<"___应用wid不为空___";
            return true;
        }
    }
    return false;
}

void AppLauncher::moveAllProcessesToGroup(ProcessInfo::AppStatus status)
{
    qDebug() << __FUNCTION__ << " " << status;
    auto appInfoIt = m_appsInfo.begin();
    while (appInfoIt != m_appsInfo.end()) {
        auto processIt = appInfoIt->begin();
        while (processIt != appInfoIt->end()) {
            qDebug() << __FUNCTION__ << "MoveProcess " << status << processIt->getPid();
            QList<quint64> childrePidList = ProcInfo::getChildrenPid(processIt->getPid());
            for (int i=0; i<childrePidList.count(); ++i) {
                qint64 childrenPid = childrePidList.at(i);
                m_cgroupInterface->MoveProcess(childrenPid, status);
            }
            QList<quint64> tasksList = ProcInfo::getTasksFromPid(processIt->getPid());
            for (int i=0; i<tasksList.count(); ++i) {
                qint64 tasks = tasksList.at(i);
                qDebug() << "---tasks---" << tasks;
                m_cgroupInterface->MoveProcess(tasks, status);
            }
            m_cgroupInterface->MoveProcess(processIt->getPid(), status);
            processIt->setStatus(status);
            ++ processIt;
        }
        ++ appInfoIt;
    }
}

void AppLauncher::moveProcessesToGroup(ProcessInfo::AppStatus status)
{
    auto appInfoIt = m_appsInfo.begin();
    while (appInfoIt != m_appsInfo.end()) {
        auto processIt = appInfoIt->begin();
        while (processIt != appInfoIt->end()) {
            qDebug() << __FUNCTION__ << "Move Alone Process " << status << processIt->getPid();
            ProcInfo procInfo(processIt->getPid());
            if (procInfo.isTencentApp(processIt->getPid())) {
                m_cgroupInterface->MoveProcess(processIt->getPid(), status);
                processIt->setStatus(status);
            }
            ++ processIt;
        }
        ++ appInfoIt;
    }
}

QString AppLauncher::getAppStartExec(const QString &desktopFileName)
{
    if (desktopFileName.isEmpty()) {
        qWarning() << __FUNCTION__ << "desktopFileName is NULL！！！";
        return QString();
    } else {
        QString fullDesktopName = desktopFullFilename(desktopFileName);
        QSettings desktop(fullDesktopName, QSettings::IniFormat);
        return desktop.value("Desktop Entry/Exec").toString();
    }
}

QStringList AppLauncher::getAllDesktopFiles()
{
    QStringList desktopList;
    QStringList desktopfilePaths = QStandardPaths::standardLocations(QStandardPaths::ApplicationsLocation);
    for (int i = 0; i < desktopfilePaths.size(); ++i) {
        QDir  dir(desktopfilePaths.at(i));
        QStringList nameFilters;
        nameFilters << "*.desktop";
        QStringList files = dir.entryList(nameFilters, QDir::Files | QDir::Readable, QDir::Name);
        for (int j=0; j<files.size(); j++) {
             desktopList << desktopfilePaths.at(i) + "/" + files.at(j);
             m_desktopList << files.at(j);
        }
    }
    return desktopList;
}

void AppLauncher::openShortcutApp(WId wid, int pid)
{
    qDebug() << __FUNCTION__ << "---openShortcutApp---wid" << wid <<" pid " << pid;
    ProcInfo procInfo(pid);
    qint64 ppid;
    QStringList desktopNameFiles = getAllDesktopFiles();
    QString appExec = procInfo.getCmdlineFromPid(pid);
    qDebug() << "---appExec---" << appExec;
    for (auto & file : desktopNameFiles) {
         QString path = file;
         QSettings desktop(path, QSettings::IniFormat);
         QString startCmd = desktop.value("Desktop Entry/Exec").toString();

         if(startCmd.contains(appExec)){
             QString desktop = file;
             for (int k=0; k<m_desktopList.size(); k++) {
                 if (desktop.contains(m_desktopList.at(k))) {
                     qDebug() << "---快捷键方式启动---" << m_desktopList.at(k);
                     m_desktopName = m_desktopList.at(k);
                     auto it = m_appsInfo.value(m_desktopName);
                     for(int i=0; i<it.size(); i++) {
                         if(!it.at(i).getWids().contains(wid)) {
                             qDebug() << "---快捷键应用窗口  add---";
                             m_appsInfo[m_desktopName].push_back(ProcessInfo(pid, QList<qint64>() << ppid, ProcessInfo::ForegroundProcess, QList<WindowId>() << wid, QStringList()));
//                             m_appsInfo[m_desktopName].push_back({ pid, ppid, QList<WId>() << wid, QStringList()});
                             updateAppStatus(m_desktopName, pid, ProcessInfo::ForegroundProcess);
                             return;
                         }
                     }
                     if (m_appsInfo.value(m_desktopName).isEmpty()) {
                         qDebug() << "---map中的desktop信息为NULL 快捷键应用窗口  add---" << pid;
                         m_appsInfo[m_desktopName].push_back(ProcessInfo(pid, QList<qint64>() << ppid, ProcessInfo::ForegroundProcess, QList<WindowId>() << wid, QStringList()));
                         updateAppStatus(m_desktopName, pid, ProcessInfo::ForegroundProcess);
                         return;
                     }
                 }
             }
         }
     }
}

#ifdef INVISIBLE_ENABLE
/// 关于定时器的过程：
/// 平板模式下，将会开启一个1s的定时器 \memberof m_timer，每次超时都会将 \memberof currentCounter +1，
/// 当进程处于‘不可见进程’状态时，为进程添加一个 \struct Timer，包含了超时时的 \memberof currentCounter
/// 值，并将该 Timer 添加到 \memberof m_timerContainer 中去，每次 m_timer 超时，都会去 m_timerContainer
/// 中找到当前与 currentCounter 相等的数值，并对相应的进程进程处理。
void AppLauncher::addTimer(const QString &desktopFile)
{
    qDebug() << "addTimer: " << desktopFile;
    deleteTimer(desktopFile);
    m_timerContainer.append({ currentCounter + interval, desktopFile });
}

///
void AppLauncher::deleteTimer(const QString &desktopFile)
{
    for (auto timer : m_timerContainer) {
        if (timer.desktopFile == desktopFile) {
            m_timerContainer.removeOne(timer);
            qDebug() << "deleteTimer: " << desktopFile;
            return;
        }
    }
}
#endif

qint64 AppLauncher::filterPidWithBackend()
{
    static pid_t appManagerPid = getpid();
    static QString childrenPidsFile =
            QString("/proc/%1/task/%2/children").arg(appManagerPid).arg(appManagerPid);
    QFile pidsFile(childrenPidsFile);
    if (!pidsFile.open(QIODevice::ReadOnly)) {
        qWarning() << "Get children pids failed: " << childrenPidsFile;
        return -1;
    }

    QStringList pids = QString(pidsFile.readAll()).split(QChar::Space);
    for (const QString &pid : pids) {
        bool found = false;
        for (const auto &appInfo : m_appsInfo) {
            auto it = appInfo.constBegin();
            while (it != appInfo.constEnd()) {
                if (it->getPid() == pid.toInt()) {
                    found = true;
                    break;
                }
                ++ it;
            }
        }
        if (!found) {
            qDebug() << __FUNCTION__ << "not found" << pid;
            int nPid = pid.toInt();
            if (ProcInfo::isWindow(nPid))
                return nPid;
        }
    }
    return -1;
}

QString AppLauncher::desktopFullFilename(const QString &desktopFileName)
{
    QFile desktopFile(desktopFileName);
    if (desktopFile.exists()) {
        return desktopFileName;
    }
    QStringList desktopfilePaths = QStandardPaths::standardLocations(QStandardPaths::ApplicationsLocation);
    for (auto path : desktopfilePaths) {
        if (desktopFileName.contains(path)) {
            return desktopFileName;
        } else if (QFile::exists(path + "/" + desktopFileName)) {
            return path + "/" + desktopFileName;
        }
    }
    return QString();
}

bool AppLauncher::doLaunchApp(const QString &desktopFile, const QStringList &args)
{
    qDebug() << "doLaunchApp" << desktopFile;
    QString fullDesktopName = desktopFullFilename(desktopFile);
    if (fullDesktopName.isEmpty()) {
        qWarning() << "Launch app failed, the dekstop file is " << desktopFile;
        return false;
    }
    if (!KDesktopFile::isDesktopFile(fullDesktopName)) {
        return false;
    }
    KDesktopFile file(fullDesktopName);
    if (!file.hasApplicationType() && !file.tryExec()) {
        return false;
    }

    // not full path file name
    QString dekstopFilename = fullDesktopName.split("/").last();

    if (m_isTabletMode) {
        qDebug() << "---m_isTabletMode---" << m_isTabletMode;
        moveProcessesToGroup(ProcessInfo::ForegroundProcess);
        if (isRuning(dekstopFilename)) {
            qDebug() << "===应用开始运行===";
            bool processHasStarted = false;
            qint64 processId = 0;
            QList<WindowId> wids;
            auto appInfoList = m_appsInfo.value(dekstopFilename);
            if (args.isEmpty()) {
                if (!appInfoList.isEmpty()) {
                    processHasStarted = true;
                    processId = appInfoList.last().getPid();
                    wids = appInfoList.last().getWids();
                }
            } else {
                auto it = appInfoList.constEnd() - 1;
                while (it != appInfoList.constBegin() - 1) {
                    if (it->getArgs() == args) {
                        processHasStarted = true;
                        processId = it->getPid();
                        wids = it->getWids();
                        break;
                    }
                    -- it;
                }
            }
            if (processHasStarted && !wids.isEmpty()) {
                updateAppStatus(dekstopFilename, processId, ProcessInfo::ForegroundProcess);
                bool actived;
                auto it = m_appsInfo.value(dekstopFilename);
                for (int i=0; i<it.size(); i++) {
                     if(it.at(i).getPid() == processId) {
                        WindowManager::activateWindow(it.at(i).getWids().last());
                        actived = true;
                     } else {
                        actived = false;
                     }
                }               
                if (!actived) {
                    qWarning() << "1---Active window failed, pid: " << processId;
                }
                QString childrenPidsFile = QString("/proc/%1/task/%2/children").arg(processId).arg(processId);
                QFile pidsFile(childrenPidsFile);
                if (!pidsFile.exists()) {
                    qWarning() << "file not exist";
                    return true;
#if 0
                    QProcess::startDetached("kill", QStringList() << "-9" << QString::number(processId));
                    m_appsInfo.remove(dekstopFilename);
#ifdef INVISIBLE_ENABLE
                    deleteTimer(dekstopFilename);
#endif
                    return false;
#endif
                }
                if (!pidsFile.open(QIODevice::ReadOnly)) {
                    qWarning() << "Get children pids failed: " << childrenPidsFile;
                    return false;
                }
                QByteArray baData = pidsFile.readAll();
                QString pidstring = QString(baData);
                QStringList pids = pidstring.split(QChar::Space);
                if (pids.endsWith("")) {
                    pids.removeLast();
                }
                if (pids.size() > 0) {
                    for (int i=0; i<pids.size(); ++i) {
                        processId = pids[i].toInt();
                        auto it = m_appsInfo.value(desktopFile);
                        for (int i=0; i<it.size(); i++) {
                             if(it.at(i).getPid() == processId) {
                                WindowManager::activateWindow(it.at(i).getWids().last());
                                actived = true;
                             }
                        }
                        if (!actived) {
                            qWarning() << "--2--Active window failed, pid: " << processId;
                        } else {
                            updateAppStatus(dekstopFilename, processId, ProcessInfo::ForegroundProcess);
                        }
                    }
                }
                return true;
            }
        }
    }
    QString startCmd = getAppStartExec(desktopFile);
    startCmd.remove(QRegExp("%."));
    startCmd.remove(QRegExp("^\""));
    startCmd.remove(QRegExp(" *$"));

    QProcess *process = new QProcess(this);
    process->setStandardInputFile(QProcess::nullDevice());
    isTerminalProgram = false;
    if (args.isEmpty()) {
        if (file.desktopGroup().hasKey("Terminal") &&
            file.desktopGroup().readEntry("Terminal") == "true" &&
            !m_appsInfo.contains(dekstopFilename)) {
            process->start("mate-terminal", QStringList() << "-e" << startCmd);
            qDebug()<< "---mate-terminal-start--" << startCmd;
            isTerminalProgram = true;
        } else {
            process->start(startCmd);
        }
    } else {
        if (startCmd.contains(QChar::Space)) {
            startCmd = startCmd.split(QChar::Space).first();
        }
        process->start(startCmd, args);
    }

    bool started = process->waitForStarted(3000);
    if (started) {
        // the app may be a singleton instance
        bool actived = false;
        if (m_appsInfo.contains(dekstopFilename)) {
            qDebug() << "--m_appsInfo.contains(dekstopFilename--";
            auto processList = m_appsInfo.value(dekstopFilename);
            QList<WindowId> wids;
            wids = processList.last().getWids();
            auto it = processList.constBegin();
            while (it != processList.constEnd()) {
                for (int i = 0; i < processList.size(); ++i) {
                    if (processList.at(i).getPid() == it->getPid()) {
                        WindowManager::activateWindow(it->getWids());
                        actived = true;
                    }
                }
                if (!actived) {
                    qWarning() << "3---Active window failed, pid: " << it->getPid();
                } else {
                    updateAppStatus(dekstopFilename, it->getPid(), ProcessInfo::ForegroundProcess);
                }
                ++ it;
            }
        }
        if (isTerminalProgram) {
            auto pids = ProcInfo::pidOfCmdName(startCmd);
            for (int var = 0; var < pids.size(); ++var) {
                qDebug() << "---isTerminalProgram.pid---" << pids.at(var);
            }
            if (pids.isEmpty()) {
                qWarning() << "Launch terminal program failed " << dekstopFilename;
                return false;
            }
            m_guiAppPid = pids.last();
            if (!m_appsInfo.contains(dekstopFilename)){
                qDebug() << "---terminal应用打开了---" << dekstopFilename << "-" << m_guiAppPid;
                m_appsInfo[dekstopFilename].push_back(ProcessInfo(m_guiAppPid, (QList<qint64>() << m_guiAppPid), ProcessInfo::ForegroundProcess, QList<WindowId>(), (QStringList() << args)));
            }
            updateAppStatus(dekstopFilename, m_guiAppPid, ProcessInfo::ForegroundProcess);
            // 针对终端应用做的特殊处理
//            connect(this, &AppLauncher::terminalPid, [=] (int terminPid, WId terminWid) {
//                qDebug() << "---connect--" << terminPid << "-" << terminWid;
//                m_guiAppPid = m_terminalPid;
//                QString startCmd = getAppStartExec(dekstopFilename);
//                qDebug() << "---startCmd---" <<startCmd;
//                if (!m_appsInfo.contains(dekstopFilename)){
//                    qDebug() << "---terminal应用打开发信号了---" << dekstopFilename << "-" << m_guiAppPid;
//                    m_appsInfo[dekstopFilename].push_back({ m_guiAppPid, ForegroundProcess, QList<WId>() << terminWid, QStringList() << args });
//                }
//                updateAppStatus(dekstopFilename, m_guiAppPid, ForegroundProcess);
//            });
        } else {
            ProcInfo procInfo(process->pid());
            // ToDo 针对kmre应用打开后显示在任务栏的处理
            connect(this, &AppLauncher::kmreAppPid, [=] {
                m_guiAppPid = m_kmrePid;
                QString startCmd = getAppStartExec(dekstopFilename);
                if (!m_appsInfo.contains(dekstopFilename) && startCmd.contains("/usr/bin/startapp")){
                    qDebug() << "---kmre应用打开发信号了---" << dekstopFilename << "-" << m_guiAppPid;
                    m_appsInfo[dekstopFilename].push_back(ProcessInfo(m_guiAppPid, (QList<qint64>() << m_guiAppPid), ProcessInfo::ForegroundProcess, QList<WindowId>(), (QStringList() << args)));
                }
                updateAppStatus(dekstopFilename, m_guiAppPid, ProcessInfo::ForegroundProcess);
            });

            // 针对腾讯应用做的特殊处理
            connect(this, &AppLauncher::tencentAppSignal, [=] (int tencentPid, WId tencentWid) {
                QString startCmd = getAppStartExec(dekstopFilename);
                if (!m_appsInfo.contains(dekstopFilename) && startCmd.contains("TXEduPlatformRuntime")){
                    m_guiAppPid = tencentPid;
                    qDebug() << "---Tencent应用打开发信号了---" << dekstopFilename << "-" << tencentPid;
                    m_appsInfo[dekstopFilename].push_back(ProcessInfo(m_guiAppPid, QList<qint64>(), ProcessInfo::ForegroundProcess, (QList<WindowId>() << tencentWid), (QStringList() << args)));
                    updateAppStatus(dekstopFilename, tencentPid, ProcessInfo::ForegroundProcess);
                }
            });

            if (!startCmd.contains("/usr/bin/startapp") && !startCmd.contains("TXEduPlatformRuntime")) {
                qDebug() << "---应用不是kmre---";
                qint32 binaryExePid = procInfo.guiAppPid();
                QList<WId> wid;
                qDebug() << "---binaryExePid---" << binaryExePid;
                m_guiAppPid = binaryExePid;
                if (binaryExePid == -1) {
                    qWarning() << "launch singleton instance app is: " << fullDesktopName;
                    return false;
                }
                qDebug() << "start app id" << m_guiAppPid;
                m_appsInfo[dekstopFilename].push_back(ProcessInfo(m_guiAppPid, (QList<qint64>() << m_guiAppPid), ProcessInfo::ForegroundProcess, QList<WindowId>(), (QStringList() << args)));
                updateAppStatus(dekstopFilename, m_guiAppPid, ProcessInfo::ForegroundProcess);
            }
        }
    } else {
        qWarning() << "Failed to start app: " << process->errorString();
        process->deleteLater();
        return false;
    }
#ifdef INVISIBLE_ENABLE
    if (m_isTabletMode) {
        m_timerContainer.append({ currentCounter + interval, dekstopFilename });
        if (!m_timer->isActive()) {
            m_timer->start(1000);
        }
    }
#endif

    int startedProcessId = m_guiAppPid;
    qDebug() << "--------startedProcessId---------" << startedProcessId;
    connect(process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
          [this, dekstopFilename, process, startedProcessId](int exitCode, QProcess::ExitStatus exitStatus) {
        Q_UNUSED(exitCode)
        Q_UNUSED(exitStatus)
        qDebug() << "process fished" << dekstopFilename << "-" << process->pid() << "--" << startedProcessId << "-" << m_guiAppPid;
        qint64 backendPid = filterPidWithBackend();
        QString finishExec = getAppStartExec(dekstopFilename);

        // ToDo 针对kmre应用打开后显示在任务栏的特殊处理
        qDebug() << "!!!!!!!程序fished后appExec!!!!" << finishExec << "-" << isTerminalProgram;
        if (finishExec.contains("/usr/bin/startapp") || isTerminalProgram == true) {
            qDebug() << "---删除kmre或者terminal的desktop文件---" << dekstopFilename;
            m_appsInfo.remove(dekstopFilename);
        }
        if (backendPid > 0) {
            qDebug() << "___backendPid___" << backendPid;
            if (m_appsInfo.contains(dekstopFilename)) {
                int latestProcess = m_appsInfo.value(dekstopFilename).count() - 1;
                if (latestProcess >= 0 && m_appsInfo[dekstopFilename].count() > latestProcess) {
                    m_appsInfo[dekstopFilename][latestProcess].setPid(backendPid);
                }
            }
        } else {
            auto processInfo = m_appsInfo.value(dekstopFilename);
            for (int i = 0; i < processInfo.count(); ++i) {
                if (processInfo.at(i).getPid() == startedProcessId) {
                    if (isTerminalProgram == false) {
                        qDebug() << "processInfo.at(i).pid == startedProcessId" << startedProcessId;
                        m_appsInfo[dekstopFilename].removeAt(i);
                        if (m_appsInfo.value(dekstopFilename).isEmpty()) {
                            qDebug() << "-1--应用关闭，删除desktop文件---";
                            m_appsInfo.remove(dekstopFilename);
                        }
                    }
                }
            }
#ifdef INVISIBLE_ENABLE
            deleteTimer(dekstopFilename);
#endif
        }
        process->deleteLater();
    });

    connect(this, &AppLauncher::aboutToQuitMainApp, [process] {
        if (process) {
            process->kill();
        }
    });
    return true;
}

bool AppLauncher::doActiveProcessByWid(const WId &wid)
{
    qDebug() << __FUNCTION__ << wid;
    qint64 pid;
    auto appInfoIt = m_appsInfo.begin();
    while (appInfoIt != m_appsInfo.end()) {
        auto widInfoIt = appInfoIt->begin();
        while (widInfoIt != appInfoIt->end()) {
            if (widInfoIt->getWids().contains(wid)) {
                pid = widInfoIt->getPid();
                qDebug() << __FUNCTION__ << "MoveProcess " << ProcessInfo::ForegroundProcess << pid;
                QList<quint64> childrePidList = ProcInfo::getChildrenPid(pid);
                for (int i=0; i<childrePidList.count(); ++i) {
                    qint64 childrenPid = childrePidList.at(i);
                    m_cgroupInterface->MoveProcess(childrenPid, ProcessInfo::ForegroundProcess);
                }
                QList<quint64> tasksList = ProcInfo::getTasksFromPid(pid);
                for (int i=0; i<tasksList.count(); ++i) {
                    qint64 tasks = tasksList.at(i);
                    qDebug() << "---tasks---" << tasks;
                    m_cgroupInterface->MoveProcess(tasks, ProcessInfo::ForegroundProcess);
                }
                m_cgroupInterface->MoveProcess(pid, ProcessInfo::ForegroundProcess);
                widInfoIt->setStatus(ProcessInfo::ForegroundProcess);
                return true;
            }
            ++ widInfoIt;
        }
        ++ appInfoIt;
    }
    return false;
}

void AppLauncher::windowChanged(const WindowId &window, bool added)
{
//    KWindowInfo windowInfo(wid, NET::WMPid, NET::WM2DesktopFileName);
//    int pid = windowInfo.pid();
    uint wid = window.toUInt();
    int pid = WindowManager::getPid(window);
    qDebug() << "--windowChanged---" << "wid: " << wid << "pid: " << pid << " " << added;

    ProcInfo procInfo(pid);
    if (procInfo.isKmreApp(pid)) {
        m_kmrePid = pid;
        qDebug()<< "---procInfo.isKmreApp(m_kmrePid)--开启kmre应用-" << m_kmrePid;
        Q_EMIT kmreAppPid();
    }
    isTencentApp = false;
    if (procInfo.isTencentApp(pid)) {
        isTencentApp = true;
        qDebug()<< "---procInfo.isTencentApp---开启腾讯应用-" << pid;
        Q_EMIT tencentAppSignal(pid, wid);
    }

//    if (procInfo.isTerminalApp(pid)) {
//        m_terminalPid = pid;
//        qDebug()<< "---procInfo.isTerminalApp(pid)--开启终端应用-" << m_terminalPid;
//        Q_EMIT terminalPid(pid, wid);
//        return;
//    }
    auto it = m_appsInfo.begin();
    while (it != m_appsInfo.end()) {
        auto processInfoIt = it->begin();
        while (processInfoIt != it->end()) {
            if (added) {
                qDebug() << "---added---" << added << "-" << processInfoIt->getPid();
                if (processInfoIt->getPid() == pid) {
                    if (added) {
                        if (!processInfoIt->getWids().contains(wid)) {
                            qDebug() << "windowChanged add" << pid;
                            processInfoIt->pushWids(wid);
//                            processInfoIt->getWids().append(wid);
                        }
                        return;
                    }
                } else {
                    // 针对快捷键打开应用做特殊处理
                    if (procInfo.getCmdlineFromPid(pid) == "mate-terminal") {
                        openShortcutApp(wid, pid);
                        return;
                    }
                    // 针对终端启动应用做特殊处理
                    if (procInfo.isTerminalApp(pid)) {
                        int ppid = procInfo.getTerAppPPid(pid);
                        if (processInfoIt->getChildrenPid().contains(ppid)) {
                            qDebug() << "---终端应用---windowChanged add" << ppid;
                            processInfoIt->pushWids(wid);
//                            processInfoIt->getWids().push_back(wid);
                            return;
                        }
                    }
                }
            } else {
                if (processInfoIt->getWids().contains(wid)) {
                     processInfoIt->removeWids(wid);
//                    processInfoIt->getWids().removeOne(wid);
                    qDebug() << "windowChanged remove" << processInfoIt->getPid();
                    if (procInfo.getCmdlineFromPid(processInfoIt->getPid()).isEmpty()) {
                        qDebug() << "---ShortcutApp---特殊处理删除desktop---" << it.key();
                        m_appsInfo.remove(it.key());
                    }
                    return;
                }
            }
            ++ processInfoIt;
        }
        ++ it;
    }
    if (m_appsInfo.isEmpty()) {
        qDebug() << "!!!m_appsInfo.isEmpty()!!!" << procInfo.getCmdlineFromPid(pid);
        if (procInfo.getCmdlineFromPid(pid) == "mate-terminal") {
            openShortcutApp(wid, pid);
        }
    }
}

bool AppLauncher::isSelfSingleInstance(const QString &desktopName)
{
    if (desktopName.isEmpty()) {
        qWarning() << __FUNCTION__ << "desktopName is NULL!!!";
        return false;
    }
    if (getAppStartExec(desktopName).contains("TXEduPlatformRuntime")) {
        return true;
    }
    return false;
}

void AppLauncher::moveTasksAndpidToGroup(const qint64 &pid, ProcessInfo::AppStatus status)
{
    if (pid <=0) {
        qWarning() << __FUNCTION__ << "pid不合法！";
    }
    QList<quint64> childrePidList = ProcInfo::getChildrenPid(pid);
    QList<quint64> tasksList = ProcInfo::getTasksFromPid(pid);

    for (int i=0; i<childrePidList.count(); ++i) {
        qint64 childrenPid = childrePidList.at(i);
        m_cgroupInterface->MoveProcess(childrenPid, status);
    }

    for (int i=0; i<tasksList.count(); ++i) {
         qint64 tasks = tasksList.at(i);
         m_cgroupInterface->MoveProcess(tasks, status);
    }
}

void AppLauncher::moveCachePidToGroup(ProcessInfo::AppStatus status)
{
    qDebug() << __FUNCTION__;
    QString pidFilePath = "/sys/fs/cgroup/freezer/CachedProcess/tasks";
    QFile file(pidFilePath);
    if (!file.exists()) {
        qDebug() << "文件路径不存在！";
    }

    if (!file.open(QIODevice::ReadOnly)) {
        qDebug() << "文件打开失败！";
    }
    // 每次读取文件中的一行，然后输出读取到的字符串
    char* str = new char[200];
    qint64 readNum = file.readLine(str, 200);
    // 当读取出现错误（返回 -1）或者读取到的字符数为 0 时，结束读取
    while ((readNum != 0) && (readNum != -1)) {
        qDebug() << __FUNCTION__ << str;
        readNum = file.readLine(str, 200);
        m_cgroupInterface->MoveProcess(QString(str).toInt(), status);
    }
    file.close();
}
