#include "audio.h"

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

#include <algorithm>  // Para clamp

#ifndef NO_AUDIO
#include "external/jail_audio.h"  // Para JA_FadeOutMusic, JA_Init, JA_PauseM...
#endif
#include "options.h"   // Para AudioOptions, audio, MusicOptions
#include "resource.h"  // Para Resource

// Singleton
Audio *Audio::instance = nullptr;

// Inicializa la instancia única del singleton
void Audio::init() { Audio::instance = new Audio(); }

// Libera la instancia
void Audio::destroy() { delete Audio::instance; }

// Obtiene la instancia
auto Audio::get() -> Audio * { return Audio::instance; }

// Constructor
Audio::Audio() { initSDLAudio(); }

// Destructor
Audio::~Audio() {
#ifndef NO_AUDIO
    JA_Quit();
#endif
}

// Reproduce la música
void Audio::playMusic(const std::string &name, const int loop) {
    music_.name = name;
    music_.loop = (loop != 0);

    if (music_enabled_ && music_.state != MusicState::PLAYING) {
#ifndef NO_AUDIO
        JA_PlayMusic(Resource::get()->getMusic(name), loop);
#endif
        music_.state = MusicState::PLAYING;
    }
}

// Pausa la música
void Audio::pauseMusic() {
    if (music_enabled_ && music_.state == MusicState::PLAYING) {
#ifndef NO_AUDIO
        JA_PauseMusic();
#endif
        music_.state = MusicState::PAUSED;
    }
}

// Detiene la música
void Audio::stopMusic() {
    if (music_enabled_) {
#ifndef NO_AUDIO
        JA_StopMusic();
#endif
        music_.state = MusicState::STOPPED;
    }
}

// Reproduce un sonido
void Audio::playSound(const std::string &name, Group group) const {
    if (sound_enabled_) {
#ifndef NO_AUDIO
        JA_PlaySound(Resource::get()->getSound(name), 0, static_cast<int>(group));
#endif
    }
}

// Detiene todos los sonidos
void Audio::stopAllSounds() const {
    if (sound_enabled_) {
#ifndef NO_AUDIO
        JA_StopChannel(-1);
#endif
    }
}

// Realiza un fundido de salida de la música
void Audio::fadeOutMusic(int milliseconds) const {
    if (music_enabled_) {
#ifndef NO_AUDIO
        JA_FadeOutMusic(milliseconds);
#endif
    }
}

// Establece el volumen de los sonidos
void Audio::setSoundVolume(int sound_volume, Group group) const {
    if (sound_enabled_) {
        sound_volume = std::clamp(sound_volume, MIN_VOLUME, MAX_VOLUME);
#ifndef NO_AUDIO
        const float CONVERTED_VOLUME = (sound_volume / 100.0F) * (Options::audio.volume / 100.0F);
        JA_SetSoundVolume(CONVERTED_VOLUME, static_cast<int>(group));
#endif
    }
}

// Establece el volumen de la música
void Audio::setMusicVolume(int music_volume) const {
    if (music_enabled_) {
        music_volume = std::clamp(music_volume, MIN_VOLUME, MAX_VOLUME);
#ifndef NO_AUDIO
        const float CONVERTED_VOLUME = (music_volume / 100.0F) * (Options::audio.volume / 100.0F);
        JA_SetMusicVolume(CONVERTED_VOLUME);
#endif
    }
}

// Aplica la configuración
void Audio::applySettings() {
    enable(Options::audio.enabled);
}

// Establecer estado general
void Audio::enable(bool value) {
    enabled_ = value;

    setSoundVolume(enabled_ ? Options::audio.sound.volume : MIN_VOLUME);
    setMusicVolume(enabled_ ? Options::audio.music.volume : MIN_VOLUME);
}

// Inicializa SDL Audio
void Audio::initSDLAudio() {
#ifndef NO_AUDIO
    if (!SDL_Init(SDL_INIT_AUDIO)) {
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_AUDIO could not initialize! SDL Error: %s", SDL_GetError());
    } else {
        JA_Init(FREQUENCY, SDL_AUDIO_S16LE, 2);
        enable(Options::audio.enabled);

        SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "** Audio system initialized successfully");
    }
#else
    SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "** Audio system disabled");
#endif
}