#ifndef KMUTIL_H
#define KMUTIL_H


#include <boost/process.hpp>
#include <glib.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>

#include <ctime>
#include <filesystem>
#include <list>
#include <map>
#include <set>
#include <sstream>
#include <string>
#include <vector>
#include <regex>
#include <stdexcept>
#include <locale>
#include <nlohmann/json.hpp>

#include "KMCommonTypePrivate.h"
#include "KMFileGuard.h"
#include "KMRemoteSummary.h"

using namespace std;
using json = nlohmann::json;
namespace fs = std::filesystem;
namespace bp = boost::process;

// 默认环境变量
static map<string, string> globalEnvDefault = {
    { "PATH", "/app/bin:/usr/bin" },
    { "LD_LIBRARY_PATH", "" },
    { "XDG_CONFIG_DIRS", "/app/etc/xdg:/etc/xdg" },
    // {"XDG_DATA_DIRS", "/app/share:/usr/share"},
    { "XDG_DATA_DIRS", "/app/share:/usr/share:/usr/share/runtime/share:/run/host/user-share:/run/host/share" },
    { "SHELL", "/bin/sh" },
    { "TEMP", "" },
    { "TEMPDIR", "" },
    { "TMP", "" },
    { "TMPDIR", "" },
    { "XDG_RUNTIME_DIR", "" },
    { "PYTHONPATH", "" },
    { "PERLLIB", "" },
    { "PERL5LIB", "" },
    { "XCURSOR_PATH", "" },
    { "GST_PLUGIN_PATH_1_0", "" },
    { "GST_REGISTRY", "" },
    { "GST_REGISTRY_1_0", "" },
    { "GST_PLUGIN_PATH", "" },
    { "GST_PLUGIN_SYSTEM_PATH", "" },
    { "GST_PLUGIN_SCANNER", "" },
    { "GST_PLUGIN_SCANNER_1_0", "" },
    { "GST_PLUGIN_SYSTEM_PATH_1_0", "" },
    { "GST_PRESET_PATH", "" },
    { "GST_PTP_HELPER", "" },
    { "GST_PTP_HELPER_1_0", "" },
    { "GST_INSTALL_PLUGINS_HELPER", "" },
    { "KRB5CCNAME", "" },
    { "XKB_CONFIG_ROOT", "" },
    { "GIO_EXTRA_MODULES", "" },
    { "GDK_BACKEND", "" },
};

static map<string, string> globalEnvDevel = {
    { "ACLOCAL_PATH", "/app/share/aclocal" }, { "C_INCLUDE_PATH", "/app/include" }, { "CPLUS_INCLUDE_PATH", "/app/include" }, { "LDFLAGS", "-L/app/lib " }, { "PKG_CONFIG_PATH", "/app/lib/pkgconfig:/app/share/pkgconfig:/usr/lib/pkgconfig:/usr/share/pkgconfig" }, { "LC_ALL", "en_US.utf8" }
};

static set<string> globalEnvCopy = { "PWD", "GDMSESSION", "XDG_CURRENT_DESKTOP", "XDG_SESSION_DESKTOP", "DESKTOP_SESSION", "EMAIL_ADDRESS", "HOME", "HOSTNAME", "LOGNAME", "REAL_NAME", "TERM", "USER", "USERNAME" };

static set<string> globalEnvCopyNoDevel = { "LANG", "LANGUAGE", "LC_ALL", "LC_ADDRESS", "LC_COLLATE", "LC_CTYPE", "LC_IDENTIFICATION", "LC_MEASUREMENT", "LC_MESSAGES", "LC_MONETARY", "LC_NAME", "LC_NUMERIC", "LC_PAPER", "LC_TELEPHONE", "LC_TIME" };

static vector<string> exportDirs = { "share/applications", "share/icons", "share/dbus-1/services", "share/gnome-shell/search-providers", "share/mime/packages", "share/metainfo", "bin" };

static vector<string> globalOfflinePackageSourceType = {".ok", ".layer", ".uab"};

// 创建枚举值与字符串的绑定映射
static std::unordered_map<KMPackageSourceType, std::string> packageSourceTypeWithString = { { KMPACKAGE_SOURCE_TYPE_ONLINE, "online" },
                                                                                            { KMPACKAGE_SOURCE_TYPE_OK, ".ok" },
                                                                                            { KMPACKAGE_SOURCE_TYPE_LAYER, ".layer" },
                                                                                            { KMPACKAGE_SOURCE_TYPE_UAB, ".uab" },
                                                                                            { KMPACKAGE_SOURCE_TYPE_OFFLINE, "offline" } };

class KMTmpfile
{
public:
    KMTmpfile();
    ~KMTmpfile();

    void init()
    {
        initialized = false;
        anonymous = false;
        srcDfd = -1;
        fg.setFd(-1);
        path = "";
    }

    int stealFd()
    {
        int fd = fg.stealFd();
        init();
        return fd;
    }

    int fd() { return fg.fd(); }

    bool initialized = false;
    bool anonymous = false;
    int srcDfd = -1;
    KMFileGuard fg{ -1 };
    std::string path;
};

typedef enum
{
    FILE_REPLACE_DATASYNC_NEW = (1 << 0),
    FILE_REPLACE_NODATASYNC = (1 << 1),
    FILE_REPLACE_INCREASING_MTIME = (1 << 2),
} FileReplaceFlags;

typedef enum
{
    LINK_TMPFILE_REPLACE,
    LINK_TMPFILE_NOREPLACE,
    LINK_TMPFILE_NOREPLACE_IGNORE_EXIST
} LinkTmpfileReplaceMode;

class KMUtil
{
public:
    KMUtil();

    static const string kmGetArch();
    static const string kmGetDefaultModule();
    static const string kmGetDefaultChannel();
    static const string getKMBox();
    static const string kmGetDBusProxy();
    static const string kmGetTimeZone();
    static bool kmStrIsInteger(const char *str);
    static bool kmValidatePathCharacters(const char *path);
    static KMRef kmString2Ref(string &str);
    static map<string, string> kmGetMinimalEnv(bool devel, bool useLdSoCache);
    static int kmRandom(int rangeStart, int rangeEnd);
    static string kmGenerateTempName(const string &tempName);
    static list<string> split(string str, char c);
    static std::string getKernelArch();
    static bool isLinux32Arch(const std::string &arch);

    /**
     * @brief 获取文件时间戳
     * 
     * @param filePath 文件路径
     * @return int 文件时间戳
     */
    static int getFileTimestamp(const string &filePath);

    /**
     * @brief 判断文件是否过期
     * 
     * 比较当前时间戳和文件时间戳大小，如果当前时间戳大于文件时间戳，并且时间差大于1小时，则返回true，否则返回false
     * 
     * @param filePath 文件路径
     * @param expiredTime 过期时间，单位为秒
     * @return true 文件过期
     * @return false 文件未过期
     */
    static bool isFileExpired(const string &filePath, int expiredTime);

    /**
     * @brief 判断文件是否是 json 文件
     * 
     * @param filePath 文件路径
     * @return true 是 json 文件
     * @return false 不是 json 文件
     */
    static bool isJsonFile(const string &filePath);

    /**
     * @brief 检查字符串的最后一个字符是否为 '\n'，如果是则将其移除。
     * 
     * 比如 description 会包含'\n'
     * 
     * @param str 返回去除 '\n' 的字符串
     */
    static void removeTrailingNewline(std::string &str);

    /**
     * @brief 判断一个 double 类型值是否是0
     * 
     * @param value 判断值
     * @param tolerance double的精度 
     * @return true  double 类型值是0
     * @return false double 类型值不是0
     */
    static bool isZero(double value, double tolerance = 1e-6);

    /**
     * @brief 格式化安装大小
     * 
     * @param size 安装大小
     * @return std::string 格式化安装工大小
     */
    static std::string formatInstalledSize(double size);

    /**
     * @brief 获取当前语言环境，并进行截断符合 desktop 规范
     * 
     * @return std::string 返回当前的语言环境字符串，取值如下：
     * 
     *  "en_US", "zh_CN", "C" 等
     */
    static std::string getCurrentLocale();

    /**
     * @brief 获取当前时间的函数
     * 
     * @return std::string 
     */
    static std::string getCurrentTime();

    /**
     * @brief 判断文件是否存在
     * 
     * @param fileName 文件名
     * @return true 文件存在
     * @return false 文件不存在
     */
    static bool fileExists(const string& fileName);

    /**
     * @brief 创建文件或者清空已有文件并写入当前时间
     * 
     * @param fileName 
     * @param fileName 文件名
     * @return true 文件操作成功
     * @return false 文件操作失败
     */
    static bool createFileIfNotExist(const string& fileName);

    /**
     * @brief 去除 ref 向量中的重复值
     * 
     * @param refVector 去除向量中重复值后的 ref 向量
     */
    static void removeDuplicateValues(vector<string> &refVector);

    /**
     * @brief 根据正则表达式判断版本号格式是否符合规范
     * 
     * @param version 版本号，如 "1.0.0.0"
     * @return true 符合4段式版本规范
     * @return false 不符合4段式版本规范
     */
    static bool isValidVersion(const std::string& version);

    /**
     * @brief 分割版本号字符串为整数数组
     * 
     * @param version 版本号，如 "1.0.0.0"
     * @return std::vector<int> 分割后的整数数组
     */
    static std::vector<int> splitVersion(const std::string& version);

    /**
     * @brief 比较版本号大小
     * 
     * 对两个版本号进行逐段比较，从主版本到扩充安全更新版本。比较规则如下：
     * 
     * - 如果 version1 的某一段小于 version2 的对应段，则返回 -1 表示 version1 小于 version2。
     * - 如果 version1 的某一段大于 version2 的对应段，则返回  1 表示 version1 大于 version2。
     * - 如果所有段都相等，则返回 0 表示两个版本号相等。
     * 
     * @param version1 第一个版本号
     * @param version2 第二个版本号
     * @return int 比较后的值。
     *     -1：第一个版本号 < 第二个版本号
     *      1：第一个版本号 > 第二个版本号
     *      0：第一个版本号 = 第二个版本号
     */
    static int compareVersions(const std::string& version1, const std::string& version2);

    /**
     * @brief 分割字符串
     * 
     * @param str 待分割的字符串
     * @param delimiter 分隔符，如 '/', '.', ',' 等
     * @return std::vector<std::string> 分割之后的向量 
     */
    static std::vector<std::string> splitStringrByDelimiter(const std::string& str, char delimiter) ;

    /**
     * @brief 验证版本号是否符合 Semver 规范
     * 
     * @param version 版本号
     * @return true 符合 Semver 规范
     * @return false 不符合 Semver 规范
     */
    static bool isValidSemver(const std::string& version);

    /**
     * @brief 解析 id 的类型
     * 
     * @param id id类型，如 org.kde.kclock, dev.kylin.runtime, dev.kylin.base
     * @return std::string id中的type类型
     */
    static std::string parseType(const std::string& id);

    /**
     * @brief 当前 ref 是否是离线包
     * 
     * @param ref 判断的 ref
     * @return true  是离线包
     * @return false 不是离线包
     */
    static bool isOfflinePackageSourceType(const std::string& ref);

    /**
     * @brief 获取包来源类型
     * 
     * @param ref 判断的 ref
     * @return KMPackageSourceType 返回来源类型为：在线包、离线包（.layer, .uab, .ok）
     */
    static KMPackageSourceType getPackageSourceType(const std::string& ref);

    /**
     * @brief 通过枚举 KMPackageSourceType 获取对应的字符串
     * 
     * @param type KMPackageSourceType 变量
     * @return std::string 获取对应的变量
     */
    static std::string packageSourceTypeToString(KMPackageSourceType type);

    /**
     * @brief 分割字符串函数，通过/分割输入字符串，将分割后的结果保存在KMRef中
     * 
     * 不同格式处理：根据分割后的向量的大小来判断字符串的格式并进行相应的解析。
     * 
     * 以下是包含的格式：
     * 
     *  - 格式${id}：仅有id部分，其他字段使用默认值。
     * 
     *  - 格式${id}/${version}：channel、module 使用默认值。
     * 
     *  - 格式${id}/${version}/${module}：channel 使用默认值。
     * 
     *  - 格式${id}/${version}/${module}/${channel}/ ：完整格式。
     * 
     *  - 格式${id}/${version}/${module}/${channel}/${arch} ：完整格式。
     * 
     * 版本验证：对version字段的格式进行验证，确保符合Semver规范。
     * 
     * @param ref 用户命令行输入的ref
     * @return KMPMRef 返回组装的 KMPMRef 结构体
     */
    static KMPMRef parseKMPMRef(const std::string &ref);

    /**
     * @brief 解析 summary.json module 模块中获取的字符串，将结果保存在 KMPMRef中
     * 
     * @param ref summary.json中获取的字符串, 如 base, runtime 中的  "stable:top.openkylin.base/2.0.0/x86_64",其中版本号大部分是缺的
     * @return KMPMRef 返回组装的 KMPMRef 结构体
     */
    static KMPMRef parseSummaryKMPMRef(const std::string &ref);

    /**
     * @brief 解析 summary.json 中获取的逐层拼接字符串，将结果保存在 KMPMRef中
     * 
     * @param ref summary.json中获取的字符串, 如 "stable/x86_64/app/top.openkylin.Music/binary/2.0.0.0"
     * @return KMPMRef 返回组装的 KMPMRef 结构体
     */
    static KMPMRef parseSummaryFuzzyKMPMRef(const std::string &ref);

    /**
     * @brief 根据 ref 构造完整的 ostree ref，并和summary.json中层次一样
     * 
     * @param pmRef 传入的 KMPMRef结构体
     * @return std::string 返回完整的 ostree ref, 和 summary.json 保持一致
     */
    static std::string dupKMPMCompletedRef(const KMPMRef &pmRef);


    /**
     * @brief 打印 KMRef 结构体
     * 
     * @param kmRef KMRef 结构体
     */
    static void printKMRef(const KMRef &kmRef);

    /**
     * @brief  打印解析前 ref 和 解析后 KMPMRef 结构体
     * 
     * @param ref 解析前 ref
     * @param kmRef  解析后 KMPMRef 结构体
     */
    static void printKMPMRef(const string &ref, const KMPMRef &kmRef);

    /**
     * @brief 根据三段式规范构造最新的Ref
     * 
     * @param name 软件包标识
     * @param channel 发布渠道
     * @param version 软件版本，4 位数字，使用.分隔，遵循语义化版本控制规范（SemVer）规范
     * @param arch 系统架构描述字符串
     * @param module  模块，为了方便软件包分发而拆分的文件集合
     * @return KMRef 返回组装的 KMRef 结构体
     */
    static KMRef constructRef(const std::string &name, const std::string &channel, const std::string &version, const std::string &arch, const std::string &module);

    static KMRef constructRef(KaimingKinds kind, const std::string &name, const std::string &arch, const std::string &branch);
    static std::string dupPref(const KMRef &ref);
    static std::string dupRef(const KMRef &ref);

    static bool isCharSafe(gunichar c);

    /**
     * @brief : 获取文件或目录中文件大小
     */ 
    static uint64_t getDirectorySize(const fs::path& dir);

    /**
     * @brief : 判断字符串前缀
     * @param : [in] src，原始字符串
     * @param : [in] prefix，字符串前缀
     * @return : true-字符串src是以prefix为前缀；false-字符串src不是以prefix为前缀
     */
    static bool startsWith(const std::string &src, const std::string &prefix);

    /**
     * @brief : 判断字符串后缀
     * @param : [in] src，原始字符串
     * @param : [in] sufix，字符串后缀
     * @return : true-字符串src是以sufix为后缀；false-字符串src不是以sufix为后缀
     */
    static bool endsWith(const std::string &src, const std::string &sufix);

    /**
     * @brief 判断字符串是否以某个子串为开始，比如"app", "runtime"
     *
     * @param str 待判断的字符串
     * @param suffix 匹配的子串
     * @return true
     * @return false
     */
    static bool startsWithForRef(const std::string &str, const std::string &prefix);

    /**
     * @brief : 判断字符串是否以某个子串为结尾，比如".Debug", ".Locale"
     * @param : [in] src，原始字符串
     * @param : [in] sufix，字符串后缀
     * @return : true-字符串src是以sufix为后缀；false-字符串src不是以sufix为后缀
     */
    static bool endsWithForRef(const std::string &src, const std::string &sufix);

    /**
     * @brief 通过 appid 获取默认的 ref 类型
     *
     * @param pref appid，类型如 org.kde.kclock
     * @return std::string 返回获取的 ref 类型: "app", "runtime"
     */
    static std::string getDefaultTypeForType(string pref);

    /**
     * @brief 截取ref 从开始到最后以 "/" 结尾的部分
     *
     * @param ref
     * @return std::string
     */
    static std::string removeLastPartWithDelimiter(const std::string &ref);

    /**
     * @brief 获取ref最后以 "/" 结尾的部分
     *
     * @param str 字符串，如运行时
     * @return std::string
     */
    static std::string getLastPartWithDelimiter(const std::string &str);

    /**
     * @brief : 递归创建目录
     * @param : [in] path, 目录
     * @param : [out] std::error_code，保存错误信息
     * @return : true-存在；false-不存在
     */
    static bool mkpath(const std::string &path, std::error_code &ec);
    static bool mkpath(const std::string &path);
    static bool mkpath(const std::string &path, unsigned int mode);

    /**
     * @brief : 目录或文件是否存在
     * @param : [in] path, 待目录或文件
     * @return : true-存在；false-不存在
     */
    static bool pathExists(const std::string &path);

    /**
     * @brief : 递归删除目录或文件
     * @param : [in] path, 待目录或文件
     * @param : [out] std::error_code，保存错误信息
     * @return : true-成功；false-失败
     */
    static bool removeAll(const std::string &path, std::error_code &ec);
    static bool removeAll(const std::string &path);

    /**
     * @brief : 目录或文件拷贝
     * @param : [in] src, 源目录或文件
     * @param : [in] dest, 目标目录或文件
     * @param : [out] std::error_code，保存错误信息
     * @return : true-成功；false-失败
     */
    static bool copyAll(const std::string &src, const std::string &dest, std::error_code &ec);
    static bool copyAll(const std::string &src, const std::string &dest);

    /**
     * @brief : 根据分隔符拆分字符串
     * @param : [in] str，源字符串
     * @param : [in] split，分隔符
     * @return: std::vector<std::string>，拆分后的字符串数组
     */
    static std::vector<std::string> splitString(const std::string &str, const std::string &split);

    /**
     * @brief : std::vector<std::string>转换成std::list<std::string>
     */
    static std::list<std::string> vectorToList(const std::vector<std::string> &vector);

    /**
     * @brief : 将字符串数组合并成一个字符串，各个元素之间用split分割
     * @param : [in] vector，待合并的字符串数组
     * @param : [in] split，分隔符
     * @return: 合并后的长字符串
     */
    static std::string join(std::vector<std::string> vector, const std::string &split);

    /**
     * @brief : 将字符串数组合并成一个字符串，各个元素之间用split分割
     * @param : [in] arr, 以NULL结尾的的字符串数组
     * @param : [in] split，分隔符
     * @return: 合并后的长字符串
     */
    static std::string join(const char **arr, const std::string &split);

    /**
     * @brief : 将字符串列表合并成一个字符串，各个元素之间用split分割
     * @param : [in] list，待合并的字符串列表
     * @param : [in] split，分隔符
     * @return: 合并后的长字符串
     */
    static std::string join(std::list<std::string> list, const std::string &split);

    /**
     * @brief : 在字符串数组v中查找是否包含字符串e
     * @param : [in] v, 字符串数组
     * @param : [in] e, 字符串
     * @return: true-包含；false-不包含
     */
    static bool contains(const std::vector<string> &v, const std::string &e);
    static bool contains(const std::string &src, const std::string &sub);

    /**
     * @brief : 将字符串str中的子串from替换为to
     * @param : [in | out] str, 原字符串
     * @param : [in] from, 需要替换的旧字符子串
     * @param : [in] to, 替换后的新子串
     * @return: 替换后的字符串引用，同str
     */
    static std::string &replace(std::string &str, const std::string &from, const std::string &to);

    /**
     * @brief : 去掉字符串两端的空白符，返回一个新的字符串
     * @param : [in] str, 原始字符串
     * @return: 去掉两端的空白符后的新字符串
     */
    static std::string strstrip(const std::string &str);

    static std::string getLibraryDir();

    /**
     * @brief : 获取路径的真实路径
     * @param : [in] path，路径
     * @return: 真实路径,软链接会替换为其连接路径
     */
    static std::string realpath(const std::string &path);

    /**
     * @brief : 将后面的std::vector中的元素追加到前面std::vector中
     * @param : [out] src，追加元素后的std::vector
     * @param : [in] dest，待追加的std::vector
     */
    static void append(std::vector<KMRef> &src, std::vector<KMRef> &&dest);

    /**
     * @brief : 创建目录软连接
     * @param : [in] target 目标目录
     * @param : [in] link 连接符号
     * @return: true-创建成功；false-创建失败
     */
    static bool createDirectorySymlink(const std::string &target, const std::string &link);
    /**
     * @brief : 创建目录软连接
     * @param : [in] target 目标目录
     * @param : [in] link 连接符号
     * @param : [out] ec 错误信息
     * @return: true-创建成功；false-创建失败
     */
    static bool createDirectorySymlink(const std::string &target, const std::string &link, std::error_code &ec);

    /**
     * @brief : 组装文件名
     * @param : [in] path, 前缀路径
     * @param : [in] name, 后续文件或目录名
     * @return: 组装后的文件路径
     */
    static std::string buildFilename(const std::string &path, const std::string &name);
    static std::string buildFilename(const std::string &path, const std::string &name1, const std::string &name2);
    static std::string buildFilename(const std::string &path, const std::string &name1, const std::string &name2, const std::string &name3);
    static std::string buildFilename(const std::string &path, const std::string &name1, const std::string &name2, const std::string &name3, const std::string &name4);

    /**
     * @brief : 规范化文件路径
     * @param : [in] path，原始文件路径
     * @return: 规范化后的文件路径
     */
    static std::string canonicalizeFilename(const std::string &path);

    static GBytes *readAllBytesByFd(int fd, std::string &errMsg);

    /**
     * @brief : 规范化目录文件描述符
     * @param : [in] fd，目录文件描述符
     */
    static int dirfdCanonicalize(int fd);

    /*
        不支持链接。适用于真正的临时存储。fd将被分配到指定的目录中.
    */
    static bool openAnonymousTmpfile(int flags, KMTmpfile &tmpfile, std::string &errMsg);
    static bool openAnonymousTmpfileFull(int flags, const std::string &dir, KMTmpfile &tmpfile, std::string &errMsg);
    static bool openTmpfileCore(int dfd, const std::string &subpath, int flags, KMTmpfile &tmpfile, std::string &errMsg);
    static bool openTmpfileLinkableAt(int dfd, const std::string &subpath, int flags, KMTmpfile &tmpfile, std::string &errMsg);

    /*
        如果memfd_create（）可用，则生成一个内容为@str的密封memfd。否则，在匿名模式下使用O_TMPFILE@tmpf，将@str写入@tmpf中，
        并将lseek（）写回起始位置。另请参阅类似的用法，例如rpm-ostree，用于运行dracut。
    */
    static bool sealedMemfdOrTmpfile(KMTmpfile &tmpfile, const std::string &name, const char *data, size_t dataLen, std::string &errMsg);

    /*
        像write（）一样，但循环直到@nbytes被写入，或者出现错误。
        出现错误时，返回-1，并设置了@errno。注：这是API对此函数以前版本的更改。
     */
    static int loopWrite(int fd, const unsigned char *buf, size_t nbytes);

    /**
     * @brief : 替换文件内容
     * @param : [in] dfd, 目录文件描述符
     * @param : [in] subpath，子路径
     * @param : [in] buf, 文件内容
     * @param : [in] size, 文件内容长度
     * @param : [in] mode, File mode; if `-1`, use `0644`
     * @param : [in] uid, 归属用户id
     * @param : [in] gid, 归属组id
     * @param : [in] flags，替换flag
     * @param : [out] errMsg，错误信息
     * @return: 操作是否成功
     */
    static bool replaceFileContentsWithPermsAt(int dfd, const char *subpath, const unsigned char *buf, size_t size, mode_t mode, uid_t uid, gid_t gid, FileReplaceFlags flags, std::string &errMsg);

    static bool linkTmpfileAt(KMTmpfile &tmpfile, LinkTmpfileReplaceMode mode, int target_dfd, const char *target, std::string &errMsg);

    /**
     * @brief : 生成临时文件名
     * @param : [in | out] tmp，临时文件名，必需如下形式的:*.XXXXXX
     * @return: 是否成功
     */
    static bool genTempName(std::string &tmp);

    /**
     * @brief 立即写文件
     * @param fileName 文件名，包含路径
     * @param content 文件内容
     * @return bool
     */
    static bool syncWriteFile(const std::string &fileName, const std::string &content);

    /**
     * @brief 读取文件内容
     * @param fileName 文件名，包含路径
     * @param content 文件内容
     * @return bool true成功 false失败
     */
    static bool readFile(const std::string &fileName, std::string &content);

    /* 有时这是/var/run，它是一个符号链接，当我们将其作为路径传递到沙箱时会引起奇怪的问题 */
    static std::string getRealXdgRuntimeDir();

    /* 获取PulseAudio用于其套接字的运行时目录。 */
    static std::string getPulseRuntimeDir();

    /*  获取PulseAudio用于其配置的目录。*/
    static std::string getPulseHome();

    /* 获取PulseAudio使用的机器ID。这是systemd/D-Bus机器ID，否则是主机名。 */
    static std::string getPulseMachineId();

    static bool argumentNeedsQuoting(const char *arg);

    /**
     * @brief : 执行外部程序
     * @param : [in] exec, 外部程序名称
     * @param : [in] args, 执行外部程序需要传入的参数
     * @return: 外部程序执行时的标准输出和标准错误信息
     */
    template<typename... Args>
    static std::string execuseCMD(const std::string &exec, Args &&...args)
    {
        bp::ipstream is;
        // bp::child c(bp::search_path(exec), std::forward<Args>(args)..., bp::std_out > is);
        bp::child c(bp::search_path(exec), std::forward<Args>(args)..., (bp::std_out & bp::std_err) > is);
        std::string result;
        std::string line;
        bool first = true;
        while (is && std::getline(is, line))
        {
            if (first)
                first = false;
            else
                result += "\n";
            result += line;
        }
        c.wait();

        return result;
    }

    /**
     * @brief : 执行外部程序
     * @param : [in] exec, 外部程序名称，不包含路径
     * @param : [in] args, 执行外部程序需要传入的参数列表
     * @param : [out] result, 外部程序执行时的标准输出和标准错误信息
     * @return: true - 成功执行；false - 出现错误
     */
    static bool spawn(const std::string &exec, const std::vector<std::string> &args, std::string &result);

    /**
     * @brief : 执行外部程序
     * @param : [in] exec, 外部程序
     * @param : [in] args, 执行外部程序需要传入的参数列表
     * @return: true - 成功执行；false - 出现错误
    */
    static bool spawn(const std::string &exec, const std::vector<std::string> &args = {});

    /**
     * @brief : 执行外部程序
     * @param : [in] exec, 外部程序名称，不包含路径
     * @param : [in] workingDirectory, 执行外部程序工作目录
     * @param : [in] args, 执行外部程序需要传入的参数列表
     * @param : [out] result, 外部程序执行时的标准输出和标准错误信息
     * @return: true - 成功执行；false - 出现错误
     */
    static bool spawn(const std::string &exec, const std::string &workingDirectory, const std::vector<std::string> &args, std::string &result);

    /**
     * @brief : 转移T*指针的所有权
     */
    template<typename T>
    static T *stealPointer(T **p)
    {
        if (p == nullptr || *p == nullptr)
            return nullptr;

        T *r = *p;
        *p = nullptr;
        return r;
    }

    /**
     * findFileInTree:
     * @brief : 在目录树中查找文件是否存在
     * @param : [in] baseDir, 查找的目标目录
     * @param : [in] filename，待查找的文件名
     * @return: true-找到; false-未找到
     */
    static bool findFileInTree(const std::string &baseDir, const std::string &filename);

    /**
     * isEmptyDirectory:
     * @brief : 判断路径是否空目录，使用前先判断是否是目录
     * @param : [in] path，路径
     * @return: true-空目录或不存在; false-不是空目录或不是目录
     */
    static bool isEmptyDirectory(const std::string &path);

    /**
     * @brief : 将字节大小转换为GB等表示的字符串
     * @param : [in] size, 字节大小
     * @return: GB/MB/KB等表示的字符串型大小
     */
    static std::string formatSize(std::uint64_t size);

    /**
     * @brief : 判断是否数字字符串
     * @return: true-表示是数字字符串；false-表示不是纯数字字符串
     */
    static bool isInteger(const char *str);

    static bool isStringEqual(std::string str1, std::string str2);

    /**
     * @brief 计算字符串中以空格为分隔符的参数个数
     *
     * @param str 待计算字符串，如 "kigo %u"
     * @return int 返回符串中以空格为分隔符的参数个数，"kigo %u" 参数个数为2
     */
    static int countArgsNumber(const std::string &str);

    /**
     * @brief 格式化desktop文件Exec字段
     *
     * @param input 输入待格式化的Exec字段，如 "kigo %u -qwindowicon kigo -qwindowtitle %c"
     * @param refId 输入的refId, 如 "org.kde.kigo"
     * @return std::string 格式化后的字段，如 "kigo --file-forwarding org.kde.kigo @@u %u @@ -qwindowicon kigo -qwindowtitle %c"
     */
    static std::string formatExecString(const std::string &input, const std::string &refId);

    /**
     * @brief 判断输入是否为 'y' 或 'Y', 或者只输入回车键
     *
     * @param input 输入的字符
     * @return true 当前是y, Y, 空格键，允许执行后续操作
     * @return false 当前非默认或y,Y, 不允许执行后续操作
     */
    static bool isYes(const char &input);

    /**
     * @brief 判断输入是否为 'n' 或 'N'
     *
     * @param input 输入的字符
     * @return true 当前是n, N
     * @return false 当前不是 n, N
     */
    static bool isNo(const char &input);
};

#endif // KMUTIL_H
