// postfx_config.cpp - Implementación del cargador de YAML del postpro.

#include "core/config/postfx_config.hpp"

#include <iostream>

#include "core/resources/resource_helper.hpp"
#include "external/fkyaml_node.hpp"

namespace Config::PostFx {

    namespace {

        // Helper: lee `key` en `node` solo si existe; deja `dst` intacto en caso
        // contrario. Así, un YAML parcial sigue funcionando con los defaults del
        // struct para los campos que falten.
        template <typename T>
        void readField(const fkyaml::node& node, const char* key, T& dst) {
            if (node.contains(key)) {
                dst = node[key].get_value<T>();
            }
        }

        // Lee un array RGB [r, g, b] (0..255) y lo normaliza a [0..1] sobre tres
        // destinos floats. Si la clave no existe o no es secuencia de 3, deja los
        // destinos como están.
        void readRgb255(const fkyaml::node& node, const char* key, float& dst_r, float& dst_g, float& dst_b) {
            if (!node.contains(key)) {
                return;
            }
            const auto& arr = node[key];
            if (!arr.is_sequence() || arr.size() < 3) {
                return;
            }
            try {
                const auto R = arr[0].get_value<int>();
                const auto G = arr[1].get_value<int>();
                const auto B = arr[2].get_value<int>();
                dst_r = static_cast<float>(R) / 255.0F;
                dst_g = static_cast<float>(G) / 255.0F;
                dst_b = static_cast<float>(B) / 255.0F;
            } catch (...) {  // @INTENTIONAL
                // Mantiene los defaults si algún elemento del RGB no es entero parseable
                // (el YAML viene de archivo, así que es razonable degradar a los defaults
                // en vez de propagar la excepción y abortar el load del postpro entero).
            }
        }

    }  // namespace

    auto load(const std::string& path) -> Rendering::GPU::PostFxParams {
        Rendering::GPU::PostFxParams params{};  // valores por defecto del struct

        auto bytes = Resource::Helper::loadFile(path);
        if (bytes.empty()) {
            std::cerr << "[PostFxConfig] No se pudo cargar " << path
                      << " — usando defaults built-in\n";
            return params;
        }

        try {
            const auto* begin = reinterpret_cast<const char*>(bytes.data());
            const auto* end = begin + bytes.size();
            auto yaml = fkyaml::node::deserialize(begin, end);

            if (yaml.contains("bloom") && yaml["bloom"].is_mapping()) {
                const auto& node = yaml["bloom"];
                readField(node, "enabled", params.bloom_enabled);
                readField(node, "intensity", params.bloom_intensity);
                readField(node, "threshold", params.bloom_threshold);
                // sigma_px és el paràmetre canònic des del separable blur; acceptem
                // també `radius_px` com a alias per a configs antigues (s'interpreta
                // com sigma directament — els valors útils estan al mateix rang ~2-5).
                readField(node, "sigma_px", params.bloom_sigma_px);
                readField(node, "radius_px", params.bloom_sigma_px);
            }

            if (yaml.contains("flicker") && yaml["flicker"].is_mapping()) {
                const auto& node = yaml["flicker"];
                readField(node, "enabled", params.flicker_enabled);
                readField(node, "amplitude", params.flicker_amplitude);
                readField(node, "frequency_hz", params.flicker_frequency_hz);
            }

            if (yaml.contains("background") && yaml["background"].is_mapping()) {
                const auto& node = yaml["background"];
                readField(node, "enabled", params.background_enabled);
                readRgb255(node, "color_min", params.background_min_r, params.background_min_g, params.background_min_b);
                readRgb255(node, "color_max", params.background_max_r, params.background_max_g, params.background_max_b);
                readField(node, "pulse_frequency_hz", params.background_pulse_freq_hz);
            }

            std::cout << "[PostFxConfig] Cargado " << path
                      << " (bloom=" << (params.bloom_enabled ? "on" : "off")
                      << " intensity=" << params.bloom_intensity
                      << ", flicker=" << (params.flicker_enabled ? "on" : "off")
                      << " amp=" << params.flicker_amplitude
                      << ", bg=" << (params.background_enabled ? "on" : "off")
                      << ")\n";
        } catch (const fkyaml::exception& e) {
            std::cerr << "[PostFxConfig] Error parseando " << path << ": " << e.what()
                      << " — usando defaults built-in\n";
        }
        return params;
    }

}  // namespace Config::PostFx
