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

#ifndef APPINFOMANAGER_H
#define APPINFOMANAGER_H

#include <QObject>
#include <QMutex>
#include "appinfo.h"

class AppStatusManager;
class AppInfoManager : public QObject
{
    friend class AppStatusManager;

    Q_OBJECT
public:
    typedef QPair<QString, QString> AppIdInstanceName;
    typedef QMap<QString, QPair<QString, QStringList>> DesktopFileExecInfo;

    explicit AppInfoManager(QObject *parent = nullptr);

    /**
     * @brief isKmreApp 根据desktop文件判断当前应用是不是kmre的应用
     * @param fullDesktopName
     * @return
     */
    static bool isKmreApp(const QString &fullDesktopName);

    /**
     * @brief newAppInstance 创建应用实例的cgroup分组
     * @param desktopFile 全路径下的desktop文件
     * @param forkPid launcher中进行打开应用的进程pid
     * @return cgroupName
     */
    QString newAppInstance(const QString &desktopFile,
                           const QStringList &args,
                           const QList<int> &pids,
                           appinfo::AppType type);

    QString desktopFile(int pid);
    QString desktopFile(quint32 wid);
    bool thawApp(int pid);
    bool thawApp(quint32 wid);

    /**
     * @brief wids 获取desktopFile应用的wids列表
     * @param desktopFile 全路径下的desktop文件
     * @return 所有该应用对应的wid列表
     */
    QList<quint32> wids(const QString &desktopFile);
    QList<quint32> wids(const QString &desktopFile, const QString &instName);

    QList<int> pids(const QString &desktopFile, const QString &instName);

    QString cmdline(int pid);
    DesktopFileExecInfo desktopFilesExec() const;

    /**
     * @brief runningStatus
     * @param desktopFile
     * @param args
     * @return <runningstatus, instname, timeout>
     */
    std::tuple<appinfo::RunningStatus, QString, bool> runningStatus(const QString &desktopFile, const QStringList &args) const;

    QString inhibit(const QString &desktopFile,
                    uint pid,
                    const QString &reason,
                    uint flags);
    void unInhibit(const QString &cookie);
    bool isInhibited(const QString &appId, const QString &instName);

    /**
     * @brief ThawApps session关机时调用，解冻所有应用
     * @return 解冻成功返回true
     */
    bool ThawApps();

public Q_SLOTS:
    void onActiveWindowChanged(quint32 currentWid, int currentPid, quint32 preWid, int prePid);
    void onWindowAdded(quint32 wid, int pid);
    void onWindowRemoved(quint32 wid);
    void onMprisDbusAdded(int pid, const QString &dbusService);
    void updateDesktopExecsInfo(const QString &path);
    void onResourceThresholdWarning(Policy::Feature resource, Policy::ResourceUrgency level);
    void onTabletModeChanged(bool tabletMode);
    void onChildPidFinished(const int &pid);
    void onAboutToQuitApp();

Q_SIGNALS:
    void appFinished(const QString &desktopNmae, qlonglong pid, uint wid);
    void appLaunched(const QString &desktopNmae, qlonglong pid, uint wid);
private:
    /**
     * @brief initDesktopExecsInfoWatcher 初始化应用desktopFileExec字段信息
     */
    void initDesktopExecsInfoWatcher();

    /**
     * @brief initConnections
     */
    void initConnections();

    /**
     * @brief appIdByDesktopFile 通过全路径下的desktop文件获得应用MD5值
     * @param desktopFile 全路径下的desktop文件
     * @return appId（MD5值）
     */
    QString appIdByDesktopFile(const QString &desktopFile) const;

    QString desktopFileName(const QString &desktopFile);

    /**
     * @brief desktopFileExec 获取desktop文件中Exec字段
     * @param desktopFileName desktop名称
     * @return Exec字段
     */
    QPair<QString, QStringList> desktopFileExec(const QString &desktopFileName);

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

    /**
     * @brief appInstanceInfoWithWindowId 根据指定信息查找appId和instName
     * @param wid、pid、instName
     * @return QPair<QString, QString>
     */
    AppIdInstanceName appInstanceInfoWithWindowId(quint32 wid);
    AppIdInstanceName appInstanceInfoWithPid(int pid);
    AppIdInstanceName appInstanceInfoWithInstName(const QString &instName);
    appinfo::AppInstance appInstanceWithPid(int pid);

    /**
     * @brief findAndroidNameByWid 调用kmre接口通过wid找到对应移动android包名
     * @param wid 窗口wid
     * @return wid对应的包名则返回
     */
    QString findAndroidNameByWid(const quint32 &wid);

    /**
     * @brief findDesktopCmdline 根据应用pid找到对应的应用id和desktop文件 // 根据cmdline进行匹配
     * @param pid 应用pid
     * @return 返回存储应用的desktop和id
     */
    QPair<QString, QString> findDesktopCmdline(int const &pid);

    /**
     * @brief findGlobalDesktopFile 非应用管理拉起的应用，根据应用的cmdline值匹配到标准路径下对应的desktop文件和id
     * @param cmdline 应用的cmdline
     * @return 应用对应的desktop和id
     */
    QString findGlobalDesktopFile(QString const &cmdline);

    /**
     * @brief desktopFileFromPackage 通过cmdline获得package里面的desktopFile信息
     * @param cmdline 进程启动信息
     * @return desktopFile
     */
    QString desktopFileFromCmdline(QString cmdline);

    /**
     * @brief pkgNameFromCmdline 通过cmdline获取包名
     * @param cmdline 应用启动进程信息
     * @return 所查询的包信息列表
     */
    QStringList pkgNameFromCmdline(const QString &cmdline);

    /**
     * @brief isDesktopFileNoDisplay 判断desktop文件中的NoDisplay字段
     * @param desktopFileName desktop名称
     * @return  字段为true返回true
     */
    bool isDesktopFileNoDisplay(const QString &desktopFileName);

    /**
     * @brief findChildrenPids 获取pid进程下所有的子进程
     * @return 所有的子进程
     */
    QList<int> findChildrenPids(const int &pid);

    /**
     * @brief updateAppInfomations 更新appinfos中已保存的appId的信息
     * @param fullDesktopName
     * @param wid
     * @param pid
     */
    void updateAppInfomations(const QString &fullDesktopName, const quint32 &wid, const int &pid);

    /**
     * @brief addAppInfomations 非应用管理打开的应用添加一下信息
     * @param fullDesktopName
     * @param wid
     * @param pid
     */
    void addAppInfomations(const QString &fullDesktopName, const QString &cmdline, const quint32 &wid, const int &pid);

    /**
     * @brief setInstanceInfos 设置实例信息
     * @param appId
     * @param instName
     */
    void updateInstanceInfos(const AppIdInstanceName &appIdInstance, const quint32 &wid, const int &pid);

    QString findAndroidNameByPid(const quint32 &pid);

    // <appid, app instance>
    QPair<QString, appinfo::AppInstance> iterateThroughAppInstances(
            const std::function<bool(const appinfo::AppInstance &instance)> &callback);

private:
    typedef QMap<QString, appinfo::AppInfo> AppInformation;
    struct Inhibitor
    {
        QString appId;
        QString cookie;
        int pid;
        inhibitor::Inhibitor type;
    };

    AppInformation appInfos;
    QMap<int, QString> m_tmpMprisDbus;
    AppStatusManager *m_appStatusManager;
    QMap<QString, QPair<QString, QStringList>> m_desktopsExec;
    QMutex m_mutex;
    QFileSystemWatcher *m_desktopFileWatcher;
    QList<Inhibitor> m_inhibitors;
    const QString kKmreAppMainWindow = "/usr/bin/kylin-kmre-window";
    const QStringList kNoCgroupApps = {
    };
};

#endif // APPINFOMANAGER_H
