#include "engine/audio.h"
#include "config.h"
#include <SDL2/SDL_mixer.h>
#include <stdio.h>
#include <math.h>

static bool s_initialized = false;

/* ── Listener state for spatial audio ────────────── */
static Vec2 s_listener_pos = {0};
static bool s_listener_set = false;

/* Default max audible distance in pixels (~40 tiles) */
#define AUDIO_DEFAULT_MAX_DIST  640.0f

bool audio_init(void) {
    /* Initialize MP3 (and OGG) decoder support */
    int mix_flags = MIX_INIT_MP3 | MIX_INIT_OGG;
    int mix_initted = Mix_Init(mix_flags);
    if ((mix_initted & mix_flags) != mix_flags) {
        fprintf(stderr, "Mix_Init: some formats not available: %s\n", Mix_GetError());
        /* Continue anyway — WAV still works */
    }

    if (Mix_OpenAudio(AUDIO_FREQUENCY, MIX_DEFAULT_FORMAT,
                      AUDIO_CHANNELS, AUDIO_CHUNK_SIZE) < 0) {
        fprintf(stderr, "Mix_OpenAudio failed: %s\n", Mix_GetError());
        return false;
    }

    /* Log what SDL_mixer actually opened */
    {
        int freq, channels;
        Uint16 format;
        if (Mix_QuerySpec(&freq, &format, &channels)) {
            printf("Audio device: %d Hz, %d channels, format=0x%04X\n",
                   freq, channels, format);
        }
    }

    Mix_AllocateChannels(MAX_SFX_CHANNELS);
    s_initialized = true;
    printf("Audio initialized.\n");
    return true;
}

Sound audio_load_sound(const char *path) {
    Sound s = {0};
    if (!s_initialized) return s;
    s.chunk = Mix_LoadWAV(path);
    if (!s.chunk) {
        fprintf(stderr, "Failed to load sound: %s (%s)\n", path, Mix_GetError());
    }
    return s;
}

Music audio_load_music(const char *path) {
    Music m = {0};
    if (!s_initialized) {
        fprintf(stderr, "audio_load_music: audio not initialized\n");
        return m;
    }
    m.music = Mix_LoadMUS(path);
    if (!m.music) {
        fprintf(stderr, "Failed to load music: %s (%s)\n", path, Mix_GetError());
    } else {
        printf("Loaded music: %s\n", path);
    }
    return m;
}

void audio_play_sound(Sound s, int volume) {
    if (!s_initialized || !s.chunk) return;
    int ch = Mix_PlayChannel(-1, (Mix_Chunk *)s.chunk, 0);
    if (ch >= 0) {
        Mix_Volume(ch, volume);
        Mix_SetPanning(ch, 255, 255);  /* reset to center (no panning) */
    }
}

int audio_play_sound_loop(Sound s, int volume) {
    if (!s_initialized || !s.chunk) return -1;
    int ch = Mix_PlayChannel(-1, (Mix_Chunk *)s.chunk, -1); /* -1 = loop forever */
    if (ch >= 0) {
        Mix_Volume(ch, volume);
        Mix_SetPanning(ch, 255, 255);  /* reset to center */
    }
    return ch;
}

void audio_stop_channel(int ch) {
    if (!s_initialized || ch < 0) return;
    Mix_HaltChannel(ch);
}

void audio_play_music(Music m, bool loop) {
    if (!s_initialized) {
        fprintf(stderr, "audio_play_music: audio not initialized\n");
        return;
    }
    if (!m.music) {
        fprintf(stderr, "audio_play_music: music handle is NULL\n");
        return;
    }
    if (Mix_PlayMusic((Mix_Music *)m.music, loop ? -1 : 1) < 0) {
        fprintf(stderr, "Mix_PlayMusic failed: %s\n", Mix_GetError());
    }
}

void audio_stop_music(void) {
    if (!s_initialized) return;
    Mix_HaltMusic();
}

void audio_free_music(Music *m) {
    if (!m || !m->music) return;
    Mix_FreeMusic((Mix_Music *)m->music);
    m->music = NULL;
}

void audio_set_music_volume(int volume) {
    if (!s_initialized) return;
    Mix_VolumeMusic(volume);
}

/* ── Positional / spatial audio ──────────────────── */

void audio_set_listener(Vec2 pos) {
    s_listener_pos = pos;
    s_listener_set = true;
}

void audio_play_sound_at(Sound s, int base_volume, Vec2 world_pos, float max_dist) {
    if (!s_initialized || !s.chunk) return;

    if (max_dist <= 0.0f) max_dist = AUDIO_DEFAULT_MAX_DIST;

    /* Fall back to non-positional if listener was never set */
    if (!s_listener_set) {
        audio_play_sound(s, base_volume);
        return;
    }

    float dx = world_pos.x - s_listener_pos.x;
    float dy = world_pos.y - s_listener_pos.y;
    float dist = sqrtf(dx * dx + dy * dy);

    /* Inaudible beyond max distance */
    if (dist >= max_dist) return;

    /* Linear attenuation: full volume at dist=0, silent at max_dist */
    float atten = 1.0f - (dist / max_dist);
    int vol = (int)(base_volume * atten);
    if (vol <= 0) return;
    if (vol > 128) vol = 128;

    int ch = Mix_PlayChannel(-1, (Mix_Chunk *)s.chunk, 0);
    if (ch < 0) return;

    Mix_Volume(ch, vol);

    /* Stereo panning: center = 127, hard-left = 0, hard-right = 254.
     * Pan linearly based on horizontal offset relative to half the
     * viewport width (~half of SCREEN_WIDTH). */
    float half_screen = (float)SCREEN_WIDTH * 0.5f;
    float pan_ratio = dx / half_screen;            /* -1..+1 roughly   */
    if (pan_ratio < -1.0f) pan_ratio = -1.0f;
    if (pan_ratio >  1.0f) pan_ratio =  1.0f;
    Uint8 pan_right = (Uint8)(127 + (int)(pan_ratio * 127.0f));
    Uint8 pan_left  = 254 - pan_right;
    Mix_SetPanning(ch, pan_left, pan_right);
}

void audio_shutdown(void) {
    if (!s_initialized) return;
    Mix_HaltMusic();
    Mix_CloseAudio();
    Mix_Quit();
    s_initialized = false;
    s_listener_set = false;
    printf("Audio shut down.\n");
}
