/*
 * 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 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/>.
 */

#ifndef APPLAUNCHER_H
#define APPLAUNCHER_H

#include <QObject>
#include <QMap>
#include <QPair>
#include <QList>
#include <QProcess>
#include <KDesktopFile>
#include <QSettings>
#include <QTimer>
#include <QFile>
#include <QStandardPaths>
#include <QtConcurrent>
#include <QFuture>
#include <QThreadPool>
#include <QDebug>
#include <kwindowsystem.h>
#include <KWindowInfo>
#include <KConfigGroup>
#include <QFileSystemWatcher>
#include "utils/processinfo.h"
#include "utils/procinfo.h"
#include "dbusservices/applauncherdaemon.h"
#include "dbusinterfaces/cgroupinterface.h"
#include "dbusinterfaces/kwininterface.h"
#include "dbusinterfaces/statusmanagerinterface.h"
#include "dbusservices/whitelistmanager.h"
#include "windowmanager/windowmanager.h"

using namespace kdk;

class QProcess;
class QTimer;
class ChildrenProcessWatcher;
class AppLauncherDaemon;
class WhiteListManager;
class ProcessInfo;
class ProcInfo;
class AppLauncher : public QObject
{
    Q_OBJECT
public:

    struct Timer {
        quint64 deadCounter;
        QString desktopFile;
        bool operator==(const Timer &l) const {
            return l.deadCounter == this->deadCounter &&
                    l.desktopFile == this->desktopFile;
        }
    };

    typedef QList<ProcessInfo> ProcessList ;
    typedef QMap<QString, ProcessList> AppsInfo;

    explicit AppLauncher(QObject *parent = nullptr);

    bool LaunchApp(const QString &desktopFile);
    bool LaunchAppWithArguments(const QString &desktopFile,const QStringList &args);
    bool LaunchXdgFile(const QString &desktopFile, const QString &fileName);
    bool LaunchDefaultAppWithUrl(const QString &url);
    bool ActiveProcessByWid(const WId &wid);
    QString AppDesktopFileNameByPid(qint64 pid);
    QString AppDesktopFileNameByWid(qint64 wid);
#if 0
    QList<uint> RunningApplications();
#endif
    void raiseApplication();
    bool raiseOnCurrentDesktop();

    ///
    /// \brief setPowerSavingModeEnable 打开/关闭省电模式
    /// \param enable 为 true 时打开省电模式
    ///
    void setPowerSavingModeEnable(bool enable);

public Q_SLOTS:
    ///
    /// \brief killCachedApp 结束掉处理缓存状态的app
    ///
    void killCachedApp();

private Q_SLOTS:
    void tabletModeChanged(bool tabletMode);
    void activeWindowChanged(const WindowId &window);
#ifdef INVISIBLE_ENABLE
    void timeOut();
    void handleTimeOut();
#endif

    ///
    /// \brief backendChildFinished 已 & 方式启动的应用结束
    /// \param pid
    ///
    void backendChildFinished(pid_t pid);
    void aboutToQuitApp();
    void windowAdded(const WindowId &window);
    void windowRemoved(const WindowId &window);

Q_SIGNALS:
    void aboutToQuitMainApp();
    void kmreAppPid();
    void terminalPid(int pid, WId wid);
    void tencentAppSignal(int pid, WId wid);
private:
    void initConnections();

    void updateAppStatus();
    void updateAppStatus(const qint64 &pid, ProcessInfo::AppStatus status);
    void updateAppStatus(const QString &desktopFile, qint64 pid, ProcessInfo::AppStatus status);

    /**
     * @brief isRuning  判断应用是否运行(根据desktop文件和窗口wid判断应用是否运行)
     * @param desktopFile   应用desktop名称
     * @return  运行状态返回true
     */
    bool isRuning(const QString &desktopFile);

    /**
     * @brief moveAllProcessesToGroup   移动所有的进程pid到指定分组
     * @param status 分组状态
     */
    void moveAllProcessesToGroup(ProcessInfo::AppStatus status);

    /**
     * @brief moveProcessesToGroup 移动特定进程到制定分组
     * @param status 分组状态
     */
    void moveProcessesToGroup(ProcessInfo::AppStatus status);

    /**
     * @brief getAppStartExec 获取desktop文件中Exec字段
     * @param desktopFileName desktop名称
     * @return Exec字段
     */
    QString getAppStartExec(const QString &desktopFileName);

    /**
     * @brief getAllDesktopFiles 获取标准路径下所有的desktop文件
     * @return desktop列表
     */
    QStringList getAllDesktopFiles();

    /**
     * @brief openShortcutApp   快捷键方式打开应用
     * @param wid   窗口wid
     * @param pid   应用pid
     */
    void openShortcutApp(WId wid, int pid);
#ifdef INVISIBLE_ENABLE
    ///
    /// \brief addTimer 当 \param desktopFile 应用处于 InvisibleProcess 状态时，
    ///  添加定时器时间，超时后移动至 CachedProcess 分组
    /// \param desktopFile
    ///
    void addTimer(const QString &desktopFile);
    void deleteTimer(const QString &desktopFile);
#endif

    /**
     * @brief filterPidWithBackend  找到当前 & 方式启动的应用 pid
     * @return  如果有 & 启动的应用，返回该应用的 pid，否则返回 -1
     */
    qint64 filterPidWithBackend();

    /**
     * @brief desktopFullFilename   获得带全路径的desktop名称
     * @param desktopFileName   应用的desktop名称
     * @return  带全路径的desktop名称
     */
    QString desktopFullFilename(const QString &desktopFileName);

    /**
     * @brief doLaunchApp   启动应用
     * @param desktopFile   应用的desktop名称
     * @param args  启动参数
     * @return  启动成功返回true
     */
    bool doLaunchApp(const QString &desktopFile, const QStringList &args = QStringList());

    /**
     * @brief doActiveProcessByWid  提供给多任务视图互动关闭时将进程移动至前台分组
     * @param wid   滑动关闭应用的进程
     * @return  移动成功返回true
     */
    bool doActiveProcessByWid(const WId &wid);

    void windowChanged(const WindowId &window, bool added);

    /**
     * @brief isSelfSingleInstance  存储应用自身单实例的方法
     * @param desktopName   应用的desktop名称
     * @return
     */
    bool isSelfSingleInstance(const QString &desktopName);

    /**
     * @brief moveTasksAndpidToGroup 移动一个pid的子进程和tasks目录下的所有线程到一个分组
     * @param pid 需要移动的主进程pid
     * @param status 移动至分组的分类名称
     */
    void moveTasksAndpidToGroup(const qint64 &pid, ProcessInfo::AppStatus status);

    /**
     * @brief moveCachePidToGroup 保护机制，防止一些僵尸进程和线程一直保存在缓存分组中
     */
    void moveCachePidToGroup(ProcessInfo::AppStatus status);

private:
    bool m_isTabletMode;
    bool m_isMulTaskView;
    bool isTencentApp;

    int m_guiAppPid;
    int m_kmrePid;
    int m_terminalPid;
    AppsInfo m_appsInfo;
    bool isTerminalProgram; 
    QStringList m_desktopList;
    QString m_desktopName;
    QFileSystemWatcher m_FileWatcher;

#ifdef INVISIBLE_ENABLE
    QTimer *m_timer;
    // contains the time when the app would be cached(CachedProcess)
    QList<Timer> m_timerContainer;
    quint64 currentCounter;
#endif
    bool m_powerSavingEnabled;
    com::kylin::CGroup::AppManager *m_cgroupInterface;
    org::ukui::KWin *m_kwinInterface;
    com::kylin::statusmanager::interface *m_statusManagerInterface;
    AppLauncherDaemon *m_appLauncherDaemon;
    WhiteListManager *m_whiteListManager;

    const quint64 interval = 10;//30 * 60;

};

#endif // APPLAUNCHER_H
