#include "KMUtil.h"

#include <sys/personality.h>
#include <sys/mman.h>
#include <sys/utsname.h>
#include <libgen.h>

#include <fstream>
#include <random>
#include <array>

#include "KMConfig.h"
#include "KMLogger.h"

static map<string, string> globalAppRefTypeMap = {
    { "org.kde.ark", "app" },        { "org.kde.elisa", "app" },    { "org.kde.filelight", "app" }, { "org.kde.haruna", "app" },   { "org.kde.isoimagewriter", "app" }, { "org.kde.itinerary", "app" },         { "org.kde.kamoso", "app" },
    { "org.kde.kapman", "app" },     { "org.kde.kcalc", "app" },    { "org.kde.kclock", "app" },    { "org.kde.kigo", "app" },     { "org.kde.kolourpaint", "app" },    { "org.kde.konsole", "app" },           { "org.kde.krdc", "app" },
    { "org.kde.kronometer", "app" }, { "org.kde.kweather", "app" }, { "org.kde.okular", "app" },    { "org.kde.palapeli", "app" }, { "top.openkylin.Clock", "app" },    { "top.openkylin.PhotoViewer", "app" },
};

static map<string, string> globalRuntimeRefTypeMap = {
    { "top.openkylin.Platform", "runtime" },
    { "top.openkylin.Sdk", "runtime" },
    { "org.kde.Sdk.Docs", "runtime" },
    { "org.freedesktop.Platform.Compat.i386", "runtime" },
    { "org.freedesktop.Platform.VAAPI.Intel", "runtime" },
    { "org.freedesktop.Platform", "runtime" },
    { "org.gtk.Gtk3theme.Yaru", "runtime" },
    { "top.openkylin.Sdk.Doc", "runtime" },
    { "top.wine.Platform", "runtime" },
    { "top.wine.Sdk.Docs", "runtime" },
    { "top.wine.Sdk.gecko", "runtime" },
    { "top.wine.Sdk.mono", "runtime" },
    { "top.wine.Sdk", "runtime" },
};

KMTmpfile::KMTmpfile() { }

KMTmpfile::~KMTmpfile()
{
    if (!path.empty() && srcDfd > 0)
    {
        ::unlinkat(srcDfd, path.c_str(), 0);
    }
}

KMUtil::KMUtil() { }

#if !defined(__i386__) && !defined(__x86_64__) && !defined(__aarch64__) && !defined(__arm__)
static string get_kernel_arch()
{
    static struct utsname buf;
    static char *arch = nullptr;

    if (arch != nullptr)
        return arch;

    if (uname(&buf))
    {
        arch = "unknown";
        return arch;
    }

    arch = buf.machine;

    /* Override for some arches */
    string mstr = buf.machine;
    if (mstr.length() == 4 && mstr.at(0) == 'i' && mstr.at(2) == '8' && mstr.at(3) == '6')
    {
        arch = "i386";
    }
    else if (mstr == "mips")
    {
#  if G_BYTE_ORDER == G_LITTLE_ENDIAN
        arch = "mipsel";
#  endif
    }
    else if (mstr == "mips64")
    {
#  if G_BYTE_ORDER == G_LITTLE_ENDIAN
        arch = "mips64el";
#  endif
    }

    return arch;
}
#endif

const string KMUtil::kmGetArch()
{
#if defined(__i386__)
    return "i386";
#elif defined(__x86_64__)
    return "x86_64";
#elif defined(__lonngarch64__)
    return "loongarch64";
#elif defined(__aarch64__)
    return "arm64";
#elif defined(__arm__)
#  if G_BYTE_ORDER == G_LITTLE_ENDIAN
    return "arm";
#  else
    return "armeb";
#  endif
#else
    return get_kernel_arch();
#endif
}

const string KMUtil::kmGetDefaultModule()
{
    return string("binary");
}

bool KMUtil::isLinux32Arch(const std::string &arch)
{
    std::string kernelArch = KMUtil::getKernelArch();

    if (kernelArch == "x86_64" && arch == "i386")
    {
        return true;
    }

    if (kernelArch == "aarch64" && arch == "arm")
    {
        return true;
    }

    return false;
}

int KMUtil::getFileTimestamp(const string &filePath)
{
    struct stat fileInfo;
    if (stat(filePath.c_str(), &fileInfo) == 0)
    {
        return fileInfo.st_mtime;
    }
    else
    {
        return 0;
    }
}

bool KMUtil::isFileExpired(const string &filePath, int expiredTime)
{
    int currentTime = time(nullptr);
    int fileTimestamp = getFileTimestamp(filePath);

    bool isExpired = currentTime - fileTimestamp > expiredTime;

    return isExpired;
}

bool KMUtil::isJsonFile(const string &filePath)
{
    std::ifstream inputFile(filePath);

    if (inputFile.is_open())
    {
        std::string jsonContent((std::istreambuf_iterator<char>(inputFile)), std::istreambuf_iterator<char>());
        std::regex commentPattern(R"(\/\/[^\n]*|\/\*[\s\S]*?\*\/)");
        std::string cleanedJson = std::regex_replace(jsonContent, commentPattern, "");

        inputFile.close();

        // 使用 nlohmann::json 解析
        try
        {
            json jsonData;
            jsonData = json::parse(cleanedJson);
            return true;
        }
        catch (const std::exception &e)
        {
            kmlogger.error("Failed to parse json file: %s", e.what());
            return false;
        }
    }
    else
    {
        return false;
    }
}   

void KMUtil::removeTrailingNewline(std::string &str)
{
    // 如果字符串不为空，且最后一个字符为 '\n'，则移除它
    if (!str.empty() && str.back() == '\n') {
        str.pop_back();
    }
}

bool KMUtil::isZero(double value, double tolerance)
{
    return std::abs(value) < tolerance;
}

std::string KMUtil::formatInstalledSize(double size)
{
    vector<string> units = { "B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };
    int index = 0;

    while (size >= 1024.0 && index < units.size())
    {
        size /= 1024.0;
        index++;
    }

    std::ostringstream oss;
    oss << std::fixed << std::setprecision(1) << size << " " << units[index];
    return oss.str();
}

std::string KMUtil::getCurrentLocale()
{
    // 获取全局的语言环境
    std::locale currentLocale = std::locale();
    
    // 获取当前语言环境的名称
    std::string localeName = currentLocale.name();

    // 查找'.'的位置
    std::size_t pos = localeName.find('.');
    
    // 如果找到了'.'，则返回第一个部分，否则返回整个字符串
    if (pos != std::string::npos) {
        return localeName.substr(0, pos);
    } else {
        return localeName;
    }
    
}

std::string KMUtil::getCurrentTime()
{
    time_t now = time(nullptr);
    char buf[80];

    strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", localtime(&now));

    return std::string(buf);
}

bool KMUtil::createFileIfNotExist(const string &fileName)
{
    std::ofstream outFile(fileName, ios::trunc); // 以 trunc 模式打开文件，文件存在时会清空
    if (outFile.is_open())
    {
        outFile << "Install time: " << getCurrentTime() << endl;
        outFile.close();
        kmlogger.info("File created/cleared and time written.");
        return true;
    }
    else
    {
        cerr << "Failed to create or open the file." << endl;
        return false;
    }
}

void KMUtil::removeDuplicateValues(vector<string> &refVector)
{
    // set 是一个自动排序容器,只存储唯一值,可以达到去重效果

    set<string> refSet(refVector.begin(), refVector.end());

    refVector.assign(refSet.begin(), refSet.end());
}

bool KMUtil::fileExists(const string &fileName)
{
    struct stat buffer;
    return (stat(fileName.c_str(), &buffer) == 0);
}

bool KMUtil::isValidVersion(const std::string &version)
{
    // 正则表达式匹配格式：主版本.次版本.修订版本.扩充安全更新版本 (数字格式)

    std::regex versionPattern(R"(^\d+\.\d+\.\d+\.\d+$)");

    return std::regex_match(version, versionPattern);
}

std::vector<int> KMUtil::splitVersion(const std::string &version)
{
    std::vector<int> versionNumbers;
    std::string::size_type start = 0;
    std::string::size_type end = version.find('.');

    while (end != std::string::npos)
    {
        versionNumbers.push_back(std::stoi(version.substr(start, end - start)));
        start = end + 1;
        end = version.find('.', start);
    }

    // 将最后一部分数字加入数组
    versionNumbers.push_back(std::stoi(version.substr(start)));

    return versionNumbers;
}

int KMUtil::compareVersions(const std::string &version1, const std::string &version2)
{
    if (!isValidVersion(version1) || !isValidVersion(version2))
    {
        throw std::invalid_argument("Invalid version format");
    }

    std::vector<int> v1 = splitVersion(version1);
    std::vector<int> v2 = splitVersion(version2);

    // 逐段比较版本号
    for (size_t i = 0; i < v1.size(); ++i)
    {
        if (v1[i] < v2[i])
        {
            return -1; // version1 < version2
        }

        if (v1[i] > v2[i])
        {
            return 1; // version1 > version2
        }
    }

    return 0; // version1 == version2
}

std::vector<std::string> KMUtil::splitStringrByDelimiter(const std::string &str, char delimiter)
{
    std::vector<std::string> tokens;
    std::string token;
    std::istringstream tokenStream(str);
    while (std::getline(tokenStream, token, delimiter))
    {
        tokens.push_back(token);
    }
    return tokens;
}

bool KMUtil::isValidSemver(const std::string &version)
{
    if (version.empty())
    {
        return false;
    }

    // 使用正则表达式验证 Semver 格式
    const std::regex semverRegex("^\\d+\\.\\d+\\.\\d+\\.\\d+(-[a-zA-Z0-9]+(\\.[a-zA-Z0-9]+)*)?$");
    return std::regex_match(version, semverRegex);
}

std::string KMUtil::parseType(const std::string &id)
{
    auto idParts = splitStringrByDelimiter(id, '.');
    if (idParts.size() > 2)
    {
        const std::string &lastPart = idParts.back();
        if (lastPart == "runtime" || lastPart == "platform" || lastPart == "Platform")
        {
            return BASE_TYPE_RUNTIME;
        }
        else if (lastPart == "base")
        {
            return BASE_TYPE_BASE;
        }
    }
    return BASE_TYPE_APP; // 默认为 BASE_TYPE_APP
}

bool KMUtil::isOfflinePackageSourceType(const std::string &ref)
{
    if (endsWithForRef(ref, ".layer") || endsWithForRef(ref, ".uab") || endsWithForRef(ref, ".ok"))
    {
        return true;
    }
    return false;
}

KMPackageSourceType KMUtil::getPackageSourceType(const std::string &ref)
{
    if (endsWithForRef(ref, ".layer"))
    {
        return KMPACKAGE_SOURCE_TYPE_LAYER;
    }

    if (endsWithForRef(ref, ".uab"))
    {
        return KMPACKAGE_SOURCE_TYPE_LAYER;
    }

    if (endsWithForRef(ref, ".ok"))
    {
        return KMPACKAGE_SOURCE_TYPE_LAYER;
    }

    return KMPACKAGE_SOURCE_TYPE_ONLINE;
}

std::string KMUtil::packageSourceTypeToString(KMPackageSourceType type)
{
    if (packageSourceTypeWithString.find(type) != packageSourceTypeWithString.end())
    {
        return packageSourceTypeWithString[type];
    }
    else
    {
        return "Unknown Type";
    }
}

std::string KMUtil::getKernelArch()
{
    static struct utsname buf;
    static const char *arch = NULL;
    char *m;

    if (arch != NULL)
    {
        return arch;
    }

    if (uname(&buf))
    {
        arch = "unknown";
        return arch;
    }

    /* By default, just pass on machine, good enough for most arches */
    arch = buf.machine;

    /* Override for some arches */

    m = buf.machine;
    /* i?86 */
    if (strlen(m) == 4 && m[0] == 'i' && m[2] == '8' && m[3] == '6')
    {
        arch = "i386";
    }
    else if (g_str_has_prefix(m, "arm"))
    {
        if (g_str_has_suffix(m, "b"))
        {
            arch = "armeb";
        }
        else
        {
            arch = "arm";
        }
    }
    else if (strcmp(m, "mips") == 0)
    {
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
        arch = "mipsel";
#endif
    }
    else if (strcmp(m, "mips64") == 0)
    {
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
        arch = "mips64el";
#endif
    }

    return arch;
}

const string KMUtil::kmGetDefaultChannel()
{
    // 先返回这个
    return "stable";
}

bool KMUtil::kmStrIsInteger(const char *str)
{
    if (str == NULL || *str == '\0')
    {
        return false;
    }

    for (; *str != '\0'; str++)
    {
        if (!g_ascii_isdigit(*str))
        {
            return false;
        }
    }

    return true;
}

bool KMUtil::isCharSafe(gunichar c)
{
    return g_unichar_isgraph(c) || c == ' ';
}

bool KMUtil::kmValidatePathCharacters(const char *path)
{
    while (*path)
    {
        gunichar c = g_utf8_get_char_validated(path, -1);
        if (c == (gunichar)-1 || c == (gunichar)-2)
        {
            return false;
        }
        else if (!isCharSafe(c))
        {
            return false;
        }

        path = g_utf8_find_next_char(path, NULL);
    }

    return true;
}

KMRef KMUtil::kmString2Ref(string &str)
{
    KMRef ref;
    int index1 = str.find('/', 0);
    int index2 = str.find('/', index1 + 1);

    if (index1 <= 0 || index2 <= 0)
    {
        return KMRef();
    }

    ref.id = str.substr(0, index1);
    ref.arch = str.substr(index1 + 1, index2 - index1 - 1);
    ref.branch = str.substr(index2 + 1, str.length() - index2 - 1);

    return ref;
}

std::map<std::string, std::string> KMUtil::kmGetMinimalEnv(bool devel, bool useLdSoCache)
{
    std::map<std::string, std::string> env;
    for (auto &pair : globalEnvDefault)
    {
        if (!pair.second.empty())
        {
            env.insert(pair);
        }
    }

    if (!useLdSoCache)
    {
        env["LD_LIBRARY_PATH"] = "/app/lib";
    }

    if (devel)
    {
        for (auto &pair : globalEnvDevel)
        {
            if (!pair.second.empty())
            {
                env.insert(pair);
            }
        }
    }

    for (auto &envKey : globalEnvCopy)
    {
        const char *envVal = getenv(envKey.c_str());
        if (envVal)
        {
            env.insert(pair<string, string>(envKey, string(envVal)));
        }
    }

    if (!devel)
    {
        for (auto &envKey : globalEnvCopyNoDevel)
        {
            const char *envVal = getenv(envKey.c_str());
            if (envVal)
            {
                env.insert(pair<string, string>(envKey, string(envVal)));
            }
        }
    }
    return env;
}

int KMUtil::kmRandom(int rangeStart, int rangeEnd)
{
    random_device r;
    default_random_engine e(r());
    uniform_int_distribution<int> uniformDist(rangeStart, rangeEnd);
    return uniformDist(e);
}

string KMUtil::kmGenerateTempName(const string &tempName)
{
    string res = tempName;
    string endStr = "";
    string charAll = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

    random_device r;
    default_random_engine e(r());
    uniform_int_distribution<int> uniformDist(0, charAll.length() - 1);

    for (int i = 0; i < 6; i++)
    {
        endStr += charAll.at(uniformDist(e));
    }

    res.replace(res.length() - 6, 6, endStr);

    return res;
}

list<string> KMUtil::split(string str, char c)
{
    list<string> retList;

    int indexStart = 0;
    int indexEnd = -1;
    do
    {
        indexEnd = str.find(c, indexStart);

        if (indexEnd < 0)
        {
            retList.push_back(str.substr(indexStart, str.length() - indexStart));
            break;
        }

        // 针对  "20.03;askkd;qwecc;"
        if (indexEnd == str.length() - 1)
        {
            retList.push_back(str.substr(indexStart, str.length() - indexStart - 1));
            break;
        }

        retList.push_back(str.substr(indexStart, indexEnd - indexStart));
        indexStart = indexEnd + 1;
    } while (true);

    return retList;
}

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

    int indexStart = 0;
    int indexEnd = -1;
    do
    {
        indexEnd = str.find(split, indexStart);

        if (indexEnd == std::string::npos)
        {
            values.push_back(str.substr(indexStart, str.length() - indexStart));
            break;
        }
        else
        {
            values.push_back(str.substr(indexStart, indexEnd - indexStart));
        }

        indexStart = indexEnd + split.length();
    } while (indexStart < str.length() - 1);

    return values;
}

const string KMUtil::getKMBox()
{
    const char *e = g_getenv("KAIMING_BOX");

    if (e != NULL)
    {
        return e;
    }
    return KAIMING_BOX;
}

const string KMUtil::kmGetDBusProxy()
{
    const char *e = g_getenv("KAIMING_DBUSPROXY");

    if (e != NULL)
    {
        return e;
    }
    return DBUSPROXY;
}

const string KMUtil::kmGetTimeZone()
{
    std::string localtime("/etc/localtime");
    /* Resolve relative path */
    std::string canonical = KMUtil::realpath(localtime);
    if (!canonical.empty())
    {
        /* Strip the prefix and slashes if possible. */
        const char *tzdir = getenv("TZDIR");
        if (tzdir != nullptr && KMUtil::startsWith(canonical, tzdir))
        {
            std::string canonicalSuffix = canonical.substr(strlen(tzdir));
            while (canonicalSuffix.at(0) == '/')
            {
                canonicalSuffix = canonicalSuffix.substr(1);
            }

            return canonicalSuffix;
        }

        std::string defaultTzpath("/usr/share/zoneinfo");
        if (KMUtil::startsWith(canonical, defaultTzpath))
        {
            std::string canonicalSuffix = canonical.substr(defaultTzpath.length());
            while (canonicalSuffix.at(0) == '/')
            {
                canonicalSuffix = canonicalSuffix.substr(1);
            }

            return canonicalSuffix;
        }
    }

    if (fs::exists("/etc/timezone"))
    {
        std::fstream fileStream("/etc/timezone");
        if (fileStream.is_open())
        {
            std::string ret;
            std::getline(fileStream, ret);
            return ret;
        }
    }

    return std::string("UTC");
}

KMPMRef KMUtil::parseKMPMRef(const std::string &ref)
{
    KMPMRef kmRef;
    std::vector<std::string> tokens;
    std::stringstream ss(ref); 
    std::string token;

    KMPackageSourceType packageSourceType = getPackageSourceType(ref);

    kmRef.refSourceType = packageSourceType;

    // 按照斜杠分割字符串
    while (std::getline(ss, token, '/'))
    {
        tokens.push_back(token);
    }

    // 根据tokens的数量判断引用字符串格式
    if (tokens.size() >= 1)
    {
        kmRef.ref.id = tokens[0];
        // kmRef.ref.kind = parseType(kmRef.ref.id); // 此处不能根据 id 后缀判断类型
    }
    else
    {
        throw std::invalid_argument("Invalid reference format: id is required.");
    }

    if (tokens.size() == 2)
    {
        // ${id}/${version} 格式
        kmRef.ref.version = tokens[1];
    }
    else if (tokens.size() == 3)
    {
        // ${id}/${version}/${module} 格式
        kmRef.ref.version = tokens[1];
        kmRef.ref.module = tokens[2];
    }
    else if (tokens.size() == 4)
    {
        // ${id}/${version}/${module}/${channel}/ 格式
        kmRef.ref.version = tokens[1];
        kmRef.ref.module = tokens[2];
        kmRef.ref.channel = tokens[3];
    }
    else if (tokens.size() == 5)
    {
        // ${id}/${version}/${module}/${channel}/${arch} 格式
        kmRef.ref.version = tokens[1];
        kmRef.ref.module = tokens[2];
        kmRef.ref.channel = tokens[3];
        kmRef.ref.arch = tokens[4];
    }

    if (kmRef.ref.arch.empty())
    {
        kmRef.ref.arch = kmGetArch();
    }

    // 如果需要对version进行Semver规范校验，可以用正则表达式
    if (!kmRef.ref.version.empty() && !isValidSemver(kmRef.ref.version))
    {
        throw std::invalid_argument("Invalid version format: version does not follow Semver specification.");
    }

    return kmRef;
}

KMPMRef KMUtil::parseSummaryKMPMRef(const std::string &ref)
{
    KMPMRef kmRef;
    std::vector<std::string> tokens;
    std::stringstream ss(ref); // "stable:top.openkylin.base/2.0.0/x86_64",
    std::string token;

    KMPackageSourceType packageSourceType = getPackageSourceType(ref);

    kmRef.refSourceType = packageSourceType;

    // 按照 '/' 分割字符串
    while (std::getline(ss, token, '/'))
    {
        tokens.push_back(token);
    }

    // 根据tokens的数量判断引用字符串格式
    if (tokens.size() >= 1)
    {
        // 按照  ':' 分割字符串
        std::stringstream ssChannel(tokens[0]);
        std::string tokenChannel;
        std::vector<std::string> tokensChannel;
        while (std::getline(ssChannel, tokenChannel, ':'))
        {
            tokensChannel.push_back(tokenChannel);
        }

        if (tokensChannel.size() == 2)
        {
            kmRef.ref.channel = tokensChannel[0];
            kmRef.ref.id = tokensChannel[1];
        }
        else
        {
            throw std::invalid_argument("Invalid reference format: channel:id is required.");
        }

    }
    else
    {
        throw std::invalid_argument("Invalid reference format: id is required.");
    }

    if (tokens.size() == 2)
    {
        // stable:top.openkylin.base/2.0.0
        kmRef.ref.version = tokens[1];
    }
    else if (tokens.size() == 3)
    {
        // stable:top.openkylin.base/2.0.0/x86_64
        kmRef.ref.version = tokens[1];
        kmRef.ref.arch = tokens[2];
    }

    return kmRef;
}

KMPMRef KMUtil::parseSummaryFuzzyKMPMRef(const std::string &ref)
{
    KMPMRef kmRef;
    std::vector<std::string> tokens;
    std::stringstream ss(ref); // "stable/x86_64/app/top.openkylin.Music/binary/2.0.0.0"
    std::string token;

    KMPackageSourceType packageSourceType = getPackageSourceType(ref);

    kmRef.refSourceType = packageSourceType;

    // 按照斜杠分割字符串
    while (std::getline(ss, token, '/'))
    {
        tokens.push_back(token);
    }

    // 根据tokens的数量判断引用字符串格式
    if (tokens.size() == 6)
    {
        kmRef.ref.channel = tokens[0];
        kmRef.ref.arch = tokens[1];
        kmRef.ref.kind = tokens[2];
        kmRef.ref.id = tokens[3];
        kmRef.ref.module = tokens[4];
        kmRef.ref.version = tokens[5];
    }
    else
    {
        throw std::invalid_argument("Invalid reference format: id is required.");
    }

    return kmRef;
}

std::string KMUtil::dupKMPMCompletedRef(const KMPMRef &pmRef)
{
    string completedRef;

    completedRef.append(pmRef.ref.channel)
        .append("/")
        .append(pmRef.ref.arch)
        .append("/")
        .append(pmRef.ref.kind)
        .append("/")
        .append(pmRef.ref.id)
        .append("/")
        .append(pmRef.ref.module)
        .append("/")
        .append(pmRef.ref.version);
    
    return completedRef;
}

void KMUtil::printKMRef(const KMRef &kmRef)
{
    kmlogger.info("<KMRefInfo>: Id:%s channel:%s version:%s arch:%s module:%s", kmRef.id.c_str(), kmRef.channel.c_str(), kmRef.version.c_str(), kmRef.arch.c_str(), kmRef.module.c_str());
}

void KMUtil::printKMPMRef(const string &ref, const KMPMRef &kmRef)
{
    kmlogger.info("<KMPMRefInfo>: Id:%s channel:%s version:%s arch:%s module:%s sourceType:%s", kmRef.ref.id.c_str(), kmRef.ref.channel.c_str(), kmRef.ref.version.c_str(), kmRef.ref.arch.c_str(),
                  kmRef.ref.module.c_str(), packageSourceTypeToString(kmRef.refSourceType).c_str());
}

KMRef KMUtil::constructRef(const std::string &name, const std::string &channel, const std::string &version, const std::string &arch, const std::string &module)
{
    KMRef ref;

    ref.id = name;

    if (channel.empty())
    {
        ref.channel = KMUtil::kmGetDefaultChannel();
        ref.branch = ref.channel;
    }
    else
    {
        ref.branch = channel;
    }

    ref.version = version;
    
    if (arch.empty())
    {
        ref.arch = KMUtil::kmGetArch();
    }
    else
    {
        ref.arch = arch;
    }

    if (module.empty())
    {
        ref.module = KMUtil::kmGetDefaultModule();
    }
    else
    {
        ref.module = module;
    }

    return ref;
}

KMRef KMUtil::constructRef(KaimingKinds kind, const std::string &name, const std::string &arch, const std::string &branch)
{
    KMRef ref;

    if (KaimingKinds::KAIMING_KINDS_APP == kind)
    {
        ref.kind = BASE_TYPE_APP;
    }
    else
    {
        ref.kind = BASE_TYPE_RUNTIME;
    }

    if (arch.empty())
    {
        ref.arch = KMUtil::kmGetArch();
    }
    else
    {
        ref.arch = arch;
    }

    if (branch.empty())
    {
        ref.branch = KMUtil::kmGetDefaultChannel();
    }
    else
    {
        ref.branch = branch;
    }

    ref.id = name;

    return ref;
}

std::string KMUtil::dupPref(const KMRef &ref)
{
    if (ref.id.empty())
    {
        return "";
    }

    return ref.id + "/" + ref.arch + "/" + ref.branch;
}

std::string KMUtil::dupRef(const KMRef &ref)
{
    if (ref.id.empty())
    {
        return "";
    }

    return ref.kind + "/" + ref.id + "/" + ref.arch + "/" + ref.branch;
}

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

/**
 * @brief : 判断字符串后缀
 * @param : [in] src，原始字符串
 * @param : [in] sufix，字符串后缀
 * @return : true-字符串src是以sufix为后缀；false-字符串src不是以sufix为后缀
 */
bool KMUtil::endsWith(const std::string &src, const std::string &sufix)
{
    size_t pos = src.rfind(sufix);
    if (pos == std::string::npos)
    {
        return false;
    }

    size_t sufixLen = sufix.length();
    size_t srcLen = src.length();

    return pos + sufix.length() == src.length();
}

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

    std::string substring = str.substr(0, prefix.length());

    // 判断截取的部分是否与前缀相等
    return substring == prefix;
}

/**
 * @brief 判断字符串是否以某个子串为结尾，比如".Debug", ".Locale"
 *
 * @param str 待判断的字符串
 * @param suffix 匹配的子串
 * @return true
 * @return false
 */
bool KMUtil::endsWithForRef(const std::string &str, const std::string &suffix)
{
    if (str.length() < suffix.length())
    {
        return false;
    }
    return str.compare(str.length() - suffix.length(), suffix.length(), suffix) == 0;
}

/**
 * @brief 通过 appid 获取默认的 ref 类型
 *
 * @param pref appid，类型如 org.kde.kclock
 * @return std::string 返回获取的 ref 类型: "app", "runtime"
 */
std::string KMUtil::getDefaultTypeForType(string pref)
{
    kmlogger.info("pref:  %s", pref.c_str());
    for (const auto &ref : globalAppRefTypeMap)
    {
        if (ref.first == pref)
        {
            kmlogger.debug("app ref => first:%s second:%s", ref.first.c_str(), ref.second.c_str());
            return ref.second;
        }
    }

    for (const auto &ref : globalRuntimeRefTypeMap)
    {
        if (ref.first == pref || pref.find(ref.first) != std::string::npos)
        {
            kmlogger.debug("runtime ref => first:%s second:%s", ref.first.c_str(), ref.second.c_str());
            return ref.second;
        }
    }

    if (endsWithForRef(pref, ".Debug") || endsWithForRef(pref, ".Locale") || endsWithForRef(pref, ".Sdk") || endsWithForRef(pref, ".Platform") || endsWithForRef(pref, ".default") || endsWithForRef(pref, ".Sources"))
    {
        kmlogger.debug("runtime ref endsWith .Debug, .Locale, .Sdk, .Platform, .Sources");
        return "runtime";
    }

    kmlogger.info("Use default type: app");
    return "app";
}

/**
 * @brief 截取ref 从开始到最后以 "/" 结尾的部分
 *
 * @param ref
 * @return std::string
 */
std::string KMUtil::removeLastPartWithDelimiter(const std::string &ref)
{
    size_t pos = ref.find_last_of('/');
    if (pos != std::string::npos)
    {
        return ref.substr(0, pos + 1);
    }
    else
    {
        // 如果没有找到 "/", 返回原始字符串
        return ref;
    }
}

/**
 * @brief 获取ref最后以 "/" 结尾的部分
 *
 * @param str 字符串，如运行时
 * @return std::string
 */
std::string KMUtil::getLastPartWithDelimiter(const std::string &str)
{
    // 查找最后一个 "/" 的位置
    size_t lastSlashPos = str.find_last_of('/');
    if (lastSlashPos != std::string::npos)
    {
        // 如果找到了 "/"，则返回其后面的部分
        return str.substr(lastSlashPos + 1);
    }
    else
    {
        // 如果没有找到 "/"，则返回空字符串
        return "stable";
    }
}

/**
 * @brief : 递归创建目录
 * @param : [in] path, 目录
 * @param : [out] std::error_code，保存错误信息
 * @return : true-存在；false-不存在
 */
bool KMUtil::mkpath(const std::string &path, std::error_code &ec)
{
    bool result = fs::create_directories(path, ec);
    if (!result && 0 == ec.value())
    {
        return true;
    }
    return result;
}

/**
 * @brief : 递归创建目录，如果目录已经存在则不报告错误
 * @param : [in] path, 目录
 * @return : true-存在；false-不存在
 */
bool KMUtil::mkpath(const std::string &path)
{
    std::error_code ec;
    return KMUtil::mkpath(path, ec);
}

/**
 * @brief : 递归创建目录，如果目录已经存在则不报告错误
 * @param : [in] path, 目录
 * @param : [in] mode，路径权限，可八进制写法，如：0755
 * @return : true-存在；false-不存在
 */
bool KMUtil::mkpath(const std::string &path, unsigned int mode)
{
    std::error_code ec;
    if (KMUtil::mkpath(path, ec))
    {
        fs::permissions(path, (fs::perms)mode);
        return true;
    }

    return false;
}

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

/**
 * @brief : 递归删除目录或文件
 * @param : [in] path, 待目录或文件
 * @param : [out] std::error_code，保存错误信息
 * @return : true-成功；false-失败
 */
bool KMUtil::removeAll(const std::string &path, std::error_code &ec)
{
    bool result = fs::remove_all(path, ec);
    if (!result && 0 == ec.value())
    {
        return true;
    }
    return result;
}

/**
 * @brief : 递归删除目录或文件
 * @param : [in] path, 待目录或文件
 * @return : true-成功；false-失败
 */
bool KMUtil::removeAll(const std::string &path)
{
    std::error_code ec;
    return KMUtil::removeAll(path, ec);
}

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

    if (fs::is_directory(src) || KMUtil::endsWith(src, "/") || fs::is_directory(dest) || KMUtil::endsWith(dest, "/"))
    {
        bool rst = KMUtil::mkpath(dest, ec);
        if (!rst)
        {
            return false;
        }
    }
    else
    {
        fs::path p(dest);
        bool rst = KMUtil::mkpath(p.parent_path().string(), ec);
        if (!rst)
        {
            return false;
        }
    }

    const auto copyOptions = fs::copy_options::update_existing | fs::copy_options::recursive;
    fs::copy(src, dest, copyOptions, ec);
    if (0 == ec.value())
    {
        return true;
    }

    return false;
}

/**
 * @brief : 目录或文件拷贝
 * @param : [in] src, 源目录或文件
 * @param : [in] dest, 目标目录或文件
 * @return : true-成功；false-失败
 */
bool KMUtil::copyAll(const std::string &src, const std::string &dest)
{
    std::error_code ec;
    return KMUtil::copyAll(src, dest, ec);
}

/**
 * @brief : std::vector<std::string>转换成std::list<std::string>
 */
std::list<std::string> KMUtil::vectorToList(const std::vector<std::string> &vector)
{
    return std::list<std::string>(vector.cbegin(), vector.cend());
}

/**
 * @brief : 将字符串数组合并成一个字符串，各个元素之间用split分割
 * @param : [in] vector，待合并的字符串数组
 * @param : [in] split，分隔符
 * @return: 合并后的长字符串
 */
std::string KMUtil::join(std::vector<std::string> vector, const std::string &split)
{
    std::string value;
    for (auto &e : vector)
    {
        if (!value.empty())
        {
            value += split;
        }
        value += e;
    }

    return value;
}

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

    for (; *arr != nullptr; arr++)
    {
        if (!value.empty())
        {
            value += split;
        }
        value += *arr;
    }

    return value;
}

/**
 * @brief : 将字符串列表合并成一个字符串，各个元素之间用split分割
 * @param : [in] list，待合并的字符串列表
 * @param : [in] split，分隔符
 * @return: 合并后的长字符串
 */
std::string KMUtil::join(std::list<std::string> list, const std::string &split)
{
    std::string value;
    for (auto &e : list)
    {
        if (!value.empty())
        {
            value += split;
        }
        value += e;
    }

    return value;
}

/**
 * @brief : 在字符串数组v中查找是否包含字符串e
 * @param : [in] v, 字符串数组
 * @param : [in] e, 字符串
 * @return: true-包含；false-不包含
 */
bool KMUtil::contains(const std::vector<string> &v, const std::string &e)
{
    for (auto &element : v)
    {
        if (e == element)
        {
            return true;
        }
    }

    return false;
}

bool KMUtil::contains(const std::string &src, const std::string &sub)
{
    return std::string::npos != src.find(sub);
}

/**
 * @brief : 将字符串str中的子串from替换为to
 * @param : [in | out] str, 原字符串
 * @param : [in] from, 需要替换的旧字符子串
 * @param : [in] to, 替换后的新子串
 * @return: 替换后的字符串引用，同str
 */
std::string &KMUtil::replace(std::string &str, const std::string &from, const std::string &to)
{
    // 替换所有出现的from为to
    size_t startPos = 0;
    while ((startPos = str.find(from, startPos)) != std::string::npos)
    {
        str.replace(startPos, from.length(), to);
        startPos += to.length(); // 防止无限循环
    }

    return str;
}

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

    size_t startPos = 0;
    size_t len = str.length();

    for (; startPos < len; ++startPos)
    {
        if (!std::isspace(str.at(startPos)))
        {
            break;
        }
    }

    if (startPos == len)
    {
        return "";
    }

    size_t endPos = len - 1;
    for (; endPos > startPos; --endPos)
    {
        if (!std::isspace(endPos))
        {
            break;
        }
    }

    return str.substr(startPos, endPos - startPos + 1);
}

// 返回对应架构的库目录
std::string KMUtil::getLibraryDir()
{
#if defined(__x86_64__)
    return "x86_64-linux-gnu";
#elif defined(__i386__)
    return "i386-linux-gnu";
#elif defined(__arm__) && defined(__ARMEL__)
    return "arm-linux-gnueabi";
#elif defined(__arm__) && defined(__ARMHF__)
    return "arm-linux-gnueabihf";
#elif defined(__aarch64__)
    return "aarch64-linux-gnu";
#elif defined(__powerpc64__) && defined(__LITTLE_ENDIAN__)
    return "powerpc64le-linux-gnu";
#elif defined(__powerpc64__) && !defined(__LITTLE_ENDIAN__)
    return "powerpc64-linux-gnu";
#else
    return kmGetArch() + "-linux-gnu";
#endif
}

/**
 * @brief : 获取路径的真实路径
 * @param : [in] inpath，路径
 * @return: 真实路径,软链接会替换为其连接路径
 */
std::string KMUtil::realpath(const std::string &inpath)
{
    if (inpath.empty())
    {
        return "";
    }

    std::string path = KMUtil::canonicalizeFilename(inpath);

    fs::path filepath(path);
    if (!fs::exists(filepath))
    {
        return "";
    }

    if (fs::is_symlink(filepath))
    {
        fs::path link = fs::read_symlink(filepath);
        fs::path targetPath = link;
        if (!link.is_absolute())
        {
            std::string target = link.string();
            targetPath = filepath.parent_path() / target;
        }
        if (!fs::exists(targetPath))
        {
            return "";
        }

        return targetPath.string();
    }

    return filepath.string();
}

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

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

/**
 * @brief : 创建目录软连接
 * @param : [in] target 目标目录
 * @param : [in] link 连接符号
 * @param : [out] ec 错误信息
 * @return: true-创建成功；false-创建失败
 */
bool KMUtil::createDirectorySymlink(const std::string &target, const std::string &link, std::error_code &ec)
{
    fs::create_directory_symlink(target, link, ec);
    if (0 == ec.value())
    {
        return true;
    }
    else
    {
        return false;
    }
}

/**
 * @brief : 组装文件名
 * @param : [in] path, 前缀路径
 * @param : [in] name, 后续文件或目录名
 * @return: 组装后的文件路径
 */
std::string KMUtil::buildFilename(const std::string &path, const std::string &name)
{
    std::string filename = path + "/" + name;
    KMUtil::replace(filename, "//", "/");

    return filename;
}

std::string KMUtil::buildFilename(const std::string &path, const std::string &name1, const std::string &name2)
{
    std::string filename = path + "/" + name1 + "/" + name2;
    KMUtil::replace(filename, "//", "/");

    return filename;
}

std::string KMUtil::buildFilename(const std::string &path, const std::string &name1, const std::string &name2, const std::string &name3)
{
    std::string filename = path + "/" + name1 + "/" + name2 + "/" + name3;
    KMUtil::replace(filename, "//", "/");

    return filename;
}

std::string KMUtil::buildFilename(const std::string &path, const std::string &name1, const std::string &name2, const std::string &name3, const std::string &name4)
{
    std::string filename = path + "/" + name1 + "/" + name2 + "/" + name3 + "/" + name4;
    KMUtil::replace(filename, "//", "/");

    return filename;
}

/**
 * @brief : 规范化文件路径
 * @param : [in] path，原始文件路径
 * @return: 规范化后的文件路径
 */
std::string KMUtil::canonicalizeFilename(const std::string &path)
{
    // g_autoptr(GFile) file = g_file_new_for_path(path.c_str());
    // return g_file_get_path(file);
    char *canonicalize = canonicalize_file_name(path.c_str());
    std::string ret;
    if (canonicalize != nullptr)
    {
        ret = canonicalize;
        ::free(canonicalize);
    }

    return ret;
}

GBytes *KMUtil::readAllBytesByFd(int fd, std::string &errMsg)
{
    g_autoptr(GMappedFile) mfile = NULL;
    g_autoptr(GError) error = NULL;
    mfile = g_mapped_file_new_from_fd(fd, FALSE, &error);
    if (!mfile)
    {
        errMsg = error->message;
        return nullptr;
    }

    return g_mapped_file_get_bytes(mfile);
}

/**
 * @brief : 规范化目录文件描述符
 * @param : [in] fd，目录文件描述符
 *
 * It's often convenient in programs to use `-1` for "unassigned fd",
 * and also because gobject-introspection doesn't support `AT_FDCWD`,
 * libglnx honors `-1` to mean `AT_FDCWD`.  This small inline function
 * canonicalizes `-1 -> AT_FDCWD`.
 */
int KMUtil::dirfdCanonicalize(int fd)
{
    if (fd == -1)
    {
        return AT_FDCWD;
    }

    return fd;
}

/*
不支持链接。适用于真正的临时存储。fd将被分配到指定的目录中.
 */
bool KMUtil::openAnonymousTmpfileFull(int flags, const std::string &dir, KMTmpfile &tmpfile, std::string &errMsg)
{
    /* Add in O_EXCL */
    if (!KMUtil::openTmpfileCore(AT_FDCWD, dir, flags | O_EXCL, tmpfile, errMsg))
    {
        return false;
    }

    if (!tmpfile.path.empty())
    {
        ::unlinkat(tmpfile.srcDfd, tmpfile.path.c_str(), 0);
    }

    tmpfile.anonymous = true;
    tmpfile.srcDfd = -1;

    return true;
}

/*
不支持链接。适用于真正的临时存储。fd将在“$TMPDIR”中分配（如果已设置），或在“/var/tmp”中进行分配（否则）。
如果您需要特定文件系统上的文件，请使用openAnonymousTmpfileFull（），它可以让您传递一个目录。
 */
bool KMUtil::openAnonymousTmpfile(int flags, KMTmpfile &tmpfile, std::string &errMsg)
{
    return KMUtil::openAnonymousTmpfileFull(flags, getenv("TMPDIR") ?: "/var/tmp", tmpfile, errMsg);
}

bool KMUtil::openTmpfileCore(int dfd, const std::string &subpath, int flags, KMTmpfile &tmpfile, std::string &errMsg)
{
    /* Picked this to match mkstemp() */
    const unsigned int mode = 0600;
    dfd = KMUtil::dirfdCanonicalize(dfd);

    /* 创建一个临时文件，稍后将其重命名为“目标”。如果可能，这将使用O_TMPFILE——在这种情况下，“ret_path”将以NULL返回。如果不可能，
            则在“ret_path”中返回所使用的临时路径名。在完整写入文件后，使用下面的link_tmpfile（）重命名结果。*/

    int fd = ::openat(dfd, subpath.c_str(), O_TMPFILE | flags, mode);
    if (fd == -1)
    {
        errMsg = "Failed to open(O_TMPFILE)";
        return false;
    }
    else
    {
        /* Workaround for https://sourceware.org/bugzilla/show_bug.cgi?id=17523
         * See also https://github.com/ostreedev/ostree/issues/991
         */
        if (::fchmod(fd, mode) < 0)
        {
            errMsg = "Failed to open(O_TMPFILE)";
            return false;
        }

        tmpfile.initialized = true;
        tmpfile.srcDfd = dfd; /* Copied; caller must keep open */
        tmpfile.fg.setFd(fd);
        tmpfile.path = "";
        return true;
    }

    return false;
}

bool KMUtil::openTmpfileLinkableAt(int dfd, const std::string &subpath, int flags, KMTmpfile &tmpfile, std::string &errMsg)
{
    // 不要允许O_EXCL，因为这对O_TMPFILE有特殊的意义；
    if ((flags & O_EXCL) != 0)
    {
        return false;
    }

    return KMUtil::openTmpfileCore(dfd, subpath, flags, tmpfile, errMsg);
}

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

    if (buf == nullptr)
    {
        return -1;
    }

    errno = 0;

    const unsigned char *p = buf;
    while (nbytes > 0)
    {
        ssize_t k = ::write(fd, p, nbytes);
        if (k < 0)
        {
            if (errno == EINTR)
            {
                continue;
            }

            return -1;
        }

        if (k == 0) /* Can't really happen */
        {
            errno = EIO;
            return -1;
        }

        p += k;
        nbytes -= k;
    }

    return 0;
}

/**
 * @brief 读取文件内容
 * @param fileName 文件名，包含路径
 * @param content 文件内容
 * @return bool true成功 false失败
 */
bool KMUtil::readFile(const std::string &fileName, std::string &content)
{
    try
    {
        content.clear();
        ifstream ifs(fileName);
        std::ostringstream in;
        in << ifs.rdbuf();
        content = in.str();
        return in.good();
    }
    catch (...)
    {
        return false;
    }
}

/**
 * @brief 立即写文件
 * @param fileName 文件名，包含路径
 * @param content 文件内容
 * @return bool
 */
bool KMUtil::syncWriteFile(const std::string &fileName, const std::string &content)
{
    std::unique_ptr<std::FILE, int (*)(std::FILE *)> fp(std::fopen(fileName.c_str(), "w"), std::fclose);

    if (!fp)
    {
        std::perror("Failed to write file");
        return false;
    }

    ::fwrite(content.c_str(), sizeof(char), content.length(), fp.get());

    ::fflush(fp.get());
    fdatasync(::fileno(fp.get()));

    return true;
}

/*
        如果memfd_create（）可用，则生成一个内容为@str的密封memfd。否则，在匿名模式下使用O_TMPFILE@tmpf，将@str写入@tmpf中，
    并将lseek（）写回起始位置。另请参阅类似的用法，例如rpm-ostree，用于运行dracut。
 */
bool KMUtil::sealedMemfdOrTmpfile(KMTmpfile &tmpfile, const std::string &name, const char *data, size_t dataLen, std::string &errMsg)
{
    int memfd = ::memfd_create(name.c_str(), MFD_CLOEXEC | MFD_ALLOW_SEALING);
    int fd;
    if (memfd != -1)
    {
        fd = memfd;
    }
    else
    {
        if (!KMUtil::openAnonymousTmpfile(O_RDWR | O_CLOEXEC, tmpfile, errMsg))
        {
            errMsg = "Failed to memfd_create file : " + name;
            return false;
        }

        fd = tmpfile.fg.fd();
    }

    if (::ftruncate(fd, dataLen) < 0)
    {
        errMsg = "Failed to ftruncate file : " + name;
        return false;
    }

    if (KMUtil::loopWrite(fd, (const unsigned char *)data, dataLen) < 0)
    {
        errMsg = "Failed to write file : " + name;
        return false;
    }

    if (::lseek(fd, 0, SEEK_SET) < 0)
    {
        errMsg = "Failed to lseek file : " + name;
        return false;
    }

    if (memfd != -1)
    {
        if (::fcntl(memfd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE | F_SEAL_SEAL) < 0)
        {
            errMsg = "Failed to fcntl(F_ADD_SEALS) file : " + name;
            return false;
        }

        /* The other values can stay default */
        tmpfile.fg.setFd(fd);
        tmpfile.initialized = true;
    }

    return true;
}

/**
 * @brief : 创建一个新文件，用@buf原子性地替换@subpath（相对于@dfd）的内容。默认情况下，如果文件已经存在，则在重命名（）之前将使用fdatasync（），以确保内容稳定。
 * 此行为和其他行为可以通过@flags进行控制。 请注意，现有文件中没有保留任何元数据，例如uid/gid或扩展属性。默认模式为“0644”。
 * @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: 操作是否成功
 */
bool KMUtil::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)
{
    dfd = KMUtil::dirfdCanonicalize(dfd);

    if (mode == (mode_t)-1)
    {
        mode = 0644;
    }

    KMTmpfile tmpf;
    char *dnbuf = strdupa(subpath);
    std::string dn = ::dirname(dnbuf);
    if (!KMUtil::openTmpfileLinkableAt(dfd, dn, O_WRONLY | O_CLOEXEC, tmpf, errMsg))
    {
        return false;
    }

    if (size == -1)
    {
        size = strlen((char *)buf);
    }

    // 快速的为某个文件分配实际的磁盘空间
    if (::fallocate(tmpf.fd(), 0, 0, size) < 0)
    {
        errMsg = "Failed to fallocate";
        return false;
    }

    if (KMUtil::loopWrite(tmpf.fd(), buf, size) < 0)
    {
        errMsg = "Failed to loopWrite to file";
        return false;
    }

    bool increasingMtime = (flags & FILE_REPLACE_INCREASING_MTIME) != 0;
    bool nodatasync = (flags & FILE_REPLACE_NODATASYNC) != 0;
    bool datasyncNew = (flags & FILE_REPLACE_DATASYNC_NEW) != 0;
    struct stat stbuf;
    bool hasStbuf = false;

    if (!nodatasync || increasingMtime)
    {
        if (TEMP_FAILURE_RETRY(::fstatat(dfd, subpath, &stbuf, AT_SYMLINK_NOFOLLOW)) != 0)
        {
            if (errno != ENOENT)
            {
                errMsg = "Failed to fstatat file";
                return false;
            }
            hasStbuf = false;
        }
        else
            hasStbuf = true;
    }

    if (!nodatasync)
    {
        bool doSync = true;
        if (!hasStbuf)
        {
            doSync = datasyncNew;
        }

        if (doSync)
        {
            if (TEMP_FAILURE_RETRY(::fdatasync(tmpf.fd())) != 0)
            {
                errMsg = "Failed to fdatasync file";
                return false;
            }
        }
    }

    if (uid != (uid_t)-1)
    {
        if (TEMP_FAILURE_RETRY(::fchown(tmpf.fd(), uid, gid)) != 0)
        {
            errMsg = "Failed to fchown file";
            return false;
        }
    }

    if (TEMP_FAILURE_RETRY(::fchmod(tmpf.fd(), mode)) != 0)
    {
        errMsg = "Failed to fchmod file";
        return false;
    }

    if (increasingMtime && hasStbuf)
    {
        struct stat fd_stbuf;
        if (::fstat(tmpf.fd(), &fd_stbuf) != 0)
        {
            errMsg = "Failed to fstat file";
            return false;
        }

        // 我们希望确保新文件的st_mtime（即第二个精度）是递增的，以避免文件经常更改时出现mtime检查问题。
        if (fd_stbuf.st_mtime <= stbuf.st_mtime)
        {
            struct timespec ts[2] = { { 0, UTIME_OMIT }, { stbuf.st_mtime + 1, 0 } };
            if (TEMP_FAILURE_RETRY(::futimens(tmpf.fd(), ts)) != 0)
            {
                errMsg = "Failed to futimens file";
                return false;
            }
        }
    }

    if (!KMUtil::linkTmpfileAt(tmpf, LINK_TMPFILE_REPLACE, dfd, subpath, errMsg))
    {
        errMsg = "Failed to linkTmpfileAt file";
        return false;
    }

    return true;
}

#define DECIMAL_STR_MAX(type) (2 + (sizeof(type) <= 1 ? 3 : sizeof(type) <= 2 ? 5 : sizeof(type) <= 4 ? 10 : sizeof(type) <= 8 ? 20 : sizeof(int[-2 * (sizeof(type) > 8)])))

// 在调用linkTmpfileAt（）为文件提供最终名称（链接到位）后使用此方法。
bool KMUtil::linkTmpfileAt(KMTmpfile &tmpf, LinkTmpfileReplaceMode mode, int target_dfd, const char *target, std::string &errMsg)
{
    if (tmpf.anonymous)
    {
        return false;
    }

    if (tmpf.fd() < 0)
    {
        return false;
    }

    if (tmpf.srcDfd != AT_FDCWD && tmpf.srcDfd < 0)
    {
        return false;
    }

    // 与原始的systemd代码不同，此函数还支持替换现有文件。
    const bool replace = (mode == LINK_TMPFILE_REPLACE);
    const bool ignore_eexist = (mode == LINK_TMPFILE_NOREPLACE_IGNORE_EXIST);

    // 对于没有O_tmpfile的旧系统，我们有“tmpfile_path”
    if (tmpf.path.empty())
    {
        // 在这种情况下，我们有O_TMPFILE，所以我们通过/proc/self/fd引用它
        std::string procFdPath = "/proc/self/fd/" + std::to_string(tmpf.fd());

        if (replace)
        {
            // 在这种情况下，我们以原子方式隐藏了临时文件，但现在我们需要使其在FS中可见，以便进行重命名。理想情况下，linkat（）将获得AT_REPLACE。
            char *dnbuf = strdupa(target);
            std::string dn = ::dirname(dnbuf);
            std::string tmpnameBuf = dn + "/tmp.XXXXXX";

            int countMax = 100;
            int count = 0;
            for (; count < countMax; count++)
            {
                std::string tmpname = tmpnameBuf;
                KMUtil::genTempName(tmpname);

                if (::linkat(AT_FDCWD, procFdPath.c_str(), target_dfd, tmpname.c_str(), AT_SYMLINK_FOLLOW) < 0)
                {
                    if (errno == EEXIST)
                    {
                        continue;
                    }
                    else
                    {
                        errMsg = "Failed to linkat " + procFdPath;
                        return false;
                    }
                }
                else
                {
                    tmpnameBuf = tmpname;
                    break;
                }
            }

            if (count == countMax)
            {
                errMsg = "Exhausted 100 attempts to create temporary file";
                return false;
            }

            if (TEMP_FAILURE_RETRY(::renameat(target_dfd, tmpnameBuf.c_str(), target_dfd, target)) != 0)
            {
                // 这是目前唯一一种需要使用O_TMPFILE清除unkat（）的情况。
                (void)::unlinkat(target_dfd, tmpnameBuf.c_str(), 0);

                errMsg = "Failed to renameat tmpfile " + tmpnameBuf;
                return false;
            }
        }
        else
        {
            if (::linkat(AT_FDCWD, procFdPath.c_str(), target_dfd, target, AT_SYMLINK_FOLLOW) < 0)
            {
                if (errno == EEXIST && mode == LINK_TMPFILE_NOREPLACE_IGNORE_EXIST)
                {
                    ;
                }
                else
                {
                    errMsg = std::string("Failed to linkat file : ") + target;
                    return false;
                }
            }
        }
    }
    else
    {
        // TODO: 暂不支持没有O_tmpfile的旧系统
    }

    return true;
}

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

    static const char letters[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    static const int NLETTERS = sizeof(letters) - 1;

    char XXXXXX[6 + 1] = { 0 };
    int i = 0;
    for (; i < 6; i++)
    {
        XXXXXX[i] = letters[g_random_int_range(0, NLETTERS)];
    }
    XXXXXX[i] = 0;

    KMUtil::replace(tmp, "XXXXXX", XXXXXX);

    return true;
}

/* 有时这是/var/run-->/run，它是一个符号链接，当我们将其作为路径传递到沙箱时会引起奇怪的问题 */
std::string KMUtil::getRealXdgRuntimeDir()
{
    const char *userRuntimeDir = g_get_user_runtime_dir();
    if (userRuntimeDir != nullptr)
    {
        return KMUtil::realpath(userRuntimeDir);
    }
    else
    {
        return std::string("/run/user/") + std::to_string(::getuid());
    }
}

/* 获取PulseAudio用于其套接字的运行时目录。*/
std::string KMUtil::getPulseRuntimeDir()
{
    const char *val = ("PULSE_RUNTIME_PATH");
    if (val != nullptr)
    {
        return KMUtil::realpath(val);
    }

    {
        const char *userRuntimeDir = g_get_user_runtime_dir();
        if (userRuntimeDir != nullptr)
        {
            std::string dir = KMUtil::buildFilename(userRuntimeDir, "pulse");
            if (fs::is_directory(dir))
            {
                return KMUtil::realpath(dir);
            }
        }
    }

    {
        std::string pulseHome = KMUtil::getPulseHome();
        std::string machineId = KMUtil::getPulseMachineId();
        if (!pulseHome.empty() && !machineId.empty())
        {
            /* This is usually a symlink, but we take its realpath() anyway */
            std::string dir = pulseHome + "/" + machineId + "-runtime";
            if (fs::is_directory(dir))
            {
                return KMUtil::realpath(dir);
            }
        }
    }

    return "";
}

/*
 * 获取PulseAudio用于其配置的目录。
 */
std::string KMUtil::getPulseHome()
{
    /* Legacy path ~/.pulse is tried first, for compatibility */
    {
        std::string ret = KMUtil::buildFilename(g_get_home_dir(), ".pulse");
        if (fs::is_directory(ret))
        {
            return ret;
        }
    }

    /* The more modern path, usually ~/.config/pulse */
    {
        std::string ret = KMUtil::buildFilename(g_get_user_config_dir(), "pulse");
        if (fs::is_directory(ret))
        {
            return ret;
        }
    }

    return "";
}

/*
 * 获取PulseAudio使用的机器ID。这是systemd/D-Bus机器ID，否则是主机名。
 */
std::string KMUtil::getPulseMachineId()
{
    std::vector<std::string> machineIds = { "/etc/machine-id", "/var/lib/dbus/machine-id" };

    for (auto &machineId : machineIds)
    {
        std::fstream file(machineId);
        if (file.is_open())
        {
            std::string line;
            std::getline(file, line);
            line = KMUtil::strstrip(line);
            size_t i = 0;
            for (; i < line.length(); i++)
            {
                if (!isxdigit(line.at(i)))
                {
                    break;
                }
            }

            if (!line.empty() && i == line.length())
            {
                return line;
            }
        }
    }

    return g_get_host_name();
}

bool KMUtil::argumentNeedsQuoting(const char *arg)
{
    if (*arg == '\0')
    {
        return true;
    }

    while (*arg != 0)
    {
        char c = *arg;
        if (!std::isalnum(c) && !(c == '-' || c == '/' || c == '~' || c == ':' || c == '.' || c == '_' || c == '=' || c == '@'))
        {
            return true;
        }
        arg++;
    }
    return false;
}

/**
 * @brief : 执行外部程序
 * @param : [in] exec, 外部程序名称，不包含路径
 * @param : [in] args, 执行外部程序需要传入的参数列表
 * @param : [out] result, 外部程序执行时的标准输出和标准错误信息
 * @return: true - 成功执行；false - 出现错误
*/
bool KMUtil::spawn(const std::string &exec, const std::vector<std::string> &args, std::string &result)
{
    std::string cmdline = exec;
    for (auto &arg : args)
    {
        cmdline += " ";
        if (KMUtil::argumentNeedsQuoting(arg.c_str()))
        {
            g_autofree char *quoted = g_shell_quote(arg.c_str());
            cmdline += quoted;
        }
        else
        {
            cmdline += arg;
        }
    }
    KMDebug(cmdline);
    
    bp::ipstream is;
    bp::child c(bp::search_path(exec), bp::args = args, (bp::std_out & bp::std_err) > is);
    std::string line;
    bool first = true;
    while (is && std::getline(is, line))
    {
        if (first)
        {
            first = false;
        }
        else
        {
            result += "\n";
        }
        result += line;
    }
    c.wait();

    if (c.exit_code() == 0)
    {
        return true;
    }
    else
    {
        return false;
    }
}

/**
 * @brief : 执行外部程序
 * @param : [in] exec, 外部程序
 * @param : [in] args, 执行外部程序需要传入的参数列表
 * @return: true - 成功执行；false - 出现错误
*/
bool KMUtil::spawn(const std::string &exec, const std::vector<std::string> &args)
{
    std::string cmdline = exec;
    for (auto &arg : args)
    {
        cmdline += " ";
        if (KMUtil::argumentNeedsQuoting(arg.c_str()))
        {
            g_autofree char *quoted = g_shell_quote(arg.c_str());
            cmdline += quoted;
        }
        else
        {
            cmdline += arg;
        }
    }
    KMDebug(cmdline);
    
    bp::ipstream is;
    bp::child c(bp::search_path(exec), bp::args = args, (bp::std_out & bp::std_err) > is);
    std::string line;
    while (is && std::getline(is, line))
    {
       std::cout << line << std::endl;
    }
    c.wait();

    if (c.exit_code() == 0)
    {
        return true;
    }
    else
    {
        return false;
    }
}

/**
 * @brief : 执行外部程序
 * @param : [in] exec, 外部程序名称，不包含路径
 * @param : [in] workingDirectory, 执行外部程序工作目录
 * @param : [in] args, 执行外部程序需要传入的参数列表
 * @param : [out] result, 外部程序执行时的标准输出和标准错误信息
 * @return: true - 成功执行；false - 出现错误
*/
bool KMUtil::spawn(const std::string &exec, const std::string &workingDirectory, const std::vector<std::string> &args, std::string &result)
{
    KMDebug("cd " + workingDirectory);
    std::string cmdline = exec;
    for (auto &arg : args)
    {
        cmdline += " ";
        if (KMUtil::argumentNeedsQuoting(arg.c_str()))
        {
            g_autofree char *quoted = g_shell_quote(arg.c_str());
            cmdline += quoted;
        }
        else
        {
            cmdline += arg;
        }
    }
    KMDebug(cmdline);

    bp::ipstream is;
    bp::child c(bp::search_path(exec), bp::start_dir(workingDirectory), bp::args = args, (bp::std_out & bp::std_err) > is);
    std::string line;
    bool first = true;
    while (is && std::getline(is, line))
    {
        if (first)
        {
            first = false;
        }
        else
        {
            result += "\n";
        }
        result += line;
    }
    c.wait();

    if (c.exit_code() == 0)
    {
        return true;
    }
    else
    {
        return false;
    }
}

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

    for (auto const &dirEntry : fs::directory_iterator{ basePath })
    {
        fs::path p = dirEntry.path();
        if (fs::is_regular_file(p) && p.filename() == filename)
        {
            return true;
        }
        else if (fs::is_directory(p))
        {
            return findFileInTree(KMUtil::buildFilename(baseDir, p.filename()), filename);
        }
    }

    return false;
}

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

    if (!fs::is_directory(p))
    {
        return false;
    }

    bool ret = true;
    for (auto const &dirEntry : fs::directory_iterator{ p })
    {
        std::string filename = dirEntry.path().filename();
        if (filename != "." && filename != "..")
        {
            ret = false;
            break;
        }
    }

    return ret;
}

/**
 * @brief : 获取文件或目录中文件大小
 */ 
uint64_t KMUtil::getDirectorySize(const fs::path& dir)
{
    if (!fs::exists(dir) || !fs::is_directory(dir)) {
        return 0;
    }

    // 不具备访问权限的目录略过
    if (::access(dir.string().c_str(), R_OK) != 0)
    {
        return 0;
    }

    if (fs::is_regular_file(dir))
    {
        return fs::file_size(dir);
    }
    else if (fs::is_symlink(dir))
    {
        return 0;
    }
    else if (fs::is_directory(dir))
    {
        uint64_t totalSize = 0;
        for (const auto& entry : fs::directory_iterator(dir)) 
        {
            if (fs::is_regular_file(entry.status())) {
                totalSize += fs::file_size(entry.path());
            }
            else if (fs::is_symlink(entry.path()))
            {
                // do nothing
            }
            else if (fs::is_directory(entry.path()))
            {
                totalSize += KMUtil::getDirectorySize(entry.path());
            }
        }

        return totalSize;
    }
 
    return 0;
}

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

    char str[16] = { 0 };
    double fcount = size * 1.0;

    if (size > GB)
    {
        snprintf(str, sizeof(str), "%.2f GB", fcount / GB);
    }
    else if (size > MB)
    {
        snprintf(str, sizeof(str), "%.2f MB", fcount / MB);
    }
    else if (size > KB)
    {
        snprintf(str, sizeof(str), "%.2f KB", fcount / KB);
    }
    else
    {
        snprintf(str, sizeof(str), "%lu B", size);
    }

    return str;
}

/**
 * @brief : 判断是否数字字符串
 * @return: true-表示是数字字符串；false-表示不是纯数字字符串
 */
bool KMUtil::isInteger(const char *s)
{
    if (s == nullptr || *s == '\0')
    {
        return false;
    }

    for (; *s != '\0'; s++)
    {
        if (!std::isdigit(*s))
        {
            return false;
        }
    }

    return true;
}

/**
 * @brief 判断字符串是否相等， `==` 运算符会存在警告
 *
 * @param str1
 * @param str2
 * @return true
 * @return false
 */
bool KMUtil::isStringEqual(std::string str1, std::string str2)
{
    return std::equal(str1.begin(), str1.end(), str2.begin(), str2.end());
}

/**
 * @brief 计算字符串中以空格为分隔符的参数个数
 *
 * @param str 待计算字符串，如 "kigo %u"
 * @return int 返回符串中以空格为分隔符的参数个数，"kigo %u" 参数个数为2
 */
int KMUtil::countArgsNumber(const std::string &str)
{
    int count = 0;
    for (char c : str)
    {
        if (c == ' ')
        {
            count++;
        }
    }

    return count + 1;
}

/**
 * @brief 格式化desktop文件Exec字段
 *
 * @param input 输入待格式化的Exec字段，如 "kigo %u -qwindowicon kigo -qwindowtitle %c", "kamoso -qwindowtitle %c"
 * @param refId 输入的refId, 如 "org.kde.kigo"
 * @return std::string 格式化后的字段，如 "kigo --file-forwarding org.kde.kigo @@u %u @@ -qwindowicon kigo -qwindowtitle %c", "kamoso org.kde.kamoso -qwindowtitle %c"
 */
std::string KMUtil::formatExecString(const std::string &input, const std::string &refId)
{
    std::istringstream iss(input);
    std::vector<std::string> tokens;
    std::vector<std::string> tokensInput;
    std::string token;

    int count = countArgsNumber(input);
    kmlogger.info("command args count: %d", count);

    while (iss >> token)
    {
        kmlogger.info("Exec input arg: %s", token.c_str());
        tokensInput.push_back(token);
    }

    size_t argsLen = tokensInput.size();

    std::string commandArg = string("--command=").append(tokensInput[0]);

    if (argsLen >= 1)
    {
        tokens.push_back(commandArg);

        for (size_t i = 1; i < argsLen; i++)
        {
            token = tokensInput[i];

            if (token == "%f" || token == "%u")
            {
                tokens.push_back("--file-forwarding");
                break;
            }
        }

        tokens.push_back(refId);

        for (size_t i = 1; i < argsLen; i++)
        {
            token = tokensInput[i];

            if (token == "%f")
            {
                tokens.push_back("@@");
                tokens.push_back(token);
                tokens.push_back("@@");
            }
            else if (token == "%u")
            {
                tokens.push_back("@@u");
                tokens.push_back(token);
                tokens.push_back("@@");
            }
            else
            {
                tokens.push_back(token);
            }
        }
    }
    else
    {
        tokens.push_back(" ");
        tokens.push_back(refId);
    }

    std::ostringstream oss;
    for (size_t i = 0; i < tokens.size(); ++i)
    {
        oss << tokens[i];
        if (i < tokens.size() - 1)
        {
            oss << ' ';
        }
    }

    return oss.str();
}

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

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