#pragma once

#include <SDL3/SDL.h>

#include "crArray.h"
#include "crStruct.h"

typedef struct stb_vorbis stb_vorbis;

class crAudio
{
public:
    static constexpr int32_t STREAM_COUNT_SFX = 32;

    static constexpr int32_t SAMPLE_RATE = 48000;   // canonical asset rate — audio_compress.py enforces it

private:
    static constexpr int32_t BGM_CHUNK_FRAMES        = 4096;
    static constexpr int32_t BGM_TARGET_QUEUED_BYTES = ( ( SAMPLE_RATE / 2 ) * 2 * 2 );   // ~0.5s of queued stereo S16

    // samples nullptr = load failed; the entry still caches so the error logs once, not per play
    struct SfxEntry
    {
        char     name[ 64 ];
        int16_t* samples;      // interleaved stereo PCM, decoded once at load; allocated by stb_vorbis (plain malloc) — released with free()
        int32_t  frameCount;
        int32_t  sampleRate;
    };

    float _masterVolume = 0.7f;
    float _bgmVolume    = 0.7f;   // effective bgm gain = master * bgm
    float _sfxVolume    = 0.7f;   // effective sfx gain = master * sfx * per-play volume

    SDL_AudioDeviceID _device = 0;

    SDL_AudioStream*  _sfxStreams[ STREAM_COUNT_SFX ] = {};
    crArray<SfxEntry> _sfxClips;
    int16_t*          _sfxScratchBuffer = nullptr;   // pan-scaled copy reused by every PlaySfx — grown by LoadSfx to the longest clip
    int32_t           _sfxScratchFrames = 0;

    SDL_AudioStream* _bgmStream   = nullptr;
    stb_vorbis*      _bgmVorbis   = nullptr;
    void*            _bgmOgg      = nullptr;    // whole ogg stays resident while streaming
    int32_t          _bgmChannels = 0;
    bool             _bgmLoop     = false;

    int16_t _bgmDecodeChunk[ BGM_CHUNK_FRAMES * 2 ] = { 0, };

public:
    bool Init();
    void Cleanup();

    void Update();  // pumps the bgm stream — call once per frame

    void SetPaused( bool paused );

    int32_t LoadSfx( const char* baseName );   // crassets/audio/sfx/<base>.ogg, decoded once, cached by name; the handle stays valid for the app lifetime

    void PlaySfx( int32_t sfx, float volume = 1.0f, float pan = 0.0f );    // pan -1(L)..0(C)..+1(R), baked at play start

    void PlayBgm( const char* baseName, bool loop );   // crassets/audio/bgm/<base>.ogg, streamed — no handle, opened on play
    void StopBgm();

    void SetMasterVolume( float volume );   // bgm applies immediately; sfx from their next play
    void SetBgmVolume( float volume );
    void SetSfxVolume( float volume );      // applies from the next PlaySfx

    float GetMasterVolume() const { return _masterVolume; }
    float GetBgmVolume() const { return _bgmVolume; }
    float GetSfxVolume() const { return _sfxVolume; }

    int32_t     SfxCount() const { return _sfxClips.Size(); }
    const char* SfxName( int32_t sfx ) const { return _sfxClips.At( sfx ).name; }

private:
    void PumpBgm();
    void CloseBgmDecoder(); // stops decoding; audio already queued on the stream drains out
};
