#include "KMBuilderConfigJson.h"
#include "KMBuilderContext.h"
#include "common/KMLogger.h"

#include <filesystem>
#include <fstream>

namespace fs = std::filesystem;

using namespace std;

KMBuilderConfigJson ::KMBuilderConfigJson()
    : KMBuilderConfig()
{
}

KMBuilderConfigJson ::~KMBuilderConfigJson() { }

bool KMBuilderConfigJson ::loadConfigFile(const string &configFile)
{
    if (configFile.substr(configFile.length() - 5, 5) != ".json")
    {
        return false;
    }

    if (!fs::exists(configFile))
    {
        km_logger.error("config file %s not exist!\n", configFile.c_str());
        return false;
    }

    if (configFile.length() <= 4)
    {
        km_logger.error("config file name %s too short!\n", configFile.c_str());
        return false;
    }

    ifstream infile;
    infile.open(configFile);
    if (!infile.is_open())
    {
        km_logger.error("config file %s open error!\n", configFile.c_str());
        return false;
    }

    this->m_configFile = fs::absolute(configFile);
    // fs::current_path依赖于当前路径，会限定死kaiming-builder的运行目录（debug时更不得了），建议后续整改。由于使用fs::current_path的地方太多，本次临时处理。
    // this->m_configPath = fs::current_path();
    this->m_configPath = fs::path(this->m_configFile).parent_path().string();
    // fs::current_path(this->m_configPath);

    Json::CharReaderBuilder reader;
    string err;
    if (!Json::parseFromStream(reader, infile, &m_rootNode, &err))
    {
        km_logger.error("file parse error!");
        return false;
    }

    return loadBase();
}

any KMBuilderConfigJson ::getAttribute(const string &name)
{
    return m_configData->attributeMap.find(name) == m_configData->attributeMap.end() ? any() : m_configData->attributeMap[name];
}

void KMBuilderConfigJson ::setConfigPath(const string &configPath)
{
    this->m_configPath = configPath;
}

shared_ptr<KMBuilderModule> KMBuilderConfigJson ::getSharedModule(const string &path)
{
    std::string savedDemarshalBaseDir = demarshalBaseDir();
    std::string sharedModuleFile = KMBuilderUtils::buildFilename(savedDemarshalBaseDir, path);
    if (!fs::exists(sharedModuleFile))
    {
        KMError("Shared module file " + sharedModuleFile + " not exists!");
        exit(1);
    }

    ifstream infile;
    infile.open(sharedModuleFile);
    if (!infile.is_open())
    {
        KMError("Shared module file " + sharedModuleFile + " open error!");
        exit(1);
    }

    // fs::current_path(fs::path(path).relative_path().parent_path());

    Json::CharReaderBuilder reader;
    string err;
    Json::Value node;
    if (!Json::parseFromStream(reader, infile, &node, &err))
    {
        // km_logger.error("file parse error!");
        // printf("file %s parse error!\n", path.c_str());
        KMError("Shared module file " + sharedModuleFile + " parse error!");
        exit(1);
    }

    std::string demarshalBaseDir = fs::path(sharedModuleFile).parent_path().string();
    setDemarshalBaseDir(demarshalBaseDir);
    std::shared_ptr<KMBuilderModule> ret = parseModuleNode(fs::current_path(), node);
    setDemarshalBaseDir(savedDemarshalBaseDir);
    return ret;
}

bool KMBuilderConfigJson ::loadBase()
{
    m_configData->attributeMap.clear();

    m_configData->attributeMap["app-id"] = getString(m_rootNode, "app-id");
    m_configData->attributeMap["id"] = getString(m_rootNode, "id");
    m_configData->attributeMap["id-platform"] = getString(m_rootNode, "id-platform");
    m_configData->attributeMap["branch"] = getString(m_rootNode, "branch");
    m_configData->attributeMap["default-branch"] = getString(m_rootNode, "default-branch");
    m_configData->attributeMap["runtime"] = getString(m_rootNode, "runtime");
    m_configData->attributeMap["runtime-commit"] = getString(m_rootNode, "runtime-commit");
    m_configData->attributeMap["runtime-version"] = getString(m_rootNode, "runtime-version");

    if (getString(m_rootNode, "sdk") == "")
        return false;
    m_configData->attributeMap["sdk"] = getString(m_rootNode, "sdk");
    m_configData->attributeMap["sdk-commit"] = getString(m_rootNode, "sdk-commit");
    m_configData->attributeMap["sdk-version"] = getString(m_rootNode, "sdk-version");
    m_configData->attributeMap["base"] = getString(m_rootNode, "base");
    m_configData->attributeMap["base-commit"] = getString(m_rootNode, "base-commit");
    m_configData->attributeMap["base-version"] = getString(m_rootNode, "base-version");
    m_configData->attributeMap["var"] = getString(m_rootNode, "var");
    m_configData->attributeMap["metadata"] = getString(m_rootNode, "metadata");
    m_configData->attributeMap["metadata-platform"] = getString(m_rootNode, "metadata-platform");
    m_configData->attributeMap["command"] = getString(m_rootNode, "command");
    m_configData->attributeMap["rename-desktop-file"] = getString(m_rootNode, "rename-desktop-file");
    m_configData->attributeMap["desktop-file-name-prefix"] = getString(m_rootNode, "desktop-file-name-prefix");
    m_configData->attributeMap["desktop-file-name-suffix"] = getString(m_rootNode, "desktop-file-name-suffix");
    m_configData->attributeMap["rename-appdata-file"] = getString(m_rootNode, "rename-appdata-file");
    m_configData->attributeMap["appdata-license"] = getString(m_rootNode, "appdata-license");
    m_configData->attributeMap["rename-icon"] = getString(m_rootNode, "rename-icon");
    m_configData->attributeMap["collection-id"] = getString(m_rootNode, "collection-id");
    m_configData->attributeMap["extension-tag"] = getString(m_rootNode, "extension-tag");
    m_configData->attributeMap["build-runtime"] = getBool(m_rootNode, "build-runtime");
    m_configData->attributeMap["build-extension"] = getBool(m_rootNode, "build-extension");
    m_configData->attributeMap["separate-locales"] = getBool(m_rootNode, "separate-locales");
    m_configData->attributeMap["writable-sdk"] = getBool(m_rootNode, "writable-sdk");
    m_configData->attributeMap["appstream-compose"] = getBool(m_rootNode, "appstream-compose");
    m_configData->attributeMap["copy-icon"] = getBool(m_rootNode, "copy-icon");

    m_configData->baseExtensions = getStringArray(m_rootNode, "base-extensions");
    m_configData->cleanup = getStringArray(m_rootNode, "cleanup");
    m_configData->cleanupCommands = getStringArray(m_rootNode, "cleanup-commands");
    m_configData->cleanupPlatform = getStringArray(m_rootNode, "cleanup-platform");
    m_configData->cleanupPlatformCommands = getStringArray(m_rootNode, "cleanup-platform-commands");
    m_configData->preparePlatformCommands = getStringArray(m_rootNode, "prepare-platform-commands");
    m_configData->finishArgs = getStringArray(m_rootNode, "finish-args");
    m_configData->inheritExtensions = getStringArray(m_rootNode, "inherit-extensions");
    m_configData->inheritSdkExtensions = getStringArray(m_rootNode, "inherit-sdk-extensions");
    m_configData->sdkExtensions = getStringArray(m_rootNode, "sdk-extensions");
    m_configData->platformExtensions = getStringArray(m_rootNode, "platform-extensions");
    m_configData->tags = getStringArray(m_rootNode, "tags");

    m_configData->buildOptions = getBuildOptions(m_rootNode.get("build-options", Json::Value()));

    std::string savedDemarshalBaseDir = demarshalBaseDir();
    std::string demarshalBaseDir = KMBuilderUtils::buildFilename(savedDemarshalBaseDir, fs::path(this->m_configFile).parent_path().string());
    setDemarshalBaseDir(demarshalBaseDir);
    m_modules = getNodeModules(m_rootNode);
    // fs::current_path(configPath);
    setDemarshalBaseDir(savedDemarshalBaseDir);
    loadAddExtensions();
    loadAddBuildExtensions();

    return true;
}

shared_ptr<KMBuildOptions> KMBuilderConfigJson ::getBuildOptions(const Json::Value &node)
{
    shared_ptr<KMBuildOptions> buildOptions = make_shared<KMBuildOptions>();

    if (node.isNull())
        return buildOptions;

    buildOptions->m_strip = getBool(node, "strip");
    buildOptions->m_noDebuginfo = getBool(node, "no-debuginfo");
    buildOptions->m_noDebuginfoCompression = getBool(node, "no-debuginfo-compression");
    buildOptions->m_cFlags = getString(node, "cflags");
    buildOptions->m_cflagsOverride = getBool(node, "cflags-override");
    buildOptions->m_cppFlags = getString(node, "cppflags");
    buildOptions->m_cppFlagsOverride = getBool(node, "cppflags-override");
    buildOptions->m_cxxFlags = getString(node, "cxxflags");
    buildOptions->m_cxxFlagsOverride = getBool(node, "cxxflags-override");
    buildOptions->m_ldFlags = getString(node, "ldflags");
    buildOptions->m_ldFlagsOverride = getBool(node, "ldflags-override");
    buildOptions->m_appendPath = getString(node, "append-path");
    buildOptions->m_prependPath = getString(node, "prepend-path");
    buildOptions->m_appendLdLibraryPath = getString(node, "append-ld-library-path");
    buildOptions->m_prependLdLibraryPath = getString(node, "prepend-ld-library-path");
    buildOptions->m_appendPkgConfigPath = getString(node, "append-pkg-config-path");
    buildOptions->m_prependPkgConfigPath = getString(node, "prepend-pkg-config-path");
    buildOptions->m_prefix = getString(node, "prefix");
    buildOptions->m_libDir = getString(node, "libdir");
    buildOptions->m_buildArgs = getStringArray(node, "build-args");
    buildOptions->m_testArgs = getStringArray(node, "test-args");
    buildOptions->m_configOpts = getStringArray(node, "config-opts");
    buildOptions->m_secretOpts = getStringArray(node, "secret-opts");
    buildOptions->m_makeArgs = getStringArray(node, "make-args");
    buildOptions->m_makeInstallArgs = getStringArray(node, "make-install-args");

    Json::Value envNode = node.get("env", Json::Value());
    if (!envNode.isNull() && envNode.isObject())
    {
        for (Json::Value::const_iterator iter = envNode.begin(); iter != envNode.end(); iter++)
        {
            buildOptions->m_env[iter.key().asString()] = getString(envNode, iter.key().asString());
        }
    }

    Json::Value secretEnvNode = node.get("secret-env", Json::Value());
    if (!secretEnvNode.isNull() && secretEnvNode.isObject())
    {
        for (Json::Value::const_iterator iter = secretEnvNode.begin(); iter != secretEnvNode.end(); iter++)
        {
            buildOptions->m_secretEnv[iter.key().asString()] = getString(secretEnvNode, iter.key().asString());
        }
    }

    return buildOptions;
}

void KMBuilderConfigJson ::loadAddExtensions()
{
    Json::Value node = m_rootNode.get("add-extensions", Json::Value());
    for (Json::Value::const_iterator iter = node.begin(); iter != node.end(); iter++)
    {
        m_configData->addExtensions[iter.key().asString()] = getExtension(node);
    }
}

void KMBuilderConfigJson ::loadAddBuildExtensions()
{
    Json::Value node = m_rootNode.get("add-build-extensions", Json::Value());
    for (Json::Value::const_iterator iter = node.begin(); iter != node.end(); iter++)
    {
        m_configData->addBuildExtensions[iter.key().asString()] = getExtension(node);
    }
}

const shared_ptr<BuilderExtension> KMBuilderConfigJson ::getExtension(const Json::Value &node)
{
    shared_ptr<BuilderExtension> ext = make_shared<BuilderExtension>();

    ext->name = getString(node, "name");
    ext->directory = getString(node, "directory");
    ext->bundle = getBool(node, "bundle");
    ext->removeAfterBuild = getBool(node, "remove-after-build");
    ext->autodelete = getBool(node, "autodelete");
    ext->noAutoDownload = getBool(node, "no-autodownload");
    ext->localeSubset = getBool(node, "locale-subset");
    ext->subdirectories = getBool(node, "subdirectories");
    ext->addLdPath = getString(node, "add-ld-path");
    ext->downloadIf = getString(node, "download-if");
    ext->enableIf = getString(node, "enable-if");
    ext->version = getString(node, "version");
    ext->subdirectorySuffix = getString(node, "subdirectory-suffix");

    string str_merge_dirs = getString(node, "merge-dirs");
    ext->mergeDirs = KMBuilderUtils::split(str_merge_dirs, ';');

    string str_versions = getString(node, "versions");
    ext->versions = KMBuilderUtils::split(str_versions, ';');

    return ext;
}

const vector<shared_ptr<KMBuilderModule>> KMBuilderConfigJson ::getNodeModules(const Json::Value &node)
{
    vector<shared_ptr<KMBuilderModule>> modules;

    Json::Value modulesNode = node.get("modules", Json::Value());

    if (modulesNode.isArray())
    {
        for (int i = 0; i < modulesNode.size(); i++)
        {
            if (modulesNode[i].isString())
            {
                string sharedModule = modulesNode[i].asString();
                if (sharedModule.substr(sharedModule.length() - 5, sharedModule.length()) == ".json")
                {
                    // if (!m_rootNode.isNull() && node == m_rootNode)
                    //     fs::current_path(configPath);todo
                    modules.push_back(getSharedModule(sharedModule));
                    continue;
                }
            }
            modules.push_back(parseModuleNode(fs::current_path(), modulesNode[i]));
        }
    }
    return modules;
}

void prasejsonNode(Json::Value sourcesNode, shared_ptr<KMBuilderModule> &builderModule, string configPath, std::string savedDemarshalBaseDir)
{
    if (sourcesNode.isArray())
    {
        vector<shared_ptr<KMBuilderSource>> buildSources;

        for (int i_source = 0; i_source < sourcesNode.size(); i_source++)
        {
            string source_type = getString(sourcesNode[i_source], "type");
            if (source_type == "archive")
            {
                cout << "archive url : " << getString(sourcesNode[i_source], "url") << endl;
                shared_ptr<KMBuilderSourceArchive> sourceArchive = make_shared<KMBuilderSourceArchive>();
                sourceArchive->setBaseDir(savedDemarshalBaseDir);
                // sourceArchive->setPath(getString(sourcesNode[i_source], "path"));
                // sourceArchive->setUrl(getString(sourcesNode[i_source], "url"));
                // sourceArchive->setMd5(getString(sourcesNode[i_source], "md5"));
                // sourceArchive->setSha1(getString(sourcesNode[i_source], "sha1"));
                // sourceArchive->setSha256(getString(sourcesNode[i_source], "sha256"));
                // sourceArchive->setSha512(getString(sourcesNode[i_source], "sha512"));

                // // 设置下载路径
                // string downloadFilename = getString(sourcesNode[i_source], "dest-filename") != "" ? getString(sourcesNode[i_source], "dest-filename") : sourceArchive->getUrl().substr(sourceArchive->getUrl().find_last_of("/") + 1);
                // sourceArchive->setDestFilename(fs::path(configPath) / STATE_DIR / "downloads" / sourceArchive->getChecksum() / downloadFilename);

                // if (getString(sourcesNode[i_source], "strip-components") != "")
                //     sourceArchive->setStripComponents(atoi(getString(sourcesNode[i_source], "strip-components").c_str()));
                // sourceArchive->setGitInit(getBool(sourcesNode[i_source], "git-init"));
                // sourceArchive->setArchiveType(getString(sourcesNode[i_source], "archive-type"));
                buildSources.push_back(sourceArchive);
            }
            else if (source_type == "dir")
            {
                shared_ptr<KMBuilderSourceDir> sourceDir = make_shared<KMBuilderSourceDir>();
                // sourceDir ->setBaseDir(filepath);
                sourceDir->setBaseDir(savedDemarshalBaseDir);
                // sourceDir->setPath(getString(sourcesNode[i_source], "path"));
                // sourceDir->setUrl(getString(sourcesNode[i_source], "url"));
                // sourceDir->setMirrorUrls(getString(sourcesNode[i_source], "mirrorurl"));
                // sourceDir->setMd5(getString(sourcesNode[i_source], "md5"));
                // sourceDir->setSha1(getString(sourcesNode[i_source], "sha1"));
                // sourceDir->setSha256(getString(sourcesNode[i_source], "sha256"));
                // sourceDir->setSha512(getString(sourcesNode[i_source], "sha512"));
                // if (getString(sourcesNode[i_source], "strip-components") != "")
                //     sourceDir->setStripComponents(atoi(getString(sourcesNode[i_source], "strip-components").c_str()));
                // sourceDir->setDestFilename(getString(sourcesNode[i_source], "dest-filename"));
                // sourceDir->setMirrorUrls(getString(sourcesNode[i_source], "mirror-urls")); // 暂时设置为 string 类型，具体类型需要参考配置文件
                buildSources.push_back(sourceDir);
            }
            else if (source_type == "file")
            {
                shared_ptr<KMBuilderSourceFile> sourceFile = make_shared<KMBuilderSourceFile>();
                sourceFile->setBaseDir(savedDemarshalBaseDir);
                // sourceFile->setPath(getString(sourcesNode[i_source], "path"));
                // sourceFile->setUrl(getString(sourcesNode[i_source], "url"));
                // sourceFile->setMirrorUrls("mirror-urls");
                // sourceFile->setMd5(getString(sourcesNode[i_source], "md5"));
                // sourceFile->setSha1(getString(sourcesNode[i_source], "sha1"));
                // sourceFile->setSha256(getString(sourcesNode[i_source], "sha256"));
                // sourceFile->setSha512(getString(sourcesNode[i_source], "sha512"));
                // sourceFile->setStripComponents(atoi(getString(sourcesNode[i_source], "strip-components").c_str()));
                // sourceFile->setDestFilename(getString(sourcesNode[i_source], "dest-filename"));
                // sourceFile->setMirrorUrls(getString(sourcesNode[i_source], "mirror-urls")); // 暂时设置为 string 类型，具体类型需要参考配置文件
                buildSources.push_back(sourceFile);
            }
            else if (source_type == "git")
            {
                shared_ptr<KMBuilderSourceGit> sourceGit = make_shared<KMBuilderSourceGit>();
                // sourceGit->setBaseDir(filepath);
                sourceGit->setBaseDir(savedDemarshalBaseDir);
                // sourceGit->setUrl(getString(sourcesNode[i_source], "url"));
                // sourceGit->setPath(getString(sourcesNode[i_source], "path"));
                // sourceGit->setBranch(getString(sourcesNode[i_source], "branch"));
                // sourceGit->setTag(getString(sourcesNode[i_source], "tag"));
                // sourceGit->setCommit(getString(sourcesNode[i_source], "commit"));

                // sourceGit->setDisableFsckeobjects(getBool(sourcesNode[i_source], "disable-fsckobjects"));
                // sourceGit->setDisableShallowClone(getBool(sourcesNode[i_source], "disable-shallow-clone"));
                // sourceGit->setDisableSubmodules(getBool(sourcesNode[i_source], "disable-submodules"));
                buildSources.push_back(sourceGit);
                //  builderModule->addBuildSource(sourceGit);
            }
            else if (source_type == "script" || source_type == "shell")
            {
                shared_ptr<KMBuilderSourceScript> sourceScript = make_shared<KMBuilderSourceScript>();
                sourceScript->setBaseDir(savedDemarshalBaseDir);
                // vector<string> commands;
                // Json::Value commands_node = sourcesNode[i_source].get("commands", Json::Value());
                // if (commands_node.isArray())
                // {
                //     for (int i = 0; i < commands_node.size(); i++)
                //     {
                //         string temp = commands_node[i].toStyledString();
                //         commands.push_back(temp);
                //     }
                // }
                // sourceScript->setCommands(commands);
                // sourceScript->setDestFilename(getString(sourcesNode[i_source], "dest-filename"));
                buildSources.push_back(sourceScript);
            }
            else if (source_type == "svn")
            {
                shared_ptr<KMBuilderSourceSvn> sourceSvn = make_shared<KMBuilderSourceSvn>();
                sourceSvn->setBaseDir(savedDemarshalBaseDir);
                // sourceSvn->setUrl(getString(sourcesNode[i_source], "url"));
                // sourceSvn->setRevision(getString(sourcesNode[i_source], "revision"));
                // sourceSvn->setUsername(getString(sourcesNode[i_source], "username"));
                // sourceSvn->setPassword(getString(sourcesNode[i_source], "password"));
                // sourceSvn->setTag(getString(sourcesNode[i_source], "tag"));
                // sourceSvn->setPath(getString(sourcesNode[i_source], "path"));
                // sourceSvn->setDepth(getString(sourcesNode[i_source], "depth"));
                buildSources.push_back(sourceSvn);
            }
            else if (source_type == "patch")
            {
                shared_ptr<KMBuilderSourcePatch> sourcePatch = make_shared<KMBuilderSourcePatch>();
                sourcePatch->setBaseDir(savedDemarshalBaseDir);
                // if (getString(sourcesNode[i_source], "path") != "")
                //     sourcePatch->addPath(getString(sourcesNode[i_source], "path"));
                // Json::Value paths_node = sourcesNode[i_source].get("paths", Json::Value());
                // if (!paths_node.isNull() && paths_node.isArray())
                // {
                //     for (int i_source = 0; i_source < paths_node.size(); i_source++)
                //     {
                //         string path_str = paths_node[i_source].asString();
                //         sourcePatch->addPath(path_str);
                //     }
                // }
                // if (getString(sourcesNode[i_source], "strip-components") != "")
                //     sourcePatch->setStripComponents(atoi(getString(sourcesNode[i_source], "strip-components").c_str()));
                // else
                //     sourcePatch->setStripComponents(1);
                // sourcePatch->setUsegit(getString(sourcesNode[i_source], "use-git"));
                // sourcePatch->setOption(getString(sourcesNode[i_source], "options"));
                // sourcePatch->setUsegitam(getString(sourcesNode[i_source], "use-git-am"));
                buildSources.push_back(sourcePatch);
            }
        }
        builderModule->setBuildSources(buildSources);
    }
}

shared_ptr<KMBuilderModule> KMBuilderConfigJson ::parseModuleNode(string filepath, const Json::Value &node)
{
    if (node.isNull())
        return shared_ptr<KMBuilderModule>();

    // 添加一个 module
    shared_ptr<KMBuilderModule> builderModule = make_shared<KMBuilderModule>();
    builderModule->setProperty(KMBuilderModule::Module_Name, getString(node, "name"));
    builderModule->setProperty(KMBuilderModule::Module_Subdir, getString(node, "subdir"));
    builderModule->setProperty(KMBuilderModule::Module_RmConfigure, getBool(node, "rm-configure"));
    builderModule->setProperty(KMBuilderModule::Module_Disable, getBool(node, "disabled"));
    builderModule->setProperty(KMBuilderModule::Module_NoAutogen, getBool(node, "no-autogen"));
    builderModule->setProperty(KMBuilderModule::Module_NoParallelMake, getBool(node, "no-parallel-make"));
    builderModule->setProperty(KMBuilderModule::Module_NoMakeInstall, getBool(node, "no-make-install"));
    builderModule->setProperty(KMBuilderModule::Module_NoPythonTimestampFix, getBool(node, "no-python-timestamp-fix"));
    builderModule->setProperty(KMBuilderModule::Module_Cmake, getBool(node, "cmake"));

    string buildSystem = getString(node, "buildsystem");
    builderModule->setBuildSystem(buildSystem);

    builderModule->setProperty(KMBuilderModule::Module_InstallRule, getString(node, "install-rule"));
    builderModule->setProperty(KMBuilderModule::Module_TestRule, getString(node, "test-rule"));
    builderModule->setProperty(KMBuilderModule::Module_Builddir, getBool(node, "builddir"));

    // sources
    std::string savedDemarshalBaseDir = demarshalBaseDir();
    Json::Value sourcesNode = node.get("sources", Json::Value());

    prasejsonNode(sourcesNode, builderModule, m_configPath, savedDemarshalBaseDir);

    setDemarshalBaseDir(savedDemarshalBaseDir);

    builderModule->setConfigOptions(getStringArray(node, "config-opts"));
    builderModule->setBuildCommands(getStringArray(node, "build-commands"));
    builderModule->setCleanupPlatform(getStringArray(node, "cleanup-platform"));
    builderModule->setPostInstalls(getStringArray(node, "post-install"));

    // 处理 module 包含的 module
    // builderModule->setBuilderModules(getNodeModules(node));
    vector<shared_ptr<KMBuilderModule>> subModules = getNodeModules(node);
    setDemarshalBaseDir(savedDemarshalBaseDir);
    builderModule->setBuilderModules(subModules);
    builderModule->setBuildOptions(getBuildOptions(node));

    return builderModule;
}

const string getString(const Json::Value &node, const string &key)
{
    if (node.isNull())
        return string();

    Json::Value strNode = node.get(key, Json::Value());
    return strNode.isNull() ? string() : strNode.asString();
}

const bool getBool(const Json::Value &node, const string &key)
{
    if (node.isNull())
        return false;

    Json::Value boolNode = node.get(key, Json::Value());
    return boolNode.isNull() ? (key == "separate-locales" || key == "appstream-compose" ? true : false) : boolNode.asBool();
}

const vector<string> getStringArray(const Json::Value &node, const string &key)
{
    vector<string> stringArray;
    Json::Value arrayNode = node.get(key, Json::Value());
    if (!arrayNode.isArray())
        return stringArray;

    for (int i = 0; i < arrayNode.size(); i++)
        stringArray.push_back(arrayNode[i].asString());

    return stringArray;
}
