#include "param.h"

#include <SDL3/SDL.h>  // Para SDL_LogCategory, SDL_LogError, SDL_LogInfo

#include <fstream>  // Para basic_istream, basic_ifstream, ifstream
#include <functional>
#include <sstream>    // Para basic_istringstream
#include <stdexcept>  // Para runtime_error
#include <string>     // Para operator==, stoi, char_traits, string, ope...
#include <unordered_map>

#include "color.h"
#include "utils.h"

Param param;

// Calcula variables a partir de otras variables
void precalculateZones();

// Asigna variables a partir de dos cadenas
auto setParams(const std::string& var, const std::string& value) -> bool;

// Establece valores por defecto a las variables
void initParam() {
    // GAME
    param.game.width = 320;
    param.game.height = 256;
    param.game.item_size = 20;
    param.game.game_area.rect = {0, 0, param.game.width, param.game.height};
    param.game.play_area.rect = {0, 0, param.game.width, 216};
    param.game.name_entry_idle_time = 10;
    param.game.name_entry_total_time = 60;
    param.game.speed = 15;
    param.game.hit_stop = true;
    param.game.hit_stop_ms = 300;
    precalculateZones();

    // SCOREBOARD
    param.scoreboard.rect = {0, 216, param.game.width, 40};
    param.scoreboard.separator_autocolor = false;
    param.scoreboard.separator_color = Color();
    param.scoreboard.easy_color = Color();
    param.scoreboard.normal_color = Color();
    param.scoreboard.hard_color = Color();
    param.scoreboard.text_autocolor = false;
    param.scoreboard.text_color1 = Color();
    param.scoreboard.text_color2 = Color();
    param.scoreboard.skip_countdown_value = 8;

    // FADE
    param.fade.num_squares_width = param.game.width / 2;
    param.fade.num_squares_height = param.game.height / 2;
    param.fade.random_squares_delay = 1;
    param.fade.random_squares_mult = 500;
    param.fade.post_duration = 80;
    param.fade.venetian_size = 16;

    // TITLE
    param.title.press_start_position = 160;
    param.title.title_duration = 800;
    param.title.arcade_edition_position = 123;
    param.title.title_c_c_position = 11;
    param.title.bg_color = Color(255, 255, 255);

    // BACKGROUND
    param.background.attenuate_color = Color(255, 255, 255, 0);

    // BALLOONS
    param.balloon.settings.at(0) = ParamBalloon::Settings(0.09F, 2.60F);
    param.balloon.settings.at(1) = ParamBalloon::Settings(0.10F, 3.50F);
    param.balloon.settings.at(2) = ParamBalloon::Settings(0.10F, 4.50F);
    param.balloon.settings.at(3) = ParamBalloon::Settings(0.10F, 4.95F);

    param.balloon.color.at(0) = "blue";
    param.balloon.color.at(1) = "orange";
    param.balloon.color.at(2) = "red";
    param.balloon.color.at(3) = "green";

    param.balloon.bouncing_sound = false;

    // NOTIFICATION
    param.notification.pos_v = NotifyPosition::TOP;
    param.notification.pos_h = NotifyPosition::LEFT;
    param.notification.sound = false;
    param.notification.color = Color(48, 48, 48);

    // INTRO
    param.intro.bg_color = Color::fromHex("543149");
    param.intro.card_color = Color::fromHex("CBDBFC");
    param.intro.shadow_color = Color::fromHex("00000080");
    param.intro.text_distance_from_bottom = 48;
}

// Carga los parámetros desde un archivo
void loadParamsFromFile(const std::string& file_path) {
    // Inicializa los parámetros con valores por defecto
    initParam();

    // Abre el archivo
    std::ifstream file(file_path);
    if (!file.is_open()) {
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: No se pudo abrir el archivo %s", file_path.c_str());
        throw std::runtime_error("No se pudo abrir el archivo: " + file_path);
    }

    SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\nReading file: %s", getFileName(file_path).c_str());

    std::string line;
    std::string param1;
    std::string param2;
    while (std::getline(file, line)) {
        // Elimina comentarios
        auto comment_pos = line.find('#');
        if (comment_pos != std::string::npos) {
            line.resize(comment_pos);
        }

        // Usa un stream para separar palabras
        std::istringstream iss(line);
        if (iss >> param1 >> param2) {
            if (!setParams(param1, param2)) {
                SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Parámetro desconocido: %s", param1.c_str());
            }
        }
    }

    // Cierra el archivo
    file.close();

    // Realiza cálculos adicionales después de cargar los parámetros
    precalculateZones();
}

auto setParams(const std::string& var, const std::string& value) -> bool {
    static const std::unordered_map<std::string, std::function<void(const std::string&)>> INT_PARAMS = {
        {"game.width", [](const std::string& v) { param.game.width = std::stoi(v); }},
        {"game.height", [](const std::string& v) { param.game.height = std::stoi(v); }},
        {"game.item_size", [](const std::string& v) { param.game.item_size = std::stoi(v); }},
        {"game.play_area.rect.x", [](const std::string& v) { param.game.play_area.rect.x = std::stoi(v); }},
        {"game.play_area.rect.y", [](const std::string& v) { param.game.play_area.rect.y = std::stoi(v); }},
        {"game.play_area.rect.w", [](const std::string& v) { param.game.play_area.rect.w = std::stoi(v); }},
        {"game.play_area.rect.h", [](const std::string& v) { param.game.play_area.rect.h = std::stoi(v); }},
        {"game.name_entry_idle_time", [](const std::string& v) { param.game.name_entry_idle_time = std::stoi(v); }},
        {"game.name_entry_total_time", [](const std::string& v) { param.game.name_entry_total_time = std::stoi(v); }},
        {"game.hit_stop_ms", [](const std::string& v) { param.game.hit_stop_ms = std::stoi(v); }},
        {"fade.num_squares_width", [](const std::string& v) { param.fade.num_squares_width = std::stoi(v); }},
        {"fade.num_squares_height", [](const std::string& v) { param.fade.num_squares_height = std::stoi(v); }},
        {"fade.random_squares_delay", [](const std::string& v) { param.fade.random_squares_delay = std::stoi(v); }},
        {"fade.random_squares_mult", [](const std::string& v) { param.fade.random_squares_mult = std::stoi(v); }},
        {"fade.post_duration", [](const std::string& v) { param.fade.post_duration = std::stoi(v); }},
        {"fade.venetian_size", [](const std::string& v) { param.fade.venetian_size = std::stoi(v); }},
        {"scoreboard.rect.x", [](const std::string& v) { param.scoreboard.rect.x = std::stoi(v); }},
        {"scoreboard.rect.y", [](const std::string& v) { param.scoreboard.rect.y = std::stoi(v); }},
        {"scoreboard.rect.w", [](const std::string& v) { param.scoreboard.rect.w = std::stoi(v); }},
        {"scoreboard.rect.h", [](const std::string& v) { param.scoreboard.rect.h = std::stoi(v); }},
        {"scoreboard.skip_countdown_value", [](const std::string& v) { param.scoreboard.skip_countdown_value = std::stoi(v); }},
        {"title.press_start_position", [](const std::string& v) { param.title.press_start_position = std::stoi(v); }},
        {"title.title_duration", [](const std::string& v) { param.title.title_duration = std::stoi(v); }},
        {"title.arcade_edition_position", [](const std::string& v) { param.title.arcade_edition_position = std::stoi(v); }},
        {"title.title_c_c_position", [](const std::string& v) { param.title.title_c_c_position = std::stoi(v); }},
        {"intro.text_distance_from_bottom", [](const std::string& v) { param.intro.text_distance_from_bottom = std::stoi(v); }}};

    static const std::unordered_map<std::string, std::function<void(const std::string&)>> COLOR_PARAMS = {
        {"fade.color", [](const std::string& v) { param.fade.color = Color::fromHex(v); }},
        {"scoreboard.separator_color", [](const std::string& v) { param.scoreboard.separator_color = Color::fromHex(v); }},
        {"scoreboard.easy_color", [](const std::string& v) { param.scoreboard.easy_color = Color::fromHex(v); }},
        {"scoreboard.normal_color", [](const std::string& v) { param.scoreboard.normal_color = Color::fromHex(v); }},
        {"scoreboard.hard_color", [](const std::string& v) { param.scoreboard.hard_color = Color::fromHex(v); }},
        {"scoreboard.text_color1", [](const std::string& v) { param.scoreboard.text_color1 = Color::fromHex(v); }},
        {"scoreboard.text_color2", [](const std::string& v) { param.scoreboard.text_color2 = Color::fromHex(v); }},
        {"title.bg_color", [](const std::string& v) { param.title.bg_color = Color::fromHex(v); }},
        {"background.attenuate_color", [](const std::string& v) { param.background.attenuate_color = Color::fromHex(v); }},
        {"notification.color", [](const std::string& v) { param.notification.color = Color::fromHex(v); }},
        {"service_menu.title_color", [](const std::string& v) { param.service_menu.title_color = Color::fromHex(v); }},
        {"service_menu.text_color", [](const std::string& v) { param.service_menu.text_color = Color::fromHex(v); }},
        {"service_menu.selected_color", [](const std::string& v) { param.service_menu.selected_color = Color::fromHex(v); }},
        {"service_menu.bg_color", [](const std::string& v) { param.service_menu.bg_color = Color::fromHex(v); }},
        {"intro.bg_color", [](const std::string& v) { param.intro.bg_color = Color::fromHex(v); }},
        {"intro.card_color", [](const std::string& v) { param.intro.card_color = Color::fromHex(v); }},
        {"intro.shadow_color", [](const std::string& v) { param.intro.shadow_color = Color::fromHex(v); }},
        {"debug.color", [](const std::string& v) { param.debug.color = Color::fromHex(v); }},
        {"resource.color", [](const std::string& v) { param.resource.color = Color::fromHex(v); }}};

    static const std::unordered_map<std::string, std::function<void(const std::string&)>> BOOL_PARAMS = {
        {"game.hit_stop", [](const std::string& v) { param.game.hit_stop = stringToBool(v); }},
        {"scoreboard.separator_autocolor", [](const std::string& v) { param.scoreboard.separator_autocolor = stringToBool(v); }},
        {"scoreboard.text_autocolor", [](const std::string& v) { param.scoreboard.text_autocolor = stringToBool(v); }},
        {"balloon.bouncing_sound", [](const std::string& v) { param.balloon.bouncing_sound = stringToBool(v); }},
        {"notification.sound", [](const std::string& v) { param.notification.sound = stringToBool(v); }},
        {"service_menu.drop_shadow", [](const std::string& v) { param.service_menu.drop_shadow = stringToBool(v); }}};

    static const std::unordered_map<std::string, std::function<void(const std::string&)>> FLOAT_PARAMS = {
        {"balloon.settings[0].vel", [](const std::string& v) { param.balloon.settings.at(0).vel = std::stof(v); }},
        {"balloon.settings[0].grav", [](const std::string& v) { param.balloon.settings.at(0).grav = std::stof(v); }},
        {"balloon.settings[1].vel", [](const std::string& v) { param.balloon.settings.at(1).vel = std::stof(v); }},
        {"balloon.settings[1].grav", [](const std::string& v) { param.balloon.settings.at(1).grav = std::stof(v); }},
        {"balloon.settings[2].vel", [](const std::string& v) { param.balloon.settings.at(2).vel = std::stof(v); }},
        {"balloon.settings[2].grav", [](const std::string& v) { param.balloon.settings.at(2).grav = std::stof(v); }},
        {"balloon.settings[3].vel", [](const std::string& v) { param.balloon.settings.at(3).vel = std::stof(v); }},
        {"balloon.settings[3].grav", [](const std::string& v) { param.balloon.settings.at(3).grav = std::stof(v); }}};

    static const std::unordered_map<std::string, std::function<void(const std::string&)>> STRING_PARAMS = {
        {"balloon.color[0]", [](const std::string& v) { param.balloon.color.at(0) = v; }},
        {"balloon.color[1]", [](const std::string& v) { param.balloon.color.at(1) = v; }},
        {"balloon.color[2]", [](const std::string& v) { param.balloon.color.at(2) = v; }},
        {"balloon.color[3]", [](const std::string& v) { param.balloon.color.at(3) = v; }}};

    // Intentar cada mapa de parámetros
    auto try_map = [&](const auto& param_map) -> bool {
        auto it = param_map.find(var);
        if (it != param_map.end()) {
            it->second(value);
            return true;
        }
        return false;
    };

    if (try_map(INT_PARAMS) || try_map(COLOR_PARAMS) || try_map(BOOL_PARAMS) ||
        try_map(FLOAT_PARAMS) || try_map(STRING_PARAMS)) {
        return true;
    }

    // Casos especiales que necesitan lógica personalizada
    if (var == "notification.pos_h") {
        if (value == "LEFT") {
            param.notification.pos_h = NotifyPosition::LEFT;
        } else if (value == "MIDDLE") {
            param.notification.pos_h = NotifyPosition::MIDDLE;
        } else {
            param.notification.pos_h = NotifyPosition::RIGHT;
        }
        return true;
    }

    if (var == "notification.pos_v") {
        param.notification.pos_v = value == "TOP" ? NotifyPosition::TOP : NotifyPosition::BOTTOM;
        return true;
    }

    return false;  // Parámetro no encontrado
}

// Calcula variables a partir de otras variables
void precalculateZones() {
    // playArea
    param.game.play_area.center_x = param.game.play_area.rect.w / 2;
    param.game.play_area.first_quarter_x = param.game.play_area.rect.w / 4;
    param.game.play_area.third_quarter_x = param.game.play_area.rect.w / 4 * 3;
    param.game.play_area.center_y = param.game.play_area.rect.h / 2;
    param.game.play_area.first_quarter_y = param.game.play_area.rect.h / 4;
    param.game.play_area.third_quarter_y = param.game.play_area.rect.h / 4 * 3;

    // gameArea
    param.game.game_area.rect = {0, 0, param.game.width, param.game.height};
    param.game.game_area.center_x = param.game.game_area.rect.w / 2;
    param.game.game_area.first_quarter_x = param.game.game_area.rect.w / 4;
    param.game.game_area.third_quarter_x = param.game.game_area.rect.w / 4 * 3;
    param.game.game_area.center_y = param.game.game_area.rect.h / 2;
    param.game.game_area.first_quarter_y = param.game.game_area.rect.h / 4;
    param.game.game_area.third_quarter_y = param.game.game_area.rect.h / 4 * 3;
}