BareGit
#include "engine_worker.hpp"

#include "protocol.hpp"
#include "window_adapter.hpp"

#include <cstdio>
#include <cstdlib>
#include <filesystem>
#include <stdexcept>
#include <string>
#include <unistd.h>
#include <vector>

extern "C"
{
int nhmain(int argc, char* argv[]);
}

namespace nethack_mcp
{

namespace
{

int findIntegerArgument(int argc, char* argv[], const char* name)
{
    for(int index = 0; index + 1 < argc; ++index)
    {
        if(std::string(argv[index]) == name)
        {
            return std::stoi(argv[index + 1]);
        }
    }
    return -1;
}

} // namespace

int runEngineWorker(int argc, char* argv[])
{
    const int descriptor = findIntegerArgument(argc, argv, "--ipc-fd");
    if(descriptor < 0)
    {
        std::fputs("--engine requires --ipc-fd\n", stderr);
        return 2;
    }

    try
    {
        FramedChannel channel(descriptor);
        Json hello = {
            {"type", "hello"},
            {"ipc_version", IPC_VERSION},
            {"game_id", nullptr},
            {"nethack_commit",
             "c94fd5225beef48143244bfb7bc42682aad58741"},
            {"lua_version", "5.4.9"},
        };
        std::string error;
        if(!channel.send(hello, error))
        {
            throw std::runtime_error(error);
        }

        Json start;
        if(!channel.receive(start, error)
           || start.value("type", "") != "start")
        {
            throw std::runtime_error(
                error.empty() ? "worker did not receive start" : error);
        }

        const std::string game_id = start.at("game_id").get<std::string>();
        const std::filesystem::path run_directory =
            start.at("run_dir").get<std::string>();
        const std::string player_name =
            start.value("name", std::string("Agent"));

        std::filesystem::current_path(run_directory);
        setenv("HOME", run_directory.c_str(), 1);
        setenv("HACKDIR", run_directory.c_str(), 1);
        setenv("NETHACKDIR", run_directory.c_str(), 1);
        const Json character = start.value("character", Json::object());
        std::string options = "showexp,showscore,time";
        for(const char* key : {"role", "race", "gender", "alignment"})
        {
            if(character.contains(key))
            {
                options += ",";
                options += key;
                options += ":";
                options += character.at(key).get<std::string>();
            }
        }
        setenv("NETHACKOPTIONS", options.c_str(), 1);

        WindowAdapter adapter(channel, game_id);
        adapter.install();

        char program[] = "nethack_mcp";
        char name_option[] = "-u";
        std::vector<char> name(player_name.begin(), player_name.end());
        name.push_back('\0');
        char* nethack_argv[] = {program, name_option, name.data(), nullptr};
        return nhmain(3, nethack_argv);
    }
    catch(const std::exception& exception)
    {
        std::fprintf(stderr, "NetHack worker failed: %s\n",
                     exception.what());
        return 2;
    }
}

} // namespace nethack_mcp