//
// Audio playback. Downloads the track as OGG Vorbis (Jellyfin transcodes
// everything server-side), decodes locally to s16 PCM via stb_vorbis, and
// hands the LINEAR_SAMPLE_ARRAY to Sound_Player. The visualizer reads from
// the same sd.samples buffer — single source of truth, no parallel decoder,
// no cursor desync.
//
// We own the Sound_Data struct, the stb_vorbis-malloc'd PCM buffer (held in
// sd.samples / sd.buffer), and the Track copy. All freed in
// sound_release_callback when Sound_Player is finished with the stream.
//

audio_play_track :: (track: Track) {
    if !app.audio_inited  return;

    pending := New(Track);
    pending.* = clone_track(track);

    // Ask Jellyfin to transcode everything to OGG Vorbis 192 kbps.
    // We pin audioSampleRate to Sound_Player's actual output device rate so
    // the OGG file's sample rate matches what Sound_Player expects when
    // calculating desired_rate (= sound_data.sampling_rate / output_rate).
    // Without this, Jellyfin defaults to 48000 Hz on many systems while
    // sound_data.sampling_rate stays at 44100 → plays at 91.9% speed.
    output_hz := Sound.get_audio_sampling_rate();
    if output_hz <= 0  output_hz = 44100;
    path := tprint(
        "/Audio/%/universal?container=ogg&audioCodec=vorbis&maxStreamingBitrate=192000&audioSampleRate=%&audioChannels=2&userId=%&deviceId=%&api_key=%",
        track.id, output_hz, app.jellyfin.user_id, app.jellyfin.device_id, app.jellyfin.auth_token,
    );
    log_info("audio: downloading '%' (% ticks)", track.name, track.duration_ticks);
    http_submit("GET", path, on_done=on_track_downloaded, user_data=pending);
}

audio_toggle_pause :: () {
    if !app.current_stream  return;
    s := app.current_stream;
    app.paused = !app.paused;
    if app.paused {
        s.current_rate = 0;
        s.desired_rate = 0;
        s.inaudible    = true;
    } else {
        s.current_rate = 1;
        s.desired_rate = 1;
        s.inaudible    = false;
    }
    media_controls_notify_status();
}

audio_is_paused :: () -> bool {
    return app.paused;
}

audio_seek_seconds :: (seconds: float) {
    if !app.current_stream || !app.current_stream.sound_data  return;
    rate   := cast(float64) app.current_stream.sound_data.sampling_rate;
    target := cast(float64) seconds * rate;
    if target < 0  target = 0;
    app.current_stream.play_cursor = target;
}

stop_current_stream :: () {
    if !app.current_stream  return;
    Sound.stop_stream_abruptly(app.current_stream.entity_id);
    app.current_stream = null;
}

// Called by Sound_Player when it is finished with a stream (natural end or
// stop_stream_abruptly). We own the Sound_Data struct and the OGG bytes in
// sound_data.buffer; free both here.
sound_release_callback :: (stream: *Sound.Sound_Stream, data: *Sound.Sound_Data) {
    free(data.buffer);
    free(data.name);
    free(data);

    if stream && stream.entity_id == app.track_entity_id {
        app.track_finished = true;
        app.current_stream = null;
    }
}

clone_track :: (t: Track) -> Track {
    out: Track;
    out.id             = copy_string(t.id);
    out.name           = copy_string(t.name);
    out.album          = copy_string(t.album);
    out.album_id       = copy_string(t.album_id);
    out.artist         = copy_string(t.artist);
    out.artist_id      = copy_string(t.artist_id);
    out.duration_ticks = t.duration_ticks;
    out.index_number   = t.index_number;
    out.is_favourite   = t.is_favourite;
    return out;
}

free_track :: (t: *Track) {
    free(t.id);
    free(t.name);
    free(t.album);
    free(t.album_id);
    free(t.artist);
    free(t.artist_id);
    t.* = .{};
}

#scope_file

on_track_downloaded :: (task: *Http_Task) {
    pending := cast(*Track) task.user_data;
    defer { free_track(pending); free(pending); }

    if !task.response.ok {
        log_error("audio download '%' failed: status=%", pending.name, task.response.status_code);
        return;
    }

    bytes := task.response.body;
    task.response.body = "";
    defer free(bytes);  // stb_vorbis_decode_memory copies what it needs

    sd, ok := decode_ogg(bytes, pending.name);
    if !ok  return;

    data := New(Sound.Sound_Data);
    data.* = sd;

    stop_current_stream();
    free_track(*app.current_track);
    app.current_track = clone_track(pending.*);
    app.paused = false;

    app.track_entity_id += 1;
    stream := Sound.make_stream(data, .MUSIC);
    stream.entity_id = app.track_entity_id;
    Sound.start_playing(stream);

    app.current_stream = stream;
    app.track_finished = false;
    media_controls_notify_track();
    lyrics_request(app.current_track.id);
    log_info("audio: playing '% — %' artist_id=%",
        app.current_track.artist, app.current_track.name, app.current_track.artist_id);
}
