// audio_cache.cpp - Implementació del caché de sons i música
// © 2025 Port a C++20 amb SDL3

#include "core/audio/audio_cache.hpp"

#include <iostream>

#include "core/resources/resource_helper.hpp"

// Inicialització de variables estàtiques
std::unordered_map<std::string, JA_Sound_t*> AudioCache::sounds_;
std::unordered_map<std::string, JA_Music_t*> AudioCache::musics_;
std::string AudioCache::sounds_base_path_ = "data/sounds/";
std::string AudioCache::music_base_path_ = "data/music/";

JA_Sound_t* AudioCache::getSound(const std::string& name) {
    // Cache hit
    auto it = sounds_.find(name);
    if (it != sounds_.end()) {
        std::cout << "[AudioCache] Sound cache hit: " << name << std::endl;
        return it->second;
    }

    // Normalize path: "laser_shoot.wav" → "sounds/laser_shoot.wav"
    std::string normalized = name;
    if (normalized.find("sounds/") != 0) {
        normalized = "sounds/" + normalized;
    }

    // Load from resource system
    std::vector<uint8_t> data = Resource::Helper::loadFile(normalized);
    if (data.empty()) {
        std::cerr << "[AudioCache] Error: no s'ha pogut carregar " << normalized << std::endl;
        return nullptr;
    }

    // Load sound from memory
    JA_Sound_t* sound = JA_LoadSound(data.data(), static_cast<uint32_t>(data.size()));
    if (sound == nullptr) {
        std::cerr << "[AudioCache] Error: no s'ha pogut decodificar " << normalized
                  << std::endl;
        return nullptr;
    }

    std::cout << "[AudioCache] Sound loaded: " << normalized << std::endl;
    sounds_[name] = sound;
    return sound;
}

JA_Music_t* AudioCache::getMusic(const std::string& name) {
    // Cache hit
    auto it = musics_.find(name);
    if (it != musics_.end()) {
        std::cout << "[AudioCache] Music cache hit: " << name << std::endl;
        return it->second;
    }

    // Normalize path: "title.ogg" → "music/title.ogg"
    std::string normalized = name;
    if (normalized.find("music/") != 0) {
        normalized = "music/" + normalized;
    }

    // Load from resource system
    std::vector<uint8_t> data = Resource::Helper::loadFile(normalized);
    if (data.empty()) {
        std::cerr << "[AudioCache] Error: no s'ha pogut carregar " << normalized << std::endl;
        return nullptr;
    }

    // Load music from memory
    JA_Music_t* music = JA_LoadMusic(data.data(), static_cast<uint32_t>(data.size()));
    if (music == nullptr) {
        std::cerr << "[AudioCache] Error: no s'ha pogut decodificar " << normalized
                  << std::endl;
        return nullptr;
    }

    std::cout << "[AudioCache] Music loaded: " << normalized << std::endl;
    musics_[name] = music;
    return music;
}

void AudioCache::clear() {
    std::cout << "[AudioCache] Clearing cache (" << sounds_.size() << " sounds, "
              << musics_.size() << " music)" << std::endl;

    // Liberar memoria de sonidos
    for (auto& [name, sound] : sounds_) {
        if (sound && sound->buffer) {
            SDL_free(sound->buffer);
        }
        delete sound;
    }
    sounds_.clear();

    // Liberar memoria de música
    for (auto& [name, music] : musics_) {
        if (music && music->buffer) {
            SDL_free(music->buffer);
        }
        if (music && music->filename) {
            free(music->filename);
        }
        delete music;
    }
    musics_.clear();
}

size_t AudioCache::getSoundCacheSize() { return sounds_.size(); }

size_t AudioCache::getMusicCacheSize() { return musics_.size(); }

std::string AudioCache::resolveSoundPath(const std::string& name) {
    // Si es un path absoluto (comienza con '/'), usarlo directamente
    if (!name.empty() && name[0] == '/') {
        return name;
    }

    // Si ya contiene el prefix base_path, usarlo directamente
    if (name.find(sounds_base_path_) == 0) {
        return name;
    }

    // Caso contrario, añadir base_path
    return sounds_base_path_ + name;
}

std::string AudioCache::resolveMusicPath(const std::string& name) {
    // Si es un path absoluto (comienza con '/'), usarlo directamente
    if (!name.empty() && name[0] == '/') {
        return name;
    }

    // Si ya contiene el prefix base_path, usarlo directamente
    if (name.find(music_base_path_) == 0) {
        return name;
    }

    // Caso contrario, añadir base_path
    return music_base_path_ + name;
}
