/*
 * 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 APPLAUNCHER_H
#define APPLAUNCHER_H

#include <QObject>
#include <QMap>
#include <QPair>
#include <QList>
#include <KDesktopFile>
#include <QSettings>
#include <QTimer>
#include <QFile>
#include <QtConcurrent>
#include <QFuture>
#include <QMutex>
#include <QDebug>
#include <kwindowsystem.h>
#include <KWindowInfo>
#include <KConfigGroup>
#include <QMimeDatabase>
#include <QVector>
#include <QJsonObject>
#include <QFileSystemWatcher>
#include "utils/processinfo.h"
#include "dbusinterfaces/cgroupinterface.h"
#include "dbusinterfaces/kwininterface.h"
#include "dbusservices/whitelistmanager.h"
#include "common.h"
#include "core/appinfomanager.h"

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

class QProcess;
class QTimer;
class QFileSystemWatcher;
class ChildrenProcessWatcher;
class AppLauncherDaemon;
class WhiteListManager;
class ProcessInfo;
class AppLauncher : public QObject
{
    Q_OBJECT
public:
    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 Open(const QString &fileName);
    bool LaunchDefaultAppWithUrl(const QString &url);
    bool ActiveProcessByWid(const uint &wid);
    bool ActiveProcessByPid(const int &pid);
    QString AppDesktopFileNameByPid(qint64 pid);
    QString AppDesktopFileNameByWid(qint64 wid);
    /**
     * @brief RecommendAppLists 获取可打开文件的推荐应用列表
     * @param fileName 文件名称
     * @return 返回默认打开应用和推荐打开应用的列表容器
     */
    QVector<QStringList> RecommendAppLists(const QString &fileName);

Q_SIGNALS:
    void aboutToQuitMainApp();

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

    ///
    /// \brief hasWindow 根据desktop文件判断当前文件是否会显示窗口
    /// \param desktopFile
    /// \return
    ///
    bool hasWindow(const QString &desktopFile);

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

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

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

    /**
     * @brief launchDesktopApp 优先使用GIO的接口打开应用，如果打开失败尝试调用 \memberof launch
     * @param desktopFile 绝对路径desktop文件
     * @param args 指定的参数
     * @param execPath 应用的启动二进制文件，用于 \memberof launch函数
     * @param execArgs 启动参数，用于 \memberof launch函数
     * @return 启动成功返回true
     */
    bool launchDesktopApp(const QString &desktopFile,
                          const QStringList &args,
                          const QString &execPath,
                          const QStringList &execArgs);

    /**
     * @brief activeAppWindow
     * @param wid
     * @param pids
     * @return
     */
    bool activeAppWindow(quint32 wid, QList<int> pids);
    bool activePolkitWindow();

    static void childProcessCallback(GDesktopAppInfo *appInfo, GPid pid, gpointer userData);

    /**
     * @brief getLaunchAppLists 利用gio的接口获取文件的默认打开应用和推荐打开应用列表
     * @param fileName 文件名称
     * @return 返回可打开文件的应用列表
     */
    QVector<QStringList> getLaunchAppLists(const QString &fileName);

private:
    org::ukui::KWin *m_kwinInterface;
    bool m_isWayland;
//    QMap<QString, QPair<QString, QStringList>> m_desktopExec;
    QMutex m_mutex;
    int m_polkitWindowPid;
    uint m_polkitWindowId;
    AppInfoManager &m_appInfoManager = common::Singleton<AppInfoManager>::GetInstance();

    const quint64 interval = 10;//30 * 60;
    const QString kKmreAppMainWindow = "/usr/bin/kylin-kmre-window";
    const QStringList polkitWindowPidCmdline = {
        "/usr/lib/x86_64-linux-gnu/ukui-polkit/polkit-ukui-authentication-agent-1",
        "/usr/lib/aarch64-linux-gnu/ukui-polkit/polkit-ukui-authentication-agent-1"
    };
    const QStringList kNeedingPolkitWindowApps = {
        "gparted.desktop"
    };
    const QStringList kFreeApps = {
        "ukui-search-menu.desktop",
        "onboard.desktop"
    };
    struct {
        int maxname;
        int fd[2];
        int launcher[2]; /* socket pair for launcher communication */
        int deadpipe[2]; /* pipe used to detect dead children */
        int initpipe[2];
        int wrapper; /* socket for wrapper communication */
        int accepted_fd; /* socket accepted and that must be closed in the child process */
        char result;
        int exit_status;
        pid_t fork;
        pid_t launcher_pid;
        pid_t kded_pid;
        int n;
        char **argv = nullptr;
        int (*func)(int, char *[]);
        int (*launcher_func)(int);
        bool debug_wait;
        QByteArray errorMsg;
        bool launcher_ok;
        bool suicide;
    } d;
};

#endif // APPLAUNCHER_H
