#include <iostream>
#include <unordered_map>
#include <string>

// This program generates an mconfig.h file. It is used in the build process.

// Map of variable name to value. Variables are passed via command line and stored
// in this map.
std::unordered_map<std::string, std::string> vars;

char to_hex_digit(int i)
{
    if (i < 10) return i + '0';
    return i - 10 + 'A';
}

// turn a string into a C++-source string, eg:
//     he said "hello"
//  becomes
//    "he said \"hello\""
static std::string stringify(std::string a)
{
    std::string out = "\"";

    for (std::string::size_type i = 0; i < a.length(); i++) {
        char c = a[i];
        if (c == '\n') out += "\\n";
        else if (c == '\t') out += "\\t";
        else if (c == '\"') out += "\\\"";
        else if (c < 0x20) {
            out += "\\x" ;
            out += to_hex_digit((c & 0xF0) >> 4);
            out += to_hex_digit((c & 0x0F));
        }
        else out += c;
    }

    out += "\"";
    return out;
}

// parse a NAME=VALUE argument and store in the variable map
void parse_arg(std::string arg)
{
    auto idx = arg.find("=", 0, 1);
    if (idx == std::string::npos) {
        throw std::string("Couldn't parse argument: ") + arg;
    }

    auto name = arg.substr(0, idx);
    auto value = arg.substr(idx + 1);
    vars.emplace(std::move(name), std::move(value));
}

int main(int argc, char **argv)
{
    for (int i = 1; i < argc; i++) {
        parse_arg(argv[i]);
    }

    using namespace std;
    cout << "// This file is auto-generated by mconfig-gen.cc.\n\n";
    cout << "#ifndef DINIT_MCONFIG_H\n";
    cout << "#define DINIT_MCONFIG_H 1\n";

    cout << "\n// Defines\n\n";
    if (vars.find("USE_UTMPX") != vars.end()) {
        cout << "#define USE_UTMPX " << vars["USE_UTMPX"] << "\n";
    }
    if (vars.find("USE_INITGROUPS") != vars.end()) {
        cout << "#define USE_INITGROUPS " << vars["USE_INITGROUPS"] << "\n";
    }
    else {
    	// Default to assuming initgroups(3) is available
    	cout << "#define USE_INITGROUPS 1\n";
    }
    if (vars.find("SUPPORT_CGROUPS") != vars.end()) {
        cout << "#define SUPPORT_CGROUPS " << vars["SUPPORT_CGROUPS"] << "\n";
    }
    if (vars.find("SUPPORT_CAPABILITIES") != vars.end()) {
        cout << "#define SUPPORT_CAPABILITIES " << vars["SUPPORT_CAPABILITIES"] << "\n";
    }
    if (vars.find("SUPPORT_IOPRIO") != vars.end()) {
        cout << "#define SUPPORT_IOPRIO " << vars["SUPPORT_IOPRIO"] << "\n";
    }
    if (vars.find("SUPPORT_OOM_ADJ") != vars.end()) {
        cout << "#define SUPPORT_OOM_ADJ " << vars["SUPPORT_OOM_ADJ"] << "\n";
    }
    if (vars.find("DEFAULT_AUTO_RESTART") != vars.end()) {
        cout << "#define DEFAULT_AUTO_RESTART " << vars["DEFAULT_AUTO_RESTART"] << "\n";
    }

    cout << "\n// Constants\n";
    cout << "\nconstexpr static char DINIT_VERSION[] = " << stringify(vars["VERSION"]) << ";\n";
    cout << "constexpr static char SYSCONTROLSOCKET[] = " << stringify(vars["SYSCONTROLSOCKET"]) << ";\n";
    cout << "constexpr static char BINDIR[] = " << stringify(vars["BINDIR"]) << ";\n";
    cout << "constexpr static char SBINDIR[] = " << stringify(vars["SBINDIR"]) << ";\n";
    cout << "constexpr static char SHUTDOWN_PREFIX[] = " << stringify(vars["SHUTDOWN_PREFIX"]) << ";\n";

    if (vars.find("DEFAULT_START_TIMEOUT") != vars.end()) {
        cout << "constexpr static unsigned DEFAULT_START_TIMEOUT = " << vars["DEFAULT_START_TIMEOUT"] << ";\n";
    }
    else {
        cout << "constexpr static unsigned DEFAULT_START_TIMEOUT = 60;\n";
    }

    if (vars.find("DEFAULT_STOP_TIMEOUT") != vars.end()) {
        cout << "constexpr static unsigned DEFAULT_STOP_TIMEOUT = " << vars["DEFAULT_STOP_TIMEOUT"] << ";\n";
    }
    else {
        cout << "constexpr static unsigned DEFAULT_STOP_TIMEOUT = 10;\n";
    }

    cout << "\n#endif // DINIT_MCONFIG_H\n";
    return 0;
}
