#include <utility>

#include "regexis024_build_system.h"

std::vector<std::string> getFromPkgConfig(const std::string& req, const std::string& name){
    std::string pc_stdout, pc_stderr;
    CommandReturnCode rc = executeCommand_and_save_output({"pkg-config", "--" + req, name}, pc_stdout, pc_stderr);
    ASSERT(rc.isOk(), "failed to use pkg-config beacause of:\n" + pc_stderr);
    // todo: learn how pkg-config actually stores these options
    std::vector<std::string> result;
    for (char ch: pc_stdout) {
        if (result.empty())
            result.emplace_back();
        if (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r') {
            if (!result.back().empty())
                result.emplace_back();
        } else {
            result.back() += ch;
        }
    }
    if (!result.empty() && result.back().empty())
        result.pop_back();
    return result;
}

ExternalLibraryTarget formExternalLibraryTargetWithNativeName(const std::string& name) {
    return {name, {getFromPkgConfig("cflags", name), getFromPkgConfig("libs", name)}};
}

struct CAWebChat {
    /* Building runlevel */
    BuildUnitsArray runlevel_1;
    /* Installation runlevel */
    BuildUnitsArray runlevel_2;

    std::string build_type;
    bool build_tests = false;

    std::vector<std::string> warning_flags = {"-Wall", "-Wno-unused-variable", "-Werror=return-type","-pedantic",
        "-Wno-unused-but-set-variable", "-Wno-reorder"};
    std::vector<std::string> version_flags = {"--std", "c++14", "-D", "_POSIX_C_SOURCE=200809L"};
    std::vector<std::string> debug_defines_release = {"_GLIBCXX_DEBUG"};
    std::vector<std::string> debug_defines_debug = {"_GLIBCXX_DEBUG", "DEBUG_ALLOW_LOUD"};
    std::vector<std::string> opt_flags_release = {"-g", "-O2"};
    std::vector<std::string> opt_flags_debug = {"-g", "-ggdb", "-O0"};

    std::vector<std::string> getSomeRadFlags() const {
        std::vector<std::string> my_flag_collection;
        gxx_add_cli_options(my_flag_collection, warning_flags);
        gxx_add_cli_options(my_flag_collection, version_flags);
        if (build_type == "release") {
            gxx_add_cli_defines(my_flag_collection, debug_defines_release);
            gxx_add_cli_options(my_flag_collection, opt_flags_release);
        } else if (build_type == "debug") {
            gxx_add_cli_defines(my_flag_collection, debug_defines_debug);
            gxx_add_cli_options(my_flag_collection, opt_flags_debug);
        }
        return my_flag_collection;
    }

    CAWebChat(const std::string& _build_type, bool _build_tests, const NormalCBuildSystemCommandMeaning& cmd)
        : build_type(_build_type), build_tests(_build_tests)
    {
        ASSERT(build_type == "release" || build_type == "debug", "Unknown build type");

        std::vector<ExternalLibraryTarget> ext_targets = {
            formExternalLibraryTargetWithNativeName("libjsonincpp"),
            formExternalLibraryTargetWithNativeName("sqlite3"),
            formExternalLibraryTargetWithNativeName("libregexis024"),
        };

        std::vector<CTarget> my_targets;

        { CTarget T{"engine_engine_number_9", "shared_library"};
            T.additional_compilation_flags = getSomeRadFlags();
            T.proj_deps = {};
            T.external_deps = {
                CTargetDependenceOnExternalLibrary{"libjsonincpp", {true, true}},
                CTargetDependenceOnExternalLibrary{"libregexis024", {true, true}}
            };
            T.units = {
                "baza.cpp",
                "thread_synchronization.cpp",
                "os_utils.cpp",
                "http_structures/client_request_parse.cpp",
                "http_structures/response_gen.cpp",
                "connecting_assets/static_asset_manager.cpp",
                "running_mainloop.cpp",
                "form_data_structure/urlencoded_query.cpp",
            };
            for (std::string& u: T.units)
                u = "http_server/engine_engine_number_9/" + u;
            T.include_pr = "http_server";
            T.include_ir = "";
            T.exported_headers = {
                "baza.h",
                "baza_throw.h",
                "thread_synchronization.h",
                "os_utils.h",
                "connecting_assets/static_asset_manager.h",
                "http_structures/client_request.h",
                "http_structures/response_gen.h",
                "running_mainloop.h",
                "form_data_structure/urlencoded_query.h",
            };
            for (std::string& u: T.exported_headers)
                u = "engine_engine_number_9/" + u;

            T.installation_dir = "";
            my_targets.push_back(T);
        }
        { CTarget T{"new_york_transit_line", "shared_library"};
            T.additional_compilation_flags = getSomeRadFlags();
            T.external_deps = {
                CTargetDependenceOnExternalLibrary{"libjsonincpp", {true, true}},
            };
            T.units = {
                "alotalot.cpp",
                "execute_expression.cpp",
                "html_case.cpp",
                "parser.cpp",
                "rendering.cpp",
                "templater.cpp",
            };
            for (std::string& u: T.units)
                u = "http_server/new_york_transit_line/" + u;
            T.include_pr = "http_server";
            T.exported_headers = {
                "templater.h",
                "html_case.h",
            };
            for (std::string& u: T.exported_headers)
                u = "new_york_transit_line/" + u;
            my_targets.push_back(T);
        }
        { CTarget T{"iu9-ca-web-chat", "executable"};
            T.additional_compilation_flags = getSomeRadFlags();
            T.proj_deps = {
                CTargetDependenceOnProjectsLibrary{"engine_engine_number_9"},
                CTargetDependenceOnProjectsLibrary{"new_york_transit_line"},
            };
            T.external_deps = {
                CTargetDependenceOnExternalLibrary{"sqlite3"}
            };
            T.units = {"main.cpp"};
            for (std::string& u: T.units)
                u = "web_chat/" + u;
            T.include_pr = "web_chat";
            T.installation_dir = "";
            my_targets.push_back(T);
        }
        regular_ctargets_to_2bus_conversion(ext_targets, my_targets, runlevel_1, runlevel_2,
            cmd.project_root, cmd.installation_root);
    }
};

int main(int argc, char** argv) {
    try {
        ASSERT_pl(argc > 0);
        std::vector<std::string> args(argc - 1);
        for (int i = 0; i + 1 < argc; i++) {
            args[i] = argv[i + 1];
        }
        NormalCBuildSystemCommandMeaning cmd;
        regular_bs_cli_cmd_interpret(args, cmd);
        const char* BS_SCRIPT_TYPE = getenv("BS_SCRIPT_TYPE");
        const char* BS_SCRIPT_TESTS = getenv("BS_SCRIPT_TESTS");
        CAWebChat bs(BS_SCRIPT_TYPE ? BS_SCRIPT_TYPE : "release", (bool)BS_SCRIPT_TESTS, cmd);
        if (cmd.need_to_build)
            complete_tasks_of_build_units(bs.runlevel_1);
        umask(~0755);
        if (cmd.need_to_install)
            complete_tasks_of_build_units(bs.runlevel_2);
    } catch (const buildSystemFailure& e) {
        printf("Build system failure\n""%s\n", e.toString().c_str());
    }
}