#include "core/audio/sound_effects_config.hpp"

#include <string>

#include <iostream>

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

namespace {

    // Lector de camp con fallback: deixa el destí intacte si la clau no
    // existeix (los defaults dels Ja::*Params s'inicialitzen al ctor del
    // struct, así que el comportament es "preset parcial = preset complet
    // con defaults per als camps 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>(); }
    }

}  // namespace

auto SoundEffectsConfig::get() -> SoundEffectsConfig& {
    static SoundEffectsConfig instance_;
    return instance_;
}

void SoundEffectsConfig::load(const std::string& file_path) {
    auto bytes = Resource::Helper::loadFile(file_path);
    if (bytes.empty()) {
        std::cerr << "[SoundEffectsConfig] no se ha podido abrir " << file_path
                  << " — sin presets de efecto disponibles\n";
        return;
    }

    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("echo") && yaml["echo"].is_mapping()) {
            for (auto it = yaml["echo"].begin(); it != yaml["echo"].end(); ++it) {
                const auto NAME = it.key().get_value<std::string>();
                const auto& node = it.value();
                Ja::EchoParams params{};
                readField(node, "delay_ms", params.delay_ms);
                readField(node, "feedback", params.feedback);
                readField(node, "wet", params.wet);
                echoes_[NAME] = params;
            }
        }

        if (yaml.contains("reverb") && yaml["reverb"].is_mapping()) {
            for (auto it = yaml["reverb"].begin(); it != yaml["reverb"].end(); ++it) {
                const auto NAME = it.key().get_value<std::string>();
                const auto& node = it.value();
                Ja::ReverbParams params{};
                readField(node, "room_size", params.room_size);
                readField(node, "damping", params.damping);
                readField(node, "wet", params.wet);
                reverbs_[NAME] = params;
            }
        }

        std::cout << "[SoundEffectsConfig] " << echoes_.size() << " preset(s) de echo y "
                  << reverbs_.size() << " de reverb desde " << file_path << "\n";
    } catch (const std::exception& e) {
        std::cerr << "[SoundEffectsConfig] error parseando " << file_path << ": " << e.what() << "\n";
    }
}

auto SoundEffectsConfig::findEcho(const std::string& name) const -> const Ja::EchoParams* {
    const auto IT = echoes_.find(name);
    return (IT == echoes_.end()) ? nullptr : &IT->second;
}

auto SoundEffectsConfig::findReverb(const std::string& name) const -> const Ja::ReverbParams* {
    const auto IT = reverbs_.find(name);
    return (IT == reverbs_.end()) ? nullptr : &IT->second;
}
