// SPDX-License-Identifier: MPL-2.0
#ifndef VYBUILD_LIB_H
#define VYBUILD_LIB_H
#include <string>
#include <fstream>
#include <filesystem>
#include <iostream>
#include <variant>
#include <vector>
#include <unistd.h>
#include <limits.h>
#include "json.hpp"
#include "xxhash.h"
#include <uuid/uuid.h>
using json=nlohmann::json;
namespace fs=std::filesystem;
const std::string syntax_version="0.1";
const std::vector<std::string> threaded_actions={"run_submodule","run_submodule_if","run_actions_parallel","vyld_compilation","run_submodule_parallel","compile_multiple_cpp"};
namespace vybuild {
  namespace fat32 {
    void load_crypto_lib(std::string path);
    bool is_crypto_lib_loaded();
    bool is_vftm_target_set();
    uint8_t* get_manifest_keypair_buffer();
    void create_fat32(std::string key,size_t size);
    void add_directory_fat32(std::string key,std::string full_path);
    void add_file_fat32(std::string key,std::string full_path,uint8_t *buffer_in,size_t file_size,bool is_vftm_target);
    std::vector<uint8_t> export_fat32(std::string key,bool generate_vftm,std::string disk_guid_var,std::string efi_part_unique_guid_var);
  }
  namespace disk {
    std::array<uint8_t,16> uuid4_to_bytes(const std::string& uuid);
    void create_disk(std::string key,size_t size,std::string disk_guid);
    void add_partition(std::string key,size_t partition_size_bytes,std::string type_guid,std::string unique_guid,uint64_t attributes,std::string name,uint8_t* in_buf,size_t in_size);
    std::vector<uint8_t> export_disk(std::string key);
  }
  namespace condition {
    bool evaluate_condition(const json& condition);
  }
  namespace parser {
    std::string root_folder_parsing(const std::string& root_folder,const std::string& file);
    std::vector<std::string> vector_string_vars(std::vector<std::string> vector_in);
    std::string string_vars(std::string string_in);
    std::vector<std::string> vector_string_path_completions(std::vector<std::string> vector_in,std::string suffix);
  }
  namespace vars {
    std::vector<std::string>& get_global_variables_list();
    void set_global_variable(std::string key,std::string value);
    std::string get_global_variable(std::string key);
  }
  namespace cache {
    void init_cache(bool enable_smart_cache);
    void store_file(std::string key,fs::path file_path);
    bool cache_contains(std::string key);
    void extract_file(std::string key,fs::path output_path);
    void clean_cache();
    std::string xxhash_to_string(XXH128_hash_t hash);
  }
  namespace actions {
    void check_arg_string(const json& args,std::string field_name);
    void check_arg_vector_string(const json& args,std::string field_name);
    enum class ActionStatus {
      Success,
      Fail,
      SystemError,
      ExitSuccess,
      ExitFail
    };
    class RunSubmoduleAction {
      public:
        RunSubmoduleAction(const json& args,std::string root_folder) {
          char path[PATH_MAX];
          getcwd(path,sizeof(path));
          chdir(root_folder.c_str());
          check_arg_string(args,"file");
          if (!args.contains("wait")) throw std::runtime_error("Wait isn't provided.");
          if (!args["wait"].is_boolean()) throw std::runtime_error("Wait isn't a boolean");
          if (!args.contains("block_if_fail")) throw std::runtime_error("Block if fail isn't provided.");
          if (!args["block_if_fail"].is_boolean()) throw std::runtime_error("Block if fail isn't a boolean");
          this->file_variables=args["file_variables"].get<bool>();
          this->file=args["file"].get<std::string>();
          this->wait=args["wait"].get<bool>();
          this->block_if_fail=args["block_if_fail"].get<bool>();
          chdir(path);
        }
        bool file_variables;
        std::string file;
        bool wait;
        bool block_if_fail;
        ActionStatus run_action();
    };
    class RunCommandWaitAction {
      public:
        RunCommandWaitAction(const json& args) {
          check_arg_vector_string(args,"command");
          if (!args.contains("show_output")) throw std::runtime_error("Show output isn't provided.");
          if (!args["show_output"].is_string()) throw std::runtime_error("Show output isn't a string");
          if (args["show_output"]!="live" && args["show_output"]!="on_failure" && args["show_output"]!="silent") {
            throw std::runtime_error("\""+args["show_output"].get<std::string>()+"\" isn't a valid output mode.");
          }
          if (!args.contains("success_status")) throw std::runtime_error("Success status isn't provided.");
          if (!args["success_status"].is_array()) throw std::runtime_error("Success status isn't a boolean");
          for (auto& arg:args["success_status"]) {
            if (!arg.is_number_integer()) throw std::runtime_error("One of the status code isn't an integer.");
          }
          if (!args.contains("ignore_success_status")) throw std::runtime_error("Ignore success status isn't provided.");
          if (!args["ignore_success_status"].is_boolean()) throw std::runtime_error("Ignore success status isn't a boolean");
          this->command_variables=args["command_variables"].get<bool>();
          this->command=args["command"].get<std::vector<std::string>>();
          this->show_output=args["show_output"].get<std::string>();
          this->success_status=args["success_status"].get<std::vector<int>>();
          this->ignore_success_status=args["ignore_success_status"].get<bool>();
        }
        bool command_variables;
        std::vector<std::string> command;
        std::string show_output;
        std::vector<int> success_status;
        bool ignore_success_status;
        ActionStatus run_action();
    };
    class SaveCommandContentAction {
      public:
        SaveCommandContentAction(const json& args) {
          check_arg_vector_string(args,"command");
          if (!args.contains("success_status")) throw std::runtime_error("Success status isn't provided.");
          if (!args["success_status"].is_array()) throw std::runtime_error("Success status isn't a boolean");
          for (auto& arg:args["success_status"]) {
            if (!arg.is_number_integer()) throw std::runtime_error("One of the status code isn't an integer.");
          }
          if (!args.contains("ignore_success_status")) throw std::runtime_error("Ignore success status isn't provided.");
          if (!args["ignore_success_status"].is_boolean()) throw std::runtime_error("Ignore success status isn't a boolean");
          check_arg_string(args,"command_output_file");
          this->command_variables=args["command_variables"].get<bool>();
          this->command=args["command"].get<std::vector<std::string>>();
          this->success_status=args["success_status"].get<std::vector<int>>();
          this->ignore_success_status=args["ignore_success_status"].get<bool>();
          this->command_output_file_variables=args["command_output_file_variables"].get<bool>();
          this->command_output_file=args["command_output_file"].get<std::string>();
        }
        bool command_variables;
        std::vector<std::string> command;
        std::vector<int> success_status;
        bool ignore_success_status;
        bool command_output_file_variables;
        std::string command_output_file;
        ActionStatus run_action();
    };
    class CompileOneAsmAction {
      public:
        CompileOneAsmAction(const json& args) {
          check_arg_string(args,"source");
          check_arg_string(args,"assembler");
          check_arg_vector_string(args,"pre_arguments");
          check_arg_vector_string(args,"post_arguments");
          check_arg_string(args,"output_file");
          if (!args.contains("success_status")) throw std::runtime_error("Success status isn't provided.");
          if (!args["success_status"].is_array()) throw std::runtime_error("Success status isn't a boolean");
          for (auto& arg:args["success_status"]) {
            if (!arg.is_number_integer()) throw std::runtime_error("One of the status code isn't an integer.");
          }
          if (!args.contains("ignore_success_status")) throw std::runtime_error("Ignore success status isn't provided.");
          if (!args["ignore_success_status"].is_boolean()) throw std::runtime_error("Ignore success status isn't a boolean");
          if (!args.contains("cache_authorized")) throw std::runtime_error("Cache authorized isn't provided.");
          if (!args["cache_authorized"].is_boolean()) throw std::runtime_error("Cache authorized isn't a boolean");
          this->source_variables=args["source_variables"].get<bool>();
          this->assembler_variables=args["assembler_variables"].get<bool>();
          this->pre_arguments_variables=args["pre_arguments_variables"].get<bool>();
          this->post_arguments_variables=args["post_arguments_variables"].get<bool>();
          this->output_file_variables=args["output_file_variables"].get<bool>();
          this->source=args["source"].get<std::string>();
          this->assembler=args["assembler"].get<std::string>();
          this->output_file=args["output_file"].get<std::string>();
          this->pre_arguments=args["pre_arguments"].get<std::vector<std::string>>();
          this->post_arguments=args["post_arguments"].get<std::vector<std::string>>();
          this->success_status=args["success_status"].get<std::vector<int>>();
          this->ignore_success_status=args["ignore_success_status"].get<bool>();
          this->cache_authorized=args["cache_authorized"].get<bool>();
        }
        bool source_variables;
        std::string source;
        bool assembler_variables;
        std::string assembler;
        bool pre_arguments_variables;
        std::vector<std::string> pre_arguments;
        bool post_arguments_variables;
        std::vector<std::string> post_arguments;
        bool output_file_variables;
        std::string output_file;
        std::vector<int> success_status;
        bool ignore_success_status;
        bool cache_authorized;
        ActionStatus run_action();
    };
    class EnsureFolderExistenceAction {
      public:
        EnsureFolderExistenceAction(const json& args) {
          check_arg_string(args,"path");
          this->path_variables=args["path_variables"].get<bool>();
          this->path=args["path"].get<std::string>();
        }
        bool path_variables;
        std::string path;
        ActionStatus run_action();
    };
    class MoveFileAction {
      public:
        MoveFileAction(const json& args) {
          check_arg_string(args,"source_file");
          check_arg_string(args,"destination_folder");
          this->source_file_variables=args["source_file_variables"].get<bool>();
          this->source_file=args["source_file"].get<std::string>();
          this->destination_folder_variables=args["destination_folder_variables"].get<bool>();
          this->destination_folder=args["destination_folder"].get<std::string>();
        }
        bool source_file_variables;
        std::string source_file;
        bool destination_folder_variables;
        std::string destination_folder;
        ActionStatus run_action();
    };
    class CompileOneCppAction {
      public:
        CompileOneCppAction(const json& args) {
          check_arg_string(args,"source");
          check_arg_string(args,"compiler");
          check_arg_vector_string(args,"pre_arguments");
          check_arg_vector_string(args,"post_arguments");
          check_arg_string(args,"output_file");
          check_arg_string(args,"headers_command");
          if (!args.contains("success_status")) throw std::runtime_error("Success status isn't provided.");
          if (!args["success_status"].is_array()) throw std::runtime_error("Success status isn't a boolean");
          for (auto& arg:args["success_status"]) {
            if (!arg.is_number_integer()) throw std::runtime_error("One of the status code isn't an integer.");
          }
          if (!args.contains("ignore_success_status")) throw std::runtime_error("Ignore success status isn't provided.");
          if (!args["ignore_success_status"].is_boolean()) throw std::runtime_error("Ignore success status isn't a boolean");
          if (!args.contains("cache_authorized")) throw std::runtime_error("Cache authorized isn't provided.");
          if (!args["cache_authorized"].is_boolean()) throw std::runtime_error("Cache authorized isn't a boolean");
          this->source_variables=args["source_variables"].get<bool>();
          this->compiler_variables=args["compiler_variables"].get<bool>();
          this->pre_arguments_variables=args["pre_arguments_variables"].get<bool>();
          this->post_arguments_variables=args["post_arguments_variables"].get<bool>();
          this->output_file_variables=args["output_file_variables"].get<bool>();
          this->headers_command_variables=args["headers_command_variables"].get<bool>();
          this->source=args["source"].get<std::string>();
          this->compiler=args["compiler"].get<std::string>();
          this->output_file=args["output_file"].get<std::string>();
          this->pre_arguments=args["pre_arguments"].get<std::vector<std::string>>();
          this->post_arguments=args["post_arguments"].get<std::vector<std::string>>();
          this->success_status=args["success_status"].get<std::vector<int>>();
          this->ignore_success_status=args["ignore_success_status"].get<bool>();
          this->cache_authorized=args["cache_authorized"].get<bool>();
          this->headers_command=args["headers_command"].get<std::string>();
        }
        bool source_variables;
        std::string source;
        bool compiler_variables;
        std::string compiler;
        bool pre_arguments_variables;
        std::vector<std::string> pre_arguments;
        bool post_arguments_variables;
        std::vector<std::string> post_arguments;
        bool output_file_variables;
        std::string output_file;
        std::vector<int> success_status;
        bool ignore_success_status;
        bool cache_authorized;
        bool headers_command_variables;
        std::string headers_command;
        ActionStatus run_action();
    };
    class RunSubmoduleIfAction {
      public:
        RunSubmoduleIfAction(const json& args,std::string root_folder) {
          char path[PATH_MAX];
          getcwd(path,sizeof(path));
          chdir(root_folder.c_str());
          check_arg_string(args,"file");
          if (!args.contains("wait")) throw std::runtime_error("Wait isn't provided.");
          if (!args["wait"].is_boolean()) throw std::runtime_error("Wait isn't a boolean");
          if (!args.contains("block_if_fail")) throw std::runtime_error("Block if fail isn't provided.");
          if (!args["block_if_fail"].is_boolean()) throw std::runtime_error("Block if fail isn't a boolean");
          if (!args.contains("condition")) throw std::runtime_error("Condition isn't provided.");
          if (!args["condition"].is_object()) throw std::runtime_error("Condition isn't a dictionnary.");
          this->condition=args["condition"];
          this->file=args["file"].get<std::string>();
          this->file=fs::absolute(fs::path(args["file"].get<std::string>()));
          this->wait=args["wait"].get<bool>();
          this->block_if_fail=args["block_if_fail"].get<bool>();
          chdir(path);
        }
        json condition;
        bool file_variables;
        std::string file;
        bool wait;
        bool block_if_fail;
        ActionStatus run_action();
    };
    class RunCommandWaitDirAction {
      public:
        RunCommandWaitDirAction(const json& args) {
          check_arg_vector_string(args,"command");
          check_arg_string(args,"working_dir");
          if (!args.contains("show_output")) throw std::runtime_error("Show output isn't provided.");
          if (!args["show_output"].is_string()) throw std::runtime_error("Show output isn't a string");
          if (args["show_output"]!="live" && args["show_output"]!="on_failure" && args["show_output"]!="silent") {
            throw std::runtime_error("\""+args["show_output"].get<std::string>()+"\" isn't a valid output mode.");
          }
          if (!args.contains("success_status")) throw std::runtime_error("Success status isn't provided.");
          if (!args["success_status"].is_array()) throw std::runtime_error("Success status isn't a boolean");
          for (auto& arg:args["success_status"]) {
            if (!arg.is_number_integer()) throw std::runtime_error("One of the status code isn't an integer.");
          }
          if (!args.contains("ignore_success_status")) throw std::runtime_error("Ignore success status isn't provided.");
          if (!args["ignore_success_status"].is_boolean()) throw std::runtime_error("Ignore success status isn't a boolean");
          this->command_variables=args["command_variables"].get<bool>();
          this->command=args["command"].get<std::vector<std::string>>();
          this->show_output=args["show_output"].get<std::string>();
          this->success_status=args["success_status"].get<std::vector<int>>();
          this->ignore_success_status=args["ignore_success_status"].get<bool>();
          this->working_dir_variables=args["working_dir_variables"].get<bool>();
          this->working_dir=args["working_dir"].get<std::string>();
        }
        bool command_variables;
        std::vector<std::string> command;
        std::string show_output;
        std::vector<int> success_status;
        bool ignore_success_status;
        bool working_dir_variables;
        std::string working_dir;
        ActionStatus run_action();
    };
    class CopyFolderAction {
      public:
        CopyFolderAction(const json& args) {
          check_arg_string(args,"source_folder");
          check_arg_string(args,"destination_folder");
          this->source_folder_variables=args["source_folder_variables"].get<bool>();
          this->source_folder=args["source_folder"].get<std::string>();
          this->destination_folder_variables=args["destination_folder_variables"].get<bool>();
          this->destination_folder=args["destination_folder"].get<std::string>();
        }
        bool source_folder_variables;
        std::string source_folder;
        bool destination_folder_variables;
        std::string destination_folder;
        ActionStatus run_action();
    };
    class ExtractFromCacheAction {
      public:
        ExtractFromCacheAction(const json& args) {
          check_arg_string(args,"target");
          check_arg_string(args,"output_file");
          this->target_variables=args["target_variables"].get<bool>();
          this->output_file_variables=args["output_file_variables"].get<bool>();
          this->target=args["target"].get<std::string>();
          this->output_file=args["output_file"].get<std::string>();
        }
        bool target_variables;
        std::string target;
        bool output_file_variables;
        std::string output_file;
        ActionStatus run_action();
    };
    class StoreInCacheAction {
      public:
        StoreInCacheAction(const json& args) {
          check_arg_string(args,"key");
          check_arg_string(args,"file");
          this->key_variables=args["key_variables"].get<bool>();
          this->file_variables=args["file_variables"].get<bool>();
          this->overwrite_allowed=args["overwrite_allowed"].get<bool>();
          this->key=args["key"].get<std::string>();
          this->file=args["file"].get<std::string>();
        }
        bool key_variables;
        std::string key;
        bool file_variables;
        std::string file;
        bool overwrite_allowed;
        ActionStatus run_action();
    };
    class EnsureFolderResetAction {
      public:
        EnsureFolderResetAction(const json& args) {
          check_arg_string(args,"path");
          this->path_variables=args["path_variables"].get<bool>();
          this->path=args["path"].get<std::string>();
        }
        bool path_variables;
        std::string path;
        ActionStatus run_action();
    };
    class RunActionsIfAction {
      public:
        RunActionsIfAction(const json& args,bool is_threading_allowed,std::string root_folder) {
          if (!args.contains("condition")) throw std::runtime_error("Condition isn't provided.");
          if (!args["condition"].is_object()) throw std::runtime_error("Condition isn't a dictionnary.");
          if (!args["actions"].is_array()) throw std::runtime_error("Provided actions aren't stored in a list.");
          for (const auto& act:args["actions"]) {
            if (!act.is_object()) throw std::runtime_error("One of the action isn't a dictionnary.");
            if (!act.contains("action")) throw std::runtime_error("Action isn't provided.");
            if (!act["action"].is_string()) throw std::runtime_error("Action isn't a string.");
            if (find(threaded_actions.begin(),threaded_actions.end(),act["action"].get<std::string>())!=threaded_actions.end() && !is_threading_allowed) throw std::runtime_error("Multithreaded actions aren't allowed in this module, and so can not be executed from RunActionsIfAction.");
            if (!act.contains("args")) throw std::runtime_error("Action arguments aren't provided.");
            if (!act["args"].is_object()) throw std::runtime_error("Action arguments aren't stored in a dictionnary.");
            actions.push_back(act);
          }
          this->condition=args["condition"];
          this->is_threading_allowed=is_threading_allowed;
          this->root_folder=root_folder;
        }
        json condition;
        std::vector<json> actions;
        bool is_threading_allowed;
        std::string root_folder;
        ActionStatus run_action();
    };
    class RunActionsIfElseAction {
      public:
        RunActionsIfElseAction(const json& args,bool is_threading_allowed,std::string root_folder) {
          if (!args.contains("condition")) throw std::runtime_error("Condition isn't provided.");
          if (!args["condition"].is_object()) throw std::runtime_error("Condition isn't a dictionnary.");
          if (!args.contains("actions_if")) throw std::runtime_error("No actions are provided.");
          if (!args["actions_if"].is_array()) throw std::runtime_error("Provided actions aren't stored in a list.");
          for (const auto& act:args["actions_if"]) {
            if (!act.is_object()) throw std::runtime_error("One of the action isn't a dictionnary.");
            if (!act.contains("action")) throw std::runtime_error("Action isn't provided.");
            if (!act["action"].is_string()) throw std::runtime_error("Action isn't a string.");
            if (find(threaded_actions.begin(),threaded_actions.end(),act["action"].get<std::string>())!=threaded_actions.end() && !is_threading_allowed) throw std::runtime_error("Multithreaded actions aren't allowed in this module, and so can not be executed from RunActionsIfAction.");
            if (!act.contains("args")) throw std::runtime_error("Action arguments aren't provided.");
            if (!act["args"].is_object()) throw std::runtime_error("Action arguments aren't stored in a dictionnary.");
            actions_if.push_back(act);
          }
          if (!args.contains("actions_else")) throw std::runtime_error("No actions are provided.");
          if (!args["actions_else"].is_array()) throw std::runtime_error("Provided actions aren't stored in a list.");
          for (const auto& act:args["actions_else"]) {
            if (!act.is_object()) throw std::runtime_error("One of the action isn't a dictionnary.");
            if (!act.contains("action")) throw std::runtime_error("Action isn't provided.");
            if (!act["action"].is_string()) throw std::runtime_error("Action isn't a string.");
            if (find(threaded_actions.begin(),threaded_actions.end(),act["action"].get<std::string>())!=threaded_actions.end() && !is_threading_allowed) throw std::runtime_error("Multithreaded actions aren't allowed in this module, and so can not be executed from RunActionsIfAction.");
            if (!act.contains("args")) throw std::runtime_error("Action arguments aren't provided.");
            if (!act["args"].is_object()) throw std::runtime_error("Action arguments aren't stored in a dictionnary.");
            actions_else.push_back(act);
          }
          this->condition=args["condition"];
          this->is_threading_allowed=is_threading_allowed;
          this->root_folder=root_folder;
        }
        json condition;
        std::vector<json> actions_if;
        std::vector<json> actions_else;
        bool is_threading_allowed;
        std::string root_folder;
        ActionStatus run_action();
    };
    class RunCommandStrAction {
      public:
        RunCommandStrAction(const json& args) {
          check_arg_string(args,"command");
          if (!args.contains("show_output")) throw std::runtime_error("Show output isn't provided.");
          if (!args["show_output"].is_string()) throw std::runtime_error("Show output isn't a string");
          if (args["show_output"]!="live" && args["show_output"]!="on_failure" && args["show_output"]!="silent") {
            throw std::runtime_error("\""+args["show_output"].get<std::string>()+"\" isn't a valid output mode.");
          }
          if (!args.contains("success_status")) throw std::runtime_error("Success status isn't provided.");
          if (!args["success_status"].is_array()) throw std::runtime_error("Success status isn't a boolean");
          for (auto& arg:args["success_status"]) {
            if (!arg.is_number_integer()) throw std::runtime_error("One of the status code isn't an integer.");
          }
          if (!args.contains("ignore_success_status")) throw std::runtime_error("Ignore success status isn't provided.");
          if (!args["ignore_success_status"].is_boolean()) throw std::runtime_error("Ignore success status isn't a boolean");
          this->command_variables=args["command_variables"].get<bool>();
          this->command=args["command"].get<std::string>();
          this->show_output=args["show_output"].get<std::string>();
          this->success_status=args["success_status"].get<std::vector<int>>();
          this->ignore_success_status=args["ignore_success_status"].get<bool>();
        }
        bool command_variables;
        std::string command;
        std::string show_output;
        std::vector<int> success_status;
        bool ignore_success_status;
        ActionStatus run_action();
    };
    class ComputeElementHashAction {
      public:
        ComputeElementHashAction(const json& args) {
          check_arg_string(args,"element");
          if (!args.contains("output_variable")) throw std::runtime_error("Output variable isn't provided.");
          if (!args["output_variable"].is_string()) throw std::runtime_error("Output variable isn't a string.");
          this->element_variables=args["element_variables"].get<bool>();
          this->element=args["element"].get<std::string>();
          this->output_variable=args["output_variable"].get<std::string>();
        }
        bool element_variables;
        std::string element;
        std::string output_variable;
        ActionStatus run_action();
    };
    class CompileMultipleCppAction {
      public:
        CompileMultipleCppAction(const json& args) {
          check_arg_vector_string(args,"source_files");
          check_arg_string(args,"compiler");
          check_arg_vector_string(args,"pre_arguments");
          check_arg_vector_string(args,"post_arguments");
          check_arg_vector_string(args,"output_files");
          check_arg_string(args,"headers_command");
          if (!args.contains("success_status")) throw std::runtime_error("Success status isn't provided.");
          if (!args["success_status"].is_array()) throw std::runtime_error("Success status isn't a boolean");
          for (auto& arg:args["success_status"]) {
            if (!arg.is_number_integer()) throw std::runtime_error("One of the status code isn't an integer.");
          }
          if (!args.contains("ignore_success_status")) throw std::runtime_error("Ignore success status isn't provided.");
          if (!args["ignore_success_status"].is_boolean()) throw std::runtime_error("Ignore success status isn't a boolean");
          if (!args.contains("cache_authorized")) throw std::runtime_error("Cache authorized isn't provided.");
          if (!args["cache_authorized"].is_boolean()) throw std::runtime_error("Cache authorized isn't a boolean");
          if (!args.contains("source_files_path_completion")) throw std::runtime_error("Source files path completion isn't provided.");
          if (!args["source_files_path_completion"].is_boolean()) throw std::runtime_error("Source files path completion isn't a boolean");
          this->source_files_path_completion=args["source_files_path_completion"].get<bool>();
          if (this->source_files_path_completion) {
            if (!args.contains("path_completion_source_ext")) throw std::runtime_error("Path completion sources extension isn't provided");
            if (!args["path_completion_source_ext"].is_string()) throw std::runtime_error("Path completion sources extension isn't a string");
            if (!args.contains("path_completion_output_ext")) throw std::runtime_error("Path completion output extension isn't provided");
            if (!args["path_completion_output_ext"].is_string()) throw std::runtime_error("Path completion output extension isn't a string");
            this->path_completion_source_ext=args["path_completion_source_ext"].get<std::string>();
            this->path_completion_output_ext=args["path_completion_output_ext"].get<std::string>();
          }
          this->source_files_variables=args["source_files_variables"].get<bool>();
          this->compiler_variables=args["compiler_variables"].get<bool>();
          this->pre_arguments_variables=args["pre_arguments_variables"].get<bool>();
          this->post_arguments_variables=args["post_arguments_variables"].get<bool>();
          this->output_files_variables=args["output_files_variables"].get<bool>();
          this->ignore_success_status=args["ignore_success_status"].get<bool>();
          this->cache_authorized=args["cache_authorized"].get<bool>();
          this->headers_command_variables=args["headers_command_variables"].get<bool>();
          this->compiler=args["compiler"].get<std::string>();
          this->headers_command=args["headers_command"].get<std::string>();
          this->source_files=args["source_files"].get<std::vector<std::string>>();
          this->pre_arguments=args["pre_arguments"].get<std::vector<std::string>>();
          this->post_arguments=args["post_arguments"].get<std::vector<std::string>>();
          this->output_files=args["output_files"].get<std::vector<std::string>>();
          this->success_status=args["success_status"].get<std::vector<int>>();
        }
        bool source_files_path_completion;
        bool source_files_variables;
        std::vector<std::string> source_files;
        bool compiler_variables;
        std::string compiler;
        bool pre_arguments_variables;
        std::vector<std::string> pre_arguments;
        bool post_arguments_variables;
        std::vector<std::string> post_arguments;
        bool output_files_variables;
        std::vector<std::string> output_files;
        std::vector<int> success_status;
        bool ignore_success_status;
        bool cache_authorized;
        bool headers_command_variables;
        std::string headers_command;
        std::string path_completion_source_ext;
        std::string path_completion_output_ext;
        ActionStatus run_action();
    };
    class CopyFileAction {
      public:
        CopyFileAction(const json& args) {
          check_arg_string(args,"source_file");
          check_arg_string(args,"destination_folder");
          this->source_file_variables=args["source_file_variables"].get<bool>();
          this->source_file=args["source_file"].get<std::string>();
          this->destination_folder_variables=args["destination_folder_variables"].get<bool>();
          this->destination_folder=args["destination_folder"].get<std::string>();
        }
        bool source_file_variables;
        std::string source_file;
        bool destination_folder_variables;
        std::string destination_folder;
        ActionStatus run_action();
    };
    class CreateFat32Action {
      public:
        CreateFat32Action(const json& args) {
          check_arg_string(args,"key");
          if (!args.contains("size")) throw std::runtime_error("Size isn't provided.");
          if (!args["size"].is_number_unsigned()) throw std::runtime_error("Size isn't an integer.");
          this->key_variables=args["key_variables"].get<bool>();
          this->key=args["key"].get<std::string>();
          this->size=args["size"].get<size_t>();
        }
        bool key_variables;
        std::string key;
        size_t size;
        ActionStatus run_action();
    };
    class CreateFolderFat32Action {
      public:
        CreateFolderFat32Action(const json& args) {
          check_arg_string(args,"key");
          check_arg_string(args,"full_path");
          this->key_variables=args["key_variables"].get<bool>();
          this->key=args["key"].get<std::string>();
          this->full_path_variables=args["full_path_variables"].get<bool>();
          this->full_path=args["full_path"].get<std::string>();
        }
        bool key_variables;
        std::string key;
        bool full_path_variables;
        std::string full_path;
        ActionStatus run_action();
    };
    class CreateFileFat32Action {
      public:
        CreateFileFat32Action(const json& args) {
          check_arg_string(args,"key");
          check_arg_string(args,"source_path");
          check_arg_string(args,"full_path");
          if (args.contains("is_vftm_target")) {
            if (!args["is_vftm_target"].is_boolean()) throw std::runtime_error("Is VFTM target isn't an boolean.");
            this->is_vftm_target=args["is_vftm_target"].get<bool>();
          }
          this->key_variables=args["key_variables"].get<bool>();
          this->key=args["key"].get<std::string>();
          this->source_path_variables=args["source_path_variables"].get<bool>();
          this->source_path=args["source_path"].get<std::string>();
          this->full_path_variables=args["full_path_variables"].get<bool>();
          this->full_path=args["full_path"].get<std::string>();
        }
        bool is_vftm_target=false;
        bool key_variables;
        std::string key;
        bool source_path_variables;
        std::string source_path;
        bool full_path_variables;
        std::string full_path;
        ActionStatus run_action();
    };
    class ExportFat32Action {
      public:
        ExportFat32Action(const json& args) {
          check_arg_string(args,"key");
          check_arg_string(args,"export_path");
          if (!args.contains("generate_vftm")) throw std::runtime_error("Generate VFTM isn't provided.");
          if (!args["generate_vftm"].is_boolean()) throw std::runtime_error("Generate VFTM isn't a boolean.");
          this->generate_vftm=args["generate_vftm"].get<bool>();
          if (this->generate_vftm) {
            if (!args.contains("disk_guid_var")) throw std::runtime_error("Disk GUID variable must be provided when generate_vftm is true.");
            if (!args["disk_guid_var"].is_string()) throw std::runtime_error("Disk GUID variable must be a string.");
            if (!args.contains("efi_part_unique_guid_var")) throw std::runtime_error("EFI partition unique GUID variable must be provided when generate_vftm is true.");
            if (!args["efi_part_unique_guid_var"].is_string()) throw std::runtime_error("EFI partition unique GUID variable must be a boolean.");
            this->disk_guid_var=args["disk_guid_var"].get<std::string>();
            this->efi_part_unique_guid_var=args["efi_part_unique_guid_var"].get<std::string>();
          }
          this->key_variables=args["key_variables"].get<bool>();
          this->key=args["key"].get<std::string>();
          this->export_path_variables=args["export_path_variables"].get<bool>();
          this->export_path=args["export_path"].get<std::string>();
        }
        bool key_variables;
        std::string key;
        bool export_path_variables;
        std::string export_path;
        bool generate_vftm;
        std::string disk_guid_var;
        std::string efi_part_unique_guid_var;
        ActionStatus run_action();
    };
    class CreateDiskAction {
      public:
        CreateDiskAction(const json& args) {
          check_arg_string(args,"key");
          check_arg_string(args,"disk_guid");
          if (!args.contains("size")) throw std::runtime_error("Size isn't provided.");
          if (!args["size"].is_number_unsigned()) throw std::runtime_error("Size isn't an integer.");
          this->key_variables=args["key_variables"].get<bool>();
          this->key=args["key"].get<std::string>();
          this->disk_guid_variables=args["disk_guid_variables"].get<bool>();
          this->disk_guid=args["disk_guid"].get<std::string>();
          this->size=args["size"].get<size_t>();
        }
        bool key_variables;
        std::string key;
        size_t size;
        bool disk_guid_variables;
        std::string disk_guid;
        ActionStatus run_action();
    };
    class AddPartitionDiskAction {
      public:
        AddPartitionDiskAction(const json& args) {
          check_arg_string(args,"key");
          check_arg_string(args,"type_guid");
          check_arg_string(args,"unique_guid");
          check_arg_string(args,"name");
          check_arg_string(args,"source_file");
          this->key_variables=args["key_variables"].get<bool>();
          this->key=args["key"].get<std::string>();
          this->type_guid_variables=args["type_guid_variables"].get<bool>();
          this->type_guid=args["type_guid"].get<std::string>();
          this->unique_guid_variables=args["unique_guid_variables"].get<bool>();
          this->unique_guid=args["unique_guid"].get<std::string>();
          this->name_variables=args["name_variables"].get<bool>();
          this->name=args["name"].get<std::string>();
          this->source_file_variables=args["source_file_variables"].get<bool>();
          this->source_file=args["source_file"].get<std::string>();
        }
        bool key_variables;
        std::string key;
        bool type_guid_variables;
        std::string type_guid;
        bool unique_guid_variables;
        std::string unique_guid;
        bool name_variables;
        std::string name;
        bool source_file_variables;
        std::string source_file;
        ActionStatus run_action();
    };
    class ExportDiskAction {
      public:
        ExportDiskAction(const json& args) {
          check_arg_string(args,"key");
          check_arg_string(args,"export_path");
          this->key_variables=args["key_variables"].get<bool>();
          this->key=args["key"].get<std::string>();
          this->export_path_variables=args["export_path_variables"].get<bool>();
          this->export_path=args["export_path"].get<std::string>();
        }
        bool key_variables;
        std::string key;
        bool export_path_variables;
        std::string export_path;
        ActionStatus run_action();
    };
    class VFTMLoadCryptoLibAction {
      public:
        VFTMLoadCryptoLibAction(const json& args) {
          check_arg_string(args,"lib_path");
          this->lib_path_variables=args["lib_path_variables"].get<bool>();
          this->lib_path=args["lib_path"].get<std::string>();
        }
        bool lib_path_variables;
        std::string lib_path;
        ActionStatus run_action();
    };
    class VFTMRunKeysGeneratorAction {
      public:
        VFTMRunKeysGeneratorAction(const json& args) {
          check_arg_vector_string(args,"command");
          check_arg_string(args,"working_dir");
          check_arg_string(args,"socket_path");
          if (!args.contains("success_status")) throw std::runtime_error("Success status isn't provided.");
          if (!args["success_status"].is_array()) throw std::runtime_error("Success status isn't a boolean");
          for (auto& arg:args["success_status"]) {
            if (!arg.is_number_integer()) throw std::runtime_error("One of the status code isn't an integer.");
          }
          if (!args.contains("ignore_success_status")) throw std::runtime_error("Ignore success status isn't provided.");
          if (!args["ignore_success_status"].is_boolean()) throw std::runtime_error("Ignore success status isn't a boolean");
          this->command_variables=args["command_variables"].get<bool>();
          this->command=args["command"].get<std::vector<std::string>>();
          this->socket_path_variables=args["socket_path_variables"].get<bool>();
          this->socket_path=args["socket_path"].get<std::string>();
          this->success_status=args["success_status"].get<std::vector<int>>();
          this->ignore_success_status=args["ignore_success_status"].get<bool>();
          this->working_dir_variables=args["working_dir_variables"].get<bool>();
          this->working_dir=args["working_dir"].get<std::string>();
        }
        bool command_variables;
        std::vector<std::string> command;
        bool socket_path_variables;
        std::string socket_path;
        std::vector<int> success_status;
        bool ignore_success_status;
        bool working_dir_variables;
        std::string working_dir;
        ActionStatus run_action();
    };
    using ParallelAction=std::variant<RunCommandWaitAction,
                                      SaveCommandContentAction,
                                      CompileOneAsmAction,
                                      EnsureFolderExistenceAction,
                                      MoveFileAction,
                                      CompileOneCppAction,
                                      RunCommandWaitDirAction,
                                      CopyFolderAction,
                                      ExtractFromCacheAction,
                                      StoreInCacheAction,
                                      EnsureFolderResetAction,
                                      RunActionsIfAction,
                                      RunCommandStrAction,
                                      RunActionsIfElseAction,
                                      ComputeElementHashAction,
                                      CopyFileAction,
                                      CreateFat32Action,
                                      CreateFolderFat32Action,
                                      CreateFileFat32Action,
                                      ExportFat32Action,
                                      CreateDiskAction,
                                      AddPartitionDiskAction,
                                      ExportDiskAction,
                                      VFTMLoadCryptoLibAction,
                                      VFTMRunKeysGeneratorAction>;
    ParallelAction return_pararrel_action(const json& args,std::string action_name);
    class RunActionsParallelAction {
      public:
        RunActionsParallelAction (const json& args) {
          if (!args.contains("actions")) throw std::runtime_error("No actions are provided.");
          if (!args["actions"].is_array()) throw std::runtime_error("Provided actions aren't stored in a list.");
          for (const auto& act:args["actions"]) {
            if (!act.is_object()) throw std::runtime_error("One of the action isn't a dictionnary.");
            if (!act.contains("action")) throw std::runtime_error("Action isn't provided.");
            if (!act["action"].is_string()) throw std::runtime_error("Action isn't a string.");
            if (!act.contains("args")) throw std::runtime_error("Action arguments aren't provided.");
            if (!act["args"].is_object()) throw std::runtime_error("Action arguments aren't stored in a dictionnary.");
            actions.push_back(return_pararrel_action(act["args"],act["action"]));
          }
        }
        std::vector<ParallelAction> actions;
        ActionStatus run_action();
    };
    class RunSubmoduleParallelAction {
      public:
        RunSubmoduleParallelAction(const json& args) {
          check_arg_vector_string(args,"submodules");
          for (const auto& act:args["submodules"]) {
            submodules.push_back(act.get<std::string>());
          }
          this->submodules_variables=args["submodules_variables"].get<bool>();
        }
        bool submodules_variables;
        std::vector<std::string> submodules;
        ActionStatus run_action();
    };
    class VyldCompilationAction {
      public:
        VyldCompilationAction(const json& args) {
          check_arg_vector_string(args,"source_files");
          check_arg_vector_string(args,"source_asm");
          check_arg_string(args,"compiler");
          check_arg_string(args,"assembler");
          check_arg_string(args,"linker");
          check_arg_string(args,"compiler_flags");
          check_arg_string(args,"output_file");
          check_arg_string(args,"headers_command");
          if (!args.contains("source_files_path_completion")) throw std::runtime_error("Source file path completion isn't provided.");
          if (!args["source_files_path_completion"].is_boolean()) throw std::runtime_error("Source file path completion isn't a boolean.");
          if (!args.contains("source_asm_path_completion")) throw std::runtime_error("Source ASM path completion isn't provided.");
          if (!args["source_asm_path_completion"].is_boolean()) throw std::runtime_error("Source ASM path completion isn't a boolean.");
          if (!args.contains("block_if_fail")) throw std::runtime_error("Block if fail isn't provided.");
          if (!args["block_if_fail"].is_boolean()) throw std::runtime_error("Block if fail isn't a boolean.");
          if (!args.contains("object_files_cache_authorized")) throw std::runtime_error("Object files caching authorized isn't provided.");
          if (!args["object_files_cache_authorized"].is_boolean()) throw std::runtime_error("Object files caching authorized isn't a boolean.");
          if (!args.contains("payloads")) throw std::runtime_error("Payloads aren't provided.");
          if (!args["payloads"].is_array()) throw std::runtime_error("Payloads aren't stored in a list.");
          this->source_files_path_completion=args["source_files_path_completion"].get<bool>();
          this->source_asm_path_completion=args["source_asm_path_completion"].get<bool>();
          this->source_files_variables=args["source_files_variables"].get<bool>();
          this->source_asm_variables=args["source_asm_variables"].get<bool>();
          this->compiler_variables=args["compiler_variables"].get<bool>();
          this->assembler_variables=args["assembler_variables"].get<bool>();
          this->linker_variables=args["linker_variables"].get<bool>();
          this->compiler_flags_variables=args["compiler_flags_variables"].get<bool>();
          this->output_file_variables=args["output_file_variables"].get<bool>();
          this->block_if_fail=args["block_if_fail"].get<bool>();
          this->object_files_cache_authorized=args["object_files_cache_authorized"].get<bool>();
          this->headers_command_variables=args["headers_command_variables"].get<bool>();
          this->source_files=args["source_files"].get<std::vector<std::string>>();
          this->source_asm=args["source_asm"].get<std::vector<std::string>>();
          this->compiler=args["compiler"].get<std::string>();
          this->assembler=args["assembler"].get<std::string>();
          this->linker=args["linker"].get<std::string>();
          this->compiler_flags=args["compiler_flags"].get<std::string>();
          this->output_file=args["output_file"].get<std::string>();
          this->headers_command=args["headers_command"].get<std::string>();
          this->payloads=args["payloads"].get<std::vector<json>>();
        }
        bool source_files_path_completion;
        bool source_files_variables;
        std::vector<std::string> source_files;
        bool compiler_variables;
        std::string compiler;
        bool source_asm_path_completion;
        bool source_asm_variables;
        std::vector<std::string> source_asm;
        bool assembler_variables;
        std::string assembler;
        bool linker_variables;
        std::string linker;
        bool compiler_flags_variables;
        std::string compiler_flags;
        bool output_file_variables;
        std::string output_file;
        bool block_if_fail;
        bool object_files_cache_authorized;
        bool headers_command_variables;
        std::string headers_command;
        std::vector<json> payloads;
        ActionStatus run_action();
    };
    using Action=std::variant<RunSubmoduleAction,
                              RunCommandWaitAction,
                              RunActionsParallelAction,
                              SaveCommandContentAction,
                              CompileOneAsmAction,
                              EnsureFolderExistenceAction,
                              MoveFileAction,
                              CompileOneCppAction,
                              VyldCompilationAction,
                              RunSubmoduleIfAction,
                              RunCommandWaitDirAction,
                              CopyFolderAction,
                              RunSubmoduleParallelAction,
                              ExtractFromCacheAction,
                              StoreInCacheAction,
                              EnsureFolderResetAction,
                              RunActionsIfAction,
                              RunCommandStrAction,
                              RunActionsIfElseAction,
                              ComputeElementHashAction,
                              CompileMultipleCppAction,
                              CopyFileAction,
                              CreateFat32Action,
                              CreateFolderFat32Action,
                              CreateFileFat32Action,
                              ExportFat32Action,
                              CreateDiskAction,
                              AddPartitionDiskAction,
                              ExportDiskAction,
                              VFTMLoadCryptoLibAction,
                              VFTMRunKeysGeneratorAction>;
    Action return_action(const json &args,std::string action_name,bool is_threading_allowed,std::string root_folder);
  }
  namespace module {
    class Module {
      public:
        Module(std::string file,bool is_threading_allowed) {
          std::ifstream f(file);
          if (!f) {
            throw std::runtime_error(file+" couldn't be opened.");
          }
          json j=json::parse(f);
          if (!j.contains("version")) throw std::runtime_error("Syntax version isn't provided.");
          if (!j["version"].is_string()) throw std::runtime_error("Syntax version isn't a string.");
          if (j["version"]!=syntax_version) throw std::runtime_error("Syntax version is "+j["version"].get<std::string>()+". Expected "+syntax_version+".");
          if (j.contains("root_folder")) {
            if (!j["root_folder"].is_string()) throw std::runtime_error("Root folder isn't a string.");
            this->root_folder=vybuild::parser::root_folder_parsing(j["root_folder"].get<std::string>(),file);
          } else {
            this->root_folder=vybuild::parser::root_folder_parsing("%filefolder%",file);
          }
          if (!j.contains("run_context")) throw std::runtime_error("Run context isn't provided.");
          if (!j["run_context"].is_string()) throw std::runtime_error("Run context isn't a string.");
          if (j["run_context"]!="root" && j["run_context"]!="sub" && j["run_context"]!="both") throw std::runtime_error("\""+j["run_context"].get<std::string>()+"\" isn't a string.");
          this->run_context=j["run_context"].get<std::string>();
          if (!j.contains("name")) throw std::runtime_error("Name isn't provided.");
          if (!j["name"].is_string()) throw std::runtime_error("Name isn't a string.");
          this->name=j["name"].get<std::string>();
          if (this->run_context=="root" && j.contains("global_vars")) {
            if (!j["global_vars"].is_object()) throw std::runtime_error("Global variables aren't in a list.");
            for (const auto& [var_name,value]:j["global_vars"].items()) {
              vybuild::vars::set_global_variable("\\$"+var_name+"\\$",value);
            }
          } else if (this->run_context!="root" && j.contains("global_vars")){
            throw std::runtime_error("Global variables are only available for modules that are guaranteed to be runned as root.");
          }
          if (this->run_context=="root" && j.contains("global_uuid")) {
            if (!j["global_uuid"].is_array()) throw std::runtime_error("Global UUID aren't in a list.");
            for (const auto& var_name:j["global_uuid"].items()) {
              uuid_t id;
              uuid_generate_random(id);
              char out[37];
              uuid_unparse_lower(id,out);
              vybuild::vars::set_global_variable("\\$"+var_name.value().get<std::string>()+"\\$",std::string(out));
            }
          } else if (this->run_context!="root" && j.contains("global_uuid")){
            throw std::runtime_error("Global UUID are only available for modules that are guaranteed to be runned as root.");
          }
          if (!j.contains("actions")) throw std::runtime_error("Couldn't find any action inside module.");
          if (!j["actions"].is_array()) throw std::runtime_error("Actions aren't in a list.");
          for (const auto& act:j["actions"]) {
            if (!act.is_object()) throw std::runtime_error("One of the action isn't a dictionnary.");
            if (!act.contains("action")) throw std::runtime_error("Action isn't provided.");
            if (!act["action"].is_string()) throw std::runtime_error("Action isn't a string.");
            if (!act.contains("args")) throw std::runtime_error("Action arguments aren't provided.");
            if (!act["args"].is_object()) throw std::runtime_error("Action arguments aren't stored in a dictionnary.");
            actions.push_back(vybuild::actions::return_action(act["args"],act["action"],is_threading_allowed,this->root_folder));
          }
        }
        std::string root_folder;
        std::string run_context;
        std::string name;
        std::vector<vybuild::actions::Action> actions;
        vybuild::actions::ActionStatus run_module(bool is_root,bool is_threaded) {
          if (this->run_context=="root" && !is_root) {
            throw std::runtime_error("Module isn't supposed to be ran as a submodule.");
          } else if (this->run_context=="sub" && is_root) {
            throw std::runtime_error("Module isn't supposed to be ran as root module.");
          }
          if (is_threaded) unshare(CLONE_FS);
          char path[PATH_MAX];
          getcwd(path,sizeof(path));
          chdir(this->root_folder.c_str());
          for (auto& a:actions) {
            vybuild::actions::ActionStatus status=std::visit([](auto& act) {
              return act.run_action();
            },a);
            if (status!=vybuild::actions::ActionStatus::Success && status!=vybuild::actions::ActionStatus::ExitSuccess) {
              chdir(path);
              return status;
            }
            if (status==vybuild::actions::ActionStatus::ExitSuccess) {
              chdir(path);
              return vybuild::actions::ActionStatus::Success;
            }
          }
          chdir(path);
          return vybuild::actions::ActionStatus::Success;
        }
    };
  }
}
#endif
