//
// Format-aware decoder. Detects MP3 / FLAC / OGG / WAV from the leading bytes
// and routes through dr_mp3/dr_flac (vendored, modules/audio_decoders) for the
// formats Sound_Player doesn't natively handle.
//
// Native formats (OGG, WAV) go through Sound.load_audio_data unchanged.
// MP3/FLAC are decoded to interleaved s16 PCM and wrapped in a Sound_Data
// with type = .LINEAR_SAMPLE_ARRAY.
//

Audio_Format :: enum {
    UNKNOWN;
    OGG;
    WAV;
    MP3;
    FLAC;
}

detect_audio_format :: (bytes: string) -> Audio_Format {
    if bytes.count < 4  return .UNKNOWN;

    b := bytes.data;
    // MP3: ID3 tag
    if b[0] == #char "I" && b[1] == #char "D" && b[2] == #char "3"   return .MP3;
    // MP3: frame sync 0xFFEx (also 0xFFFx for MPEG-1, 0xFFEx for MPEG-2)
    if b[0] == 0xFF && (b[1] & 0xE0) == 0xE0                          return .MP3;
    // FLAC: "fLaC"
    if b[0] == #char "f" && b[1] == #char "L" && b[2] == #char "a" && b[3] == #char "C"  return .FLAC;
    // OGG: "OggS"
    if b[0] == #char "O" && b[1] == #char "g" && b[2] == #char "g" && b[3] == #char "S"  return .OGG;
    // WAV: "RIFF"
    if b[0] == #char "R" && b[1] == #char "I" && b[2] == #char "F" && b[3] == #char "F"  return .WAV;

    return .UNKNOWN;
}

//
// Decode `bytes` into a Sound_Data ready to feed to Sound.make_stream().
//
// Ownership notes:
// - On the OGG/WAV path, Sound_Player keeps `bytes` alive via Sound_Data.buffer.
// - On the MP3/FLAC path, we call decoder_free on the original bytes immediately
//   (we have decoded to a separate s16 PCM buffer); the Sound_Data references
//   the dr_libs-malloc'd PCM buffer instead.
//
// TODO: free Sound_Data + its buffer in the release_asset path. Today we leak.
//
decode_audio :: (bytes: string, name: string) -> Sound.Sound_Data, Audio_Format, bool {
    format := detect_audio_format(bytes);
    log_info("decode_audio: '%' bytes=% format=%", name, bytes.count, format);

    if format == .OGG || format == .WAV {
        result := Sound.load_audio_data(name, bytes);
        return result, format, result.loaded;
    }

    if format == .MP3 || format == .FLAC {
        channels:     u32;
        sample_rate:  u32;
        total_frames: u64;

        samples: *s16;
        if format == .MP3 {
            samples = Audio_Decoders.decode_mp3(
                bytes.data, cast(u64) bytes.count,
                *channels, *sample_rate, *total_frames);
        } else {
            samples = Audio_Decoders.decode_flac(
                bytes.data, cast(u64) bytes.count,
                *channels, *sample_rate, *total_frames);
        }

        if !samples {
            log_error("decode_audio: decoder failed for '%'", name);
            return .{}, format, false;
        }

        // Source bytes are no longer needed.
        free(bytes);

        total_samples := total_frames * cast(u64) channels;

        result: Sound.Sound_Data;
        result.name                     = copy_string(name);
        result.loaded                   = true;
        result.type                     = .LINEAR_SAMPLE_ARRAY;
        result.nchannels                = cast(u16) channels;
        result.sampling_rate            = sample_rate;
        result.nsamples_times_nchannels = cast(s64) total_samples;
        result.samples                  = samples;
        result.buffer.count             = cast(s64) (total_samples * size_of(s16));
        result.buffer.data              = cast(*u8) samples;

        log_info("decode_audio: % ch=%, rate=%, frames=%", format, channels, sample_rate, total_frames);
        return result, format, true;
    }

    if bytes.count >= 8 && bytes.data {
        b := bytes.data;
        log_error("decode_audio: unknown format for '%' (% bytes; first 8: %x %x %x %x %x %x %x %x)",
                  name, bytes.count,
                  formatInt(b[0], base=16), formatInt(b[1], base=16),
                  formatInt(b[2], base=16), formatInt(b[3], base=16),
                  formatInt(b[4], base=16), formatInt(b[5], base=16),
                  formatInt(b[6], base=16), formatInt(b[7], base=16));
    } else {
        log_error("decode_audio: unknown format for '%' (% bytes — too short or null)", name, bytes.count);
    }
    return .{}, format, false;
}
