#ifndef __KMCONTEXT_H__
#define __KMCONTEXT_H__

#include "KMExports.h"
#include "KMMetaData.h"

using namespace std;

typedef enum
{
    KAIMING_POLICY_NONE,
    KAIMING_POLICY_SEE,
    KAIMING_POLICY_TALK,
    KAIMING_POLICY_OWN
} KMPolicy;

typedef enum
{
    KAIMING_CONTEXT_SHARED_NONE = 0,
    KAIMING_CONTEXT_SHARED_NETWORK = 1 << 0,
    KAIMING_CONTEXT_SHARED_IPC = 1 << 1,
} KMContextShares;

typedef enum
{
    KAIMING_CONTEXT_SOCKET_NONE = 0,
    KAIMING_CONTEXT_SOCKET_X11 = 1 << 0,
    KAIMING_CONTEXT_SOCKET_WAYLAND = 1 << 1,
    KAIMING_CONTEXT_SOCKET_PULSEAUDIO = 1 << 2,
    KAIMING_CONTEXT_SOCKET_SESSION_BUS = 1 << 3,
    KAIMING_CONTEXT_SOCKET_SYSTEM_BUS = 1 << 4,
    KAIMING_CONTEXT_SOCKET_FALLBACK_X11 = 1 << 5, /* For backwards compat, also set SOCKET_X11 */
    KAIMING_CONTEXT_SOCKET_SSH_AUTH = 1 << 6,
    KAIMING_CONTEXT_SOCKET_PCSC = 1 << 7,
    KAIMING_CONTEXT_SOCKET_CUPS = 1 << 8,
    KAIMING_CONTEXT_SOCKET_GPG_AGENT = 1 << 9,
} KMContextSockets;

typedef enum
{
    KAIMING_CONTEXT_DEVICE_NONE = 0,
    KAIMING_CONTEXT_DEVICE_DRI = 1 << 0,
    KAIMING_CONTEXT_DEVICE_ALL = 1 << 1,
    KAIMING_CONTEXT_DEVICE_KVM = 1 << 2,
    KAIMING_CONTEXT_DEVICE_SHM = 1 << 3,
} KMContextDevices;

typedef enum
{
    KAIMING_CONTEXT_FEATURE_NONE = 0,
    KAIMING_CONTEXT_FEATURE_DEVEL = 1 << 0,
    KAIMING_CONTEXT_FEATURE_MULTIARCH = 1 << 1,
    KAIMING_CONTEXT_FEATURE_BLUETOOTH = 1 << 2,
    KAIMING_CONTEXT_FEATURE_CANBUS = 1 << 3,
    KAIMING_CONTEXT_FEATURE_PER_APP_DEV_SHM = 1 << 4,
} KMContextFeatures;

/**
 * @brief : 主要用于封装各种参数上下文
 */
class KMContext
{
public:
    KMContext();
    ~KMContext();

    bool loadMetadata(KMMetaData *metadata);

    /**
     * @brief : 创建 exports 结构体
     */
    unique_ptr<KMExports> createExports(const fs::path &appPath, const vector<fs::path> &extraAppIdPaths, bool create, string &xdgDirConfOut);

    const KMContextShares &getShares() { return m_shares; }

    const KMContextShares &getSharesValid() { return m_sharesValid; }

    const KMContextSockets &getSockets() { return m_sockets; }

    const KMContextSockets &getSocketsValid() { return m_socketsValid; }

    const KMContextDevices &getDevices() { return m_devices; }

    const KMContextDevices &getDevicesValid() { return m_devicesValid; }

    const KMContextFeatures &getFeatures() { return m_features; }

    const KMContextFeatures &getFeaturesValid() { return m_featuresValid; }

    const map<string, string> &getEnvVars() { return m_envVars; }

    const map<string, int> &getPersistent() { return m_persistent; }

    const map<string, KMFilesystemMode> &getFilesystems() { return m_filesystems; }

    const map<string, std::vector<std::string>> &getGenericPolicy() { return m_genericPolicy; }

    const map<string, KMPolicy> &getSystemBusPolicy() { return m_systemBusPolicy; }

    const map<string, KMPolicy> &getSessionBusPolicy() { return m_sessionBusPolicy; }

    bool addShares(const std::string &share);
    bool removeShares(const std::string &share);

    bool addSockets(const std::string &socket);
    bool removeSockets(const std::string &socket);

    bool addDevices(const std::string &device);
    bool removeDevices(const std::string &device);

    bool addFeatures(const std::string &feature);
    bool removeFeatures(const std::string &feature);

    void addEnvVar(const std::string &env, const std::string &value);

    void setSessionBusPolicy(const std::string &name, KMPolicy policy);
    void setSystemBusPolicy(const std::string &name, KMPolicy policy);

    bool applyGenericPolicy(const std::string &policy_key, const std::string &value);

    void addPersistent(const string &path, int val);

    void merge(const KMContext &other);

    bool allowsFeatures(KMContextFeatures features) { return (m_features & features) == features; }

    KMRunFlags getRunFlags();

    bool getNeedsSessionBusProxy() { return !m_sessionBusPolicy.empty(); }

    bool getNeedsSystemBusProxy() { return !m_systemBusPolicy.empty(); }

    static string policy2string(KMPolicy policy);

    void resetNonPermissions() { m_envVars.clear(); }

    void resetPermissions()
    {
        m_shares = KMContextShares::KAIMING_CONTEXT_SHARED_NONE;
        m_sockets = KMContextSockets::KAIMING_CONTEXT_SOCKET_NONE;
        m_devices = KMContextDevices::KAIMING_CONTEXT_DEVICE_NONE;
        m_features = KMContextFeatures::KAIMING_CONTEXT_FEATURE_NONE;

        m_sharesValid = KMContextShares::KAIMING_CONTEXT_SHARED_NONE;
        m_socketsValid = KMContextSockets::KAIMING_CONTEXT_SOCKET_NONE;
        m_devicesValid = KMContextDevices::KAIMING_CONTEXT_DEVICE_NONE;
        m_featuresValid = KMContextFeatures::KAIMING_CONTEXT_FEATURE_NONE;

        m_filesystems.clear();
        m_persistent.clear();
        m_genericPolicy.clear();
        m_systemBusPolicy.clear();
        m_sessionBusPolicy.clear();
    }

    void allowHostFs() { takeFilesystem("host", KAIMING_FILESYSTEM_MODE_READ_WRITE); }

    void takeFilesystem(const std::string &fs, const KMFilesystemMode &mode);

    void saveMetadata(bool flatten, std::shared_ptr<KMMetaData> &metadata);

    std::shared_ptr<KMExports> getExportsFull(const std::string &appIdDir, const std::vector<std::string> &extraAppIdDirs, bool doCreate, bool includeDefaultDirs, std::string &xdgDirsConfOut, bool &homeAccessOut);

    std::vector<std::string> getSessionBusPolicyAllowedOwnNames();

public:
    static bool parseFilesystem(const string &filesystemAndMode, bool cancel, string &filesystem_out, KMFilesystemMode &mode);
    static std::string parseFilesystemFlags(const string &filesystemAndMode, bool cancel, KMFilesystemMode &outMode);
    static std::string unparseFilesystemFlags(const std::string &path, KMFilesystemMode mode);
    static bool getXdgUserDirFromString(const std::string &filesystem, std::string &configKey, std::string &suffix, std::string &dir);
    static bool getXdgDirFromPrefix(const std::string &prefix, std::string &where, std::string &dir);
    static std::string getXdgDirFromString(const std::string &filesystem, std::string &suffix, std::string &where);

private:
    template<typename T>
    T fromString(const string &str, const char **names);

    void addShares(KMContextShares shares);
    void removeShares(KMContextShares shares);

    void addSockets(KMContextSockets socket);
    void removeSockets(KMContextSockets socket);

    void addDevices(KMContextDevices device);
    void removeDevices(KMContextDevices device);

    void addFeatures(KMContextFeatures feature);
    void removeFeatures(KMContextFeatures feature);

    void buildinExport(KMExports &exports, const std::string &appIdDir, const std::vector<std::string> &extraAppIdDirs, bool doCreate, std::string &xdgDirsConfOut, bool &homeAccessOut);

    KMContextShares m_shares;
    KMContextShares m_sharesValid;
    KMContextSockets m_sockets;
    KMContextSockets m_socketsValid;
    KMContextDevices m_devices;
    KMContextDevices m_devicesValid;
    KMContextFeatures m_features;
    KMContextFeatures m_featuresValid;

    map<string, string> m_envVars;
    map<string, int> m_persistent;
    map<string, KMFilesystemMode> m_filesystems;
    map<string, std::vector<std::string>> m_genericPolicy;
    map<string, KMPolicy> m_systemBusPolicy;
    map<string, KMPolicy> m_sessionBusPolicy;
};

#endif
