#pragma once

#include "libretro.h"
#include "audio_engine.h"

#include <android/native_window.h>
#include <atomic>
#include <mutex>
#include <string>
#include <thread>
#include <unordered_map>
#include <vector>

enum class DisplayMode : int {
    STRETCH = 0,
    ASPECT_RATIO = 1,
    INTEGER_SCALE = 2
};

class LibretroFrontend {
public:
    static LibretroFrontend& instance();

    bool loadCore(const char* soPath);
    bool loadGame(const char* romPath);
    void start();
    void pause();
    void resume();
    void stop();
    void reset();

    void setWindow(ANativeWindow* window);
    void releaseWindow();

    void setButtonState(uint16_t state);
    void setAnalogState(int stick, int16_t x, int16_t y);

    bool saveState(const char* path);
    bool loadState(const char* path);

    void setAudioEnabled(bool enabled);
    void setFrameSkip(int skip);
    void setDisplayMode(int mode);

    void setSystemDir(const char* dir);
    void setSaveDir(const char* dir);
    void setControllerPortDevice(unsigned port, unsigned device);
    void setPendingControllerDevice(int device);
    void setCoreOption(const char* key, const char* value);

    bool isRunning() const { return running_.load(); }

private:
    LibretroFrontend() = default;
    ~LibretroFrontend();

    void runLoop();
    void unloadCore();

    // Libretro callbacks (static because libretro uses C function pointers)
    static bool environmentCallback(unsigned cmd, void* data);
    static void videoRefreshCallback(const void* data, unsigned width, unsigned height, size_t pitch);
    static size_t audioSampleBatchCallback(const int16_t* data, size_t frames);
    static void audioSampleCallback(int16_t left, int16_t right);
    static void inputPollCallback();
    static int16_t inputStateCallback(unsigned port, unsigned device, unsigned index, unsigned id);
    static void logCallback(enum retro_log_level level, const char* fmt, ...);

    // Core function pointers (resolved via dlsym)
    void* core_handle_ = nullptr;

    retro_init_t                     core_init_ = nullptr;
    retro_deinit_t                   core_deinit_ = nullptr;
    retro_api_version_t              core_api_version_ = nullptr;
    retro_get_system_info_t          core_get_system_info_ = nullptr;
    retro_get_system_av_info_t       core_get_system_av_info_ = nullptr;
    retro_set_environment_t          core_set_environment_ = nullptr;
    retro_set_video_refresh_t        core_set_video_refresh_ = nullptr;
    retro_set_audio_sample_t         core_set_audio_sample_ = nullptr;
    retro_set_audio_sample_batch_t   core_set_audio_sample_batch_ = nullptr;
    retro_set_input_poll_t           core_set_input_poll_ = nullptr;
    retro_set_input_state_t          core_set_input_state_ = nullptr;
    retro_set_controller_port_device_t core_set_controller_port_device_ = nullptr;
    retro_load_game_t                core_load_game_ = nullptr;
    retro_unload_game_t              core_unload_game_ = nullptr;
    retro_run_t                      core_run_ = nullptr;
    retro_reset_t                    core_reset_ = nullptr;
    retro_serialize_size_t           core_serialize_size_ = nullptr;
    retro_serialize_t                core_serialize_ = nullptr;
    retro_unserialize_t              core_unserialize_ = nullptr;
    retro_get_memory_data_t          core_get_memory_data_ = nullptr;
    retro_get_memory_size_t          core_get_memory_size_ = nullptr;

    // State
    std::atomic<bool> running_{false};
    std::atomic<bool> paused_{false};
    std::atomic<bool> game_loaded_{false};
    std::thread emu_thread_;

    // Video
    ANativeWindow* window_ = nullptr;
    std::mutex window_mutex_;
    unsigned pixel_format_ = RETRO_PIXEL_FORMAT_RGB565;
    std::atomic<DisplayMode> display_mode_{DisplayMode::STRETCH};
    float core_aspect_ratio_ = 0.0f;
    unsigned core_base_width_ = 0;
    unsigned core_base_height_ = 0;

    // Audio
    AudioEngine audio_engine_;

    // Input
    std::atomic<uint16_t> button_state_{0};
    std::atomic<int16_t> analog_lx_{0}, analog_ly_{0};
    std::atomic<int16_t> analog_rx_{0}, analog_ry_{0};

    // Paths
    std::string system_dir_;
    std::string save_dir_;

    // ROM data (kept alive for cores with need_fullpath=false)
    std::vector<uint8_t> rom_data_;
    bool need_fullpath_ = true;

    // Pending controller device (set by frontend before loadCore, applied during loadCore)
    // -1 means "do not call retro_set_controller_port_device" (default)
    int pending_controller_device_ = -1;

    // Frame timing
    double target_fps_ = 60.0;
    int frame_skip_ = 0;

    // Core options
    std::unordered_map<std::string, std::string> core_options_;
    std::atomic<bool> options_updated_{false};
};
