#include "libretro_frontend.h"

#include <android/log.h>
#include <android/native_window.h>
#include <dlfcn.h>
#include <chrono>
#include <cstdarg>
#include <cstring>
#include <fstream>
#include <vector>

#define LOG_TAG "LibretroFrontend"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)

LibretroFrontend& LibretroFrontend::instance() {
    static LibretroFrontend inst;
    return inst;
}

LibretroFrontend::~LibretroFrontend() {
    stop();
    unloadCore();
    releaseWindow();
}

// --- Core Loading ---

bool LibretroFrontend::loadCore(const char* soPath) {
    unloadCore();

    LOGI("Loading core: %s", soPath);
    core_handle_ = dlopen(soPath, RTLD_LAZY);
    if (!core_handle_) {
        LOGE("dlopen failed: %s", dlerror());
        return false;
    }

    #define LOAD_SYM(field, name) \
        field = (decltype(field))dlsym(core_handle_, #name); \
        if (!field) { LOGE("Missing symbol: %s", #name); unloadCore(); return false; }

    LOAD_SYM(core_init_, retro_init);
    LOAD_SYM(core_deinit_, retro_deinit);
    LOAD_SYM(core_api_version_, retro_api_version);
    LOAD_SYM(core_get_system_info_, retro_get_system_info);
    LOAD_SYM(core_get_system_av_info_, retro_get_system_av_info);
    LOAD_SYM(core_set_environment_, retro_set_environment);
    LOAD_SYM(core_set_video_refresh_, retro_set_video_refresh);
    LOAD_SYM(core_set_audio_sample_, retro_set_audio_sample);
    LOAD_SYM(core_set_audio_sample_batch_, retro_set_audio_sample_batch);
    LOAD_SYM(core_set_input_poll_, retro_set_input_poll);
    LOAD_SYM(core_set_input_state_, retro_set_input_state);
    LOAD_SYM(core_set_controller_port_device_, retro_set_controller_port_device);
    LOAD_SYM(core_load_game_, retro_load_game);
    LOAD_SYM(core_unload_game_, retro_unload_game);
    LOAD_SYM(core_run_, retro_run);
    LOAD_SYM(core_reset_, retro_reset);
    LOAD_SYM(core_serialize_size_, retro_serialize_size);
    LOAD_SYM(core_serialize_, retro_serialize);
    LOAD_SYM(core_unserialize_, retro_unserialize);
    LOAD_SYM(core_get_memory_data_, retro_get_memory_data);
    LOAD_SYM(core_get_memory_size_, retro_get_memory_size);
    #undef LOAD_SYM

    // Set callbacks before init
    core_set_environment_(environmentCallback);
    core_init_();
    core_set_video_refresh_(videoRefreshCallback);
    core_set_audio_sample_(audioSampleCallback);
    core_set_audio_sample_batch_(audioSampleBatchCallback);
    core_set_input_poll_(inputPollCallback);
    core_set_input_state_(inputStateCallback);

    // Set controller device at the exact spot pcsx_rearmed expects (before get_system_info)
    // Only call if explicitly requested by the frontend - some cores (FCEUmm) crash on this call
    // before a game is loaded.
    if (pending_controller_device_ >= 0) {
        core_set_controller_port_device_(0, static_cast<unsigned>(pending_controller_device_));
        LOGI("Pre-init controller port 0 set to device %d", pending_controller_device_);
    }

    retro_system_info sys_info{};
    core_get_system_info_(&sys_info);
    need_fullpath_ = sys_info.need_fullpath;
    LOGI("Core loaded: %s %s", sys_info.library_name, sys_info.library_version);
    LOGI("  need_fullpath: %d, block_extract: %d", sys_info.need_fullpath, sys_info.block_extract);

    return true;
}

bool LibretroFrontend::loadGame(const char* romPath) {
    if (!core_handle_) {
        LOGE("No core loaded");
        return false;
    }

    LOGI("Loading game: %s", romPath);

    retro_game_info game_info{};
    game_info.path = romPath;
    game_info.meta = nullptr;

    if (need_fullpath_) {
        game_info.data = nullptr;
        game_info.size = 0;
    } else {
        // Core requires ROM data in memory
        std::ifstream rom_file(romPath, std::ios::binary | std::ios::ate);
        if (!rom_file) {
            LOGE("Failed to open ROM file: %s", romPath);
            return false;
        }
        size_t rom_size = rom_file.tellg();
        rom_file.seekg(0);
        rom_data_.resize(rom_size);
        rom_file.read(reinterpret_cast<char*>(rom_data_.data()), rom_size);
        game_info.data = rom_data_.data();
        game_info.size = rom_size;
        LOGI("ROM loaded into memory: %zu bytes", rom_size);
    }

    if (!core_load_game_(&game_info)) {
        LOGE("retro_load_game failed");
        return false;
    }

    game_loaded_.store(true);

    // Get AV info for timing and geometry
    retro_system_av_info av_info{};
    core_get_system_av_info_(&av_info);
    target_fps_ = av_info.timing.fps;
    double sample_rate = av_info.timing.sample_rate;

    core_base_width_ = av_info.geometry.base_width;
    core_base_height_ = av_info.geometry.base_height;
    core_aspect_ratio_ = av_info.geometry.aspect_ratio;
    if (core_aspect_ratio_ <= 0.0f && core_base_height_ > 0) {
        core_aspect_ratio_ = static_cast<float>(core_base_width_) / static_cast<float>(core_base_height_);
    }

    LOGI("Game loaded - %ux%u @ %.1f fps, audio %.0f Hz, aspect %.3f",
         av_info.geometry.base_width, av_info.geometry.base_height,
         target_fps_, sample_rate, core_aspect_ratio_);

    // Start audio
    audio_engine_.start(static_cast<int32_t>(sample_rate));

    return true;
}

// --- Lifecycle ---

void LibretroFrontend::start() {
    if (running_.load()) return;
    if (!game_loaded_.load()) {
        LOGE("Cannot start: no game loaded");
        return;
    }

    running_.store(true);
    paused_.store(false);
    emu_thread_ = std::thread(&LibretroFrontend::runLoop, this);
    LOGI("Emulation started");
}

void LibretroFrontend::pause() {
    paused_.store(true);
    LOGD("Emulation paused");
}

void LibretroFrontend::resume() {
    paused_.store(false);
    LOGD("Emulation resumed");
}

void LibretroFrontend::stop() {
    running_.store(false);
    paused_.store(false);

    if (emu_thread_.joinable()) {
        emu_thread_.join();
    }

    // Save SRAM before unloading
    if (game_loaded_.load() && core_handle_) {
        void* sram = core_get_memory_data_(RETRO_MEMORY_SAVE_RAM);
        size_t sram_size = core_get_memory_size_(RETRO_MEMORY_SAVE_RAM);
        if (sram && sram_size > 0 && !save_dir_.empty()) {
            std::string save_path = save_dir_ + "/game.srm";
            std::ofstream out(save_path, std::ios::binary);
            if (out) {
                out.write(static_cast<const char*>(sram), sram_size);
                LOGI("SRAM saved: %zu bytes", sram_size);
            }
        }

        core_unload_game_();
        game_loaded_.store(false);
    }

    rom_data_.clear();
    rom_data_.shrink_to_fit();
    audio_engine_.stop();
    LOGI("Emulation stopped");
}

void LibretroFrontend::reset() {
    if (core_handle_ && game_loaded_.load()) {
        core_reset_();
        LOGI("Emulation reset");
    }
}

void LibretroFrontend::unloadCore() {
    if (core_handle_) {
        if (game_loaded_.load()) {
            core_unload_game_();
            game_loaded_.store(false);
        }
        core_deinit_();
        dlclose(core_handle_);
        core_handle_ = nullptr;
        // Clear options so they don't leak into the next core
        core_options_.clear();
        pending_controller_device_ = -1;
        LOGI("Core unloaded");
    }
}

// --- Emulation Loop ---

void LibretroFrontend::runLoop() {
    using clock = std::chrono::high_resolution_clock;
    auto frame_duration = std::chrono::duration_cast<std::chrono::nanoseconds>(
        std::chrono::duration<double>(1.0 / target_fps_));

    while (running_.load()) {
        if (paused_.load()) {
            std::this_thread::sleep_for(std::chrono::milliseconds(16));
            continue;
        }

        auto frame_start = clock::now();
        core_run_();

        // Frame pacing
        auto elapsed = clock::now() - frame_start;
        if (elapsed < frame_duration) {
            std::this_thread::sleep_for(frame_duration - elapsed);
        }
    }
}

// --- Video ---

void LibretroFrontend::setWindow(ANativeWindow* window) {
    std::lock_guard<std::mutex> lock(window_mutex_);
    if (window_) {
        ANativeWindow_release(window_);
    }
    window_ = window;
    if (window_) {
        ANativeWindow_acquire(window_);
    }
}

void LibretroFrontend::releaseWindow() {
    std::lock_guard<std::mutex> lock(window_mutex_);
    if (window_) {
        ANativeWindow_release(window_);
        window_ = nullptr;
    }
}

void LibretroFrontend::videoRefreshCallback(const void* data, unsigned width, unsigned height, size_t pitch) {
    if (!data) return; // dupe frame

    auto& fe = instance();
    std::lock_guard<std::mutex> lock(fe.window_mutex_);
    if (!fe.window_) return;

    int32_t windowFormat;
    int bytesPerPixel;
    switch (fe.pixel_format_) {
        case RETRO_PIXEL_FORMAT_RGB565:
            windowFormat = AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM;
            bytesPerPixel = 2;
            break;
        case RETRO_PIXEL_FORMAT_XRGB8888:
            windowFormat = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
            bytesPerPixel = 4;
            break;
        case RETRO_PIXEL_FORMAT_0RGB1555:
        default:
            windowFormat = AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM;
            bytesPerPixel = 2;
            break;
    }

    DisplayMode mode = fe.display_mode_.load();

    if (mode == DisplayMode::STRETCH) {
        // Original behavior: set buffer geometry to core output, let surface stretch
        ANativeWindow_setBuffersGeometry(fe.window_, width, height, windowFormat);

        ANativeWindow_Buffer buffer;
        if (ANativeWindow_lock(fe.window_, &buffer, nullptr) != 0) return;

        auto* dst = static_cast<uint8_t*>(buffer.bits);
        auto* src = static_cast<const uint8_t*>(data);
        size_t dstStride = buffer.stride * bytesPerPixel;
        size_t copyWidth = width * bytesPerPixel;

        for (unsigned y = 0; y < height; y++) {
            memcpy(dst + y * dstStride, src + y * pitch, copyWidth);
        }

        ANativeWindow_unlockAndPost(fe.window_);
        return;
    }

    // For ASPECT_RATIO and INTEGER_SCALE, we render into a buffer matching
    // the surface size and blit the frame into a calculated sub-rect.
    int surfaceWidth = ANativeWindow_getWidth(fe.window_);
    int surfaceHeight = ANativeWindow_getHeight(fe.window_);
    if (surfaceWidth <= 0 || surfaceHeight <= 0) return;

    // Set buffer geometry to match the full surface
    ANativeWindow_setBuffersGeometry(fe.window_, surfaceWidth, surfaceHeight, windowFormat);

    ANativeWindow_Buffer buffer;
    if (ANativeWindow_lock(fe.window_, &buffer, nullptr) != 0) return;

    auto* dst = static_cast<uint8_t*>(buffer.bits);
    size_t dstStride = buffer.stride * bytesPerPixel;

    // Clear entire buffer to black
    for (int y = 0; y < buffer.height; y++) {
        memset(dst + y * dstStride, 0, buffer.width * bytesPerPixel);
    }

    // Calculate destination rect
    int destX = 0, destY = 0, destW = surfaceWidth, destH = surfaceHeight;

    if (mode == DisplayMode::ASPECT_RATIO) {
        float aspect = fe.core_aspect_ratio_;
        if (aspect <= 0.0f) {
            aspect = (height > 0) ? static_cast<float>(width) / static_cast<float>(height) : 1.0f;
        }
        float surfaceAspect = static_cast<float>(surfaceWidth) / static_cast<float>(surfaceHeight);

        if (surfaceAspect > aspect) {
            // Pillarbox: surface is wider than content
            destH = surfaceHeight;
            destW = static_cast<int>(surfaceHeight * aspect + 0.5f);
        } else {
            // Letterbox: surface is taller than content
            destW = surfaceWidth;
            destH = static_cast<int>(surfaceWidth / aspect + 0.5f);
        }
        destX = (surfaceWidth - destW) / 2;
        destY = (surfaceHeight - destH) / 2;
    } else if (mode == DisplayMode::INTEGER_SCALE) {
        int scaleX = surfaceWidth / static_cast<int>(width);
        int scaleY = surfaceHeight / static_cast<int>(height);
        int scale = (scaleX < scaleY) ? scaleX : scaleY;
        if (scale < 1) scale = 1;
        destW = width * scale;
        destH = height * scale;
        destX = (surfaceWidth - destW) / 2;
        destY = (surfaceHeight - destH) / 2;
    }

    // Clamp destination rect to buffer bounds
    if (destX < 0) destX = 0;
    if (destY < 0) destY = 0;
    if (destX + destW > buffer.width) destW = buffer.width - destX;
    if (destY + destH > buffer.height) destH = buffer.height - destY;

    // Nearest-neighbor scale blit
    auto* src = static_cast<const uint8_t*>(data);

    for (int dy = 0; dy < destH; dy++) {
        unsigned srcY = dy * height / destH;
        if (srcY >= height) srcY = height - 1;
        const uint8_t* srcRow = src + srcY * pitch;
        uint8_t* dstRow = dst + (destY + dy) * dstStride + destX * bytesPerPixel;

        for (int dx = 0; dx < destW; dx++) {
            unsigned srcX = dx * width / destW;
            if (srcX >= width) srcX = width - 1;
            memcpy(dstRow + dx * bytesPerPixel, srcRow + srcX * bytesPerPixel, bytesPerPixel);
        }
    }

    ANativeWindow_unlockAndPost(fe.window_);
}

// --- Audio ---

size_t LibretroFrontend::audioSampleBatchCallback(const int16_t* data, size_t frames) {
    instance().audio_engine_.pushSamples(data, frames);
    return frames;
}

void LibretroFrontend::audioSampleCallback(int16_t left, int16_t right) {
    int16_t buf[2] = {left, right};
    instance().audio_engine_.pushSamples(buf, 1);
}

// --- Input ---

void LibretroFrontend::setButtonState(uint16_t state) {
    button_state_.store(state);
}

void LibretroFrontend::setAnalogState(int stick, int16_t x, int16_t y) {
    if (stick == 0) {
        analog_lx_.store(x);
        analog_ly_.store(y);
    } else {
        analog_rx_.store(x);
        analog_ry_.store(y);
    }
}

void LibretroFrontend::inputPollCallback() {
    // No-op: input state is always current via atomics
}

int16_t LibretroFrontend::inputStateCallback(unsigned port, unsigned device, unsigned index, unsigned id) {
    if (port != 0) return 0;

    auto& fe = instance();

    if (device == RETRO_DEVICE_JOYPAD) {
        return (fe.button_state_.load() >> id) & 1;
    }

    if (device == RETRO_DEVICE_ANALOG) {
        if (index == RETRO_DEVICE_INDEX_ANALOG_LEFT) {
            return (id == RETRO_DEVICE_ID_ANALOG_X) ? fe.analog_lx_.load() : fe.analog_ly_.load();
        } else if (index == RETRO_DEVICE_INDEX_ANALOG_RIGHT) {
            return (id == RETRO_DEVICE_ID_ANALOG_X) ? fe.analog_rx_.load() : fe.analog_ry_.load();
        }
    }

    return 0;
}

// --- Environment ---

bool LibretroFrontend::environmentCallback(unsigned cmd, void* data) {
    auto& fe = instance();

    switch (cmd) {
        case RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY: {
            auto** dir = static_cast<const char**>(data);
            *dir = fe.system_dir_.c_str();
            return true;
        }
        case RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY: {
            auto** dir = static_cast<const char**>(data);
            *dir = fe.save_dir_.c_str();
            return true;
        }
        case RETRO_ENVIRONMENT_GET_CORE_ASSETS_DIRECTORY: {
            auto** dir = static_cast<const char**>(data);
            *dir = fe.system_dir_.c_str();
            return true;
        }
        case RETRO_ENVIRONMENT_SET_PIXEL_FORMAT: {
            auto* fmt = static_cast<const unsigned*>(data);
            fe.pixel_format_ = *fmt;
            LOGD("Pixel format set to: %u", *fmt);
            return true;
        }
        case RETRO_ENVIRONMENT_GET_VARIABLE: {
            auto* var = static_cast<retro_variable*>(data);
            if (var->key) {
                auto it = fe.core_options_.find(var->key);
                if (it != fe.core_options_.end()) {
                    var->value = it->second.c_str();
                    return true;
                }
            }
            var->value = nullptr;
            return false;
        }
        case RETRO_ENVIRONMENT_SET_VARIABLES: {
            // Core is telling us what variables it supports
            auto* vars = static_cast<const retro_variable*>(data);
            while (vars && vars->key) {
                // Only set defaults if we haven't already set a value
                if (fe.core_options_.find(vars->key) == fe.core_options_.end()) {
                    // Parse default from the description (format: "Description; val1|val2|val3")
                    if (vars->value) {
                        std::string desc(vars->value);
                        auto semi = desc.find("; ");
                        if (semi != std::string::npos) {
                            std::string values = desc.substr(semi + 2);
                            auto pipe = values.find('|');
                            std::string first = (pipe != std::string::npos) ?
                                values.substr(0, pipe) : values;
                            fe.core_options_[vars->key] = first;
                        }
                    }
                }
                vars++;
            }
            return true;
        }
        case RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE: {
            auto* updated = static_cast<bool*>(data);
            *updated = fe.options_updated_.exchange(false);
            return true;
        }
        case RETRO_ENVIRONMENT_GET_CAN_DUPE: {
            auto* can = static_cast<bool*>(data);
            *can = true;
            return true;
        }
        case RETRO_ENVIRONMENT_GET_LOG_INTERFACE: {
            auto* cb = static_cast<retro_log_callback*>(data);
            cb->log = logCallback;
            return true;
        }
        case RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS:
        case RETRO_ENVIRONMENT_SET_CONTROLLER_INFO:
        case RETRO_ENVIRONMENT_SET_MEMORY_MAPS:
        case RETRO_ENVIRONMENT_SET_SUPPORT_NO_GAME:
        case RETRO_ENVIRONMENT_SET_SUBSYSTEM_INFO:
        case RETRO_ENVIRONMENT_SET_SERIALIZATION_QUIRKS:
        case RETRO_ENVIRONMENT_SET_CORE_OPTIONS:
        case RETRO_ENVIRONMENT_SET_CORE_OPTIONS_INTL:
        case RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2:
        case RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2_INTL:
        case RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY:
        case RETRO_ENVIRONMENT_SET_CORE_OPTIONS_UPDATE_DISPLAY_CALLBACK:
            return true; // acknowledge but ignore

        case RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION: {
            auto* version = static_cast<unsigned*>(data);
            *version = 0; // use v0 variables API (simplest)
            return true;
        }
        case RETRO_ENVIRONMENT_GET_AUDIO_VIDEO_ENABLE: {
            auto* bits = static_cast<int*>(data);
            *bits = 3; // enable both audio (bit 1) and video (bit 0)
            return true;
        }
        case RETRO_ENVIRONMENT_GET_INPUT_DEVICE_CAPABILITIES: {
            auto* caps = static_cast<uint64_t*>(data);
            *caps = (1 << RETRO_DEVICE_JOYPAD) | (1 << RETRO_DEVICE_ANALOG);
            return true;
        }
        case RETRO_ENVIRONMENT_SET_GEOMETRY: {
            auto* geom = static_cast<const retro_game_geometry*>(data);
            fe.core_base_width_ = geom->base_width;
            fe.core_base_height_ = geom->base_height;
            if (geom->aspect_ratio > 0.0f) {
                fe.core_aspect_ratio_ = geom->aspect_ratio;
            } else if (geom->base_height > 0) {
                fe.core_aspect_ratio_ = static_cast<float>(geom->base_width) / static_cast<float>(geom->base_height);
            }
            LOGD("Geometry changed: %ux%u, aspect %.3f", geom->base_width, geom->base_height, fe.core_aspect_ratio_);
            return true;
        }
        case RETRO_ENVIRONMENT_GET_LANGUAGE: {
            auto* lang = static_cast<unsigned*>(data);
            *lang = 0; // RETRO_LANGUAGE_ENGLISH
            return true;
        }
        default:
            LOGD("Unhandled env cmd: %u", cmd);
            return false;
    }
}

void LibretroFrontend::logCallback(enum retro_log_level level, const char* fmt, ...) {
    va_list args;
    va_start(args, fmt);

    int priority;
    switch (level) {
        case RETRO_LOG_DEBUG: priority = ANDROID_LOG_DEBUG; break;
        case RETRO_LOG_INFO:  priority = ANDROID_LOG_INFO; break;
        case RETRO_LOG_WARN:  priority = ANDROID_LOG_WARN; break;
        case RETRO_LOG_ERROR: priority = ANDROID_LOG_ERROR; break;
        default:              priority = ANDROID_LOG_VERBOSE; break;
    }

    __android_log_vprint(priority, "RetroCore", fmt, args);
    va_end(args);
}

// --- Save States ---

bool LibretroFrontend::saveState(const char* path) {
    if (!core_handle_ || !game_loaded_.load()) return false;

    size_t size = core_serialize_size_();
    if (size == 0) return false;

    std::vector<uint8_t> buf(size);
    if (!core_serialize_(buf.data(), size)) {
        LOGE("retro_serialize failed");
        return false;
    }

    std::ofstream out(path, std::ios::binary);
    if (!out) {
        LOGE("Failed to open save state file: %s", path);
        return false;
    }
    out.write(reinterpret_cast<const char*>(buf.data()), size);
    LOGI("State saved: %zu bytes -> %s", size, path);
    return true;
}

bool LibretroFrontend::loadState(const char* path) {
    if (!core_handle_ || !game_loaded_.load()) return false;

    std::ifstream in(path, std::ios::binary | std::ios::ate);
    if (!in) {
        LOGE("Failed to open state file: %s", path);
        return false;
    }

    size_t size = in.tellg();
    in.seekg(0);

    std::vector<uint8_t> buf(size);
    in.read(reinterpret_cast<char*>(buf.data()), size);

    if (!core_unserialize_(buf.data(), size)) {
        LOGE("retro_unserialize failed");
        return false;
    }

    LOGI("State loaded: %zu bytes <- %s", size, path);
    return true;
}

// --- Config ---

void LibretroFrontend::setAudioEnabled(bool enabled) {
    audio_engine_.setEnabled(enabled);
}

void LibretroFrontend::setDisplayMode(int mode) {
    if (mode >= 0 && mode <= 2) {
        display_mode_.store(static_cast<DisplayMode>(mode));
        LOGI("Display mode set to: %d", mode);
    }
}

void LibretroFrontend::setFrameSkip(int skip) {
    frame_skip_ = skip;
    options_updated_.store(true);
}

void LibretroFrontend::setSystemDir(const char* dir) {
    system_dir_ = dir;
}

void LibretroFrontend::setSaveDir(const char* dir) {
    save_dir_ = dir;
}

void LibretroFrontend::setControllerPortDevice(unsigned port, unsigned device) {
    if (core_handle_ && core_set_controller_port_device_) {
        core_set_controller_port_device_(port, device);
        LOGI("Controller port %u set to device %u", port, device);
    }
}

void LibretroFrontend::setPendingControllerDevice(int device) {
    pending_controller_device_ = device;
}

void LibretroFrontend::setCoreOption(const char* key, const char* value) {
    core_options_[key] = value;
    options_updated_.store(true);
}
