//! Decoder dispatch for [`Track`] — implements [`Track::decode_to_pcm16`] and
//! [`Track::to_ogg_bytes`].
//!
//! - [`msadpcm`] — full MS-ADPCM → PCM16 software decoder.
//! - [`ogg_vorbis`] — OGG Vorbis deobfuscation (XOR passthrough for version 2 and 3).

use tracing::{info, info_span};

use crate::error::{Error, Result};
use crate::scd::format::ScdFormat;
use crate::scd::track::{AdpcmWaveFormat, ExtraData, Track};
use crate::tracing_schema::span_names;

pub(crate) mod msadpcm;
pub(crate) mod ogg_vorbis;

#[cfg(test)]
mod tests;

impl Track<'_> {
    /// Decodes the track payload to interleaved PCM16 samples.
    ///
    /// Only [`ScdFormat::MsAdpcm`] is implemented; all other formats return a dedicated
    /// "unsupported" error rather than panicking.  [`ScdFormat::Empty`] returns an empty `Vec`.
    ///
    /// Emits a `scd.decode` tracing event on success.
    pub fn decode_to_pcm16(&self) -> Result<Vec<i16>> {
        let format = self.format();
        let bytes_in = self.raw_bytes().len();
        let span = info_span!(span_names::SCD_DECODE, format = format.as_raw(), bytes_in);
        let _enter = span.enter();
        let start = std::time::Instant::now();

        let samples = match format {
            ScdFormat::Empty => Vec::new(),
            ScdFormat::MsAdpcm => decode_msadpcm(self)?,
            ScdFormat::OggVorbis => return Err(Error::OggVorbisDecodingUnsupported),
            ScdFormat::Mp3 => return Err(Error::Mp3DecodingUnsupported),
            ScdFormat::Atrac9 => return Err(Error::Atrac9DecodingUnsupported),
            ScdFormat::Unknown(raw) => return Err(Error::UnknownCodecDecodingUnsupported { raw }),
        };

        let elapsed_ms = u64::try_from(start.elapsed().as_millis()).unwrap_or(u64::MAX);
        info!(
            target: "scd",
            format = format.as_raw(),
            bytes_in,
            samples_out = samples.len(),
            elapsed_ms,
            "scd.decode",
        );

        Ok(samples)
    }

    /// Returns a deobfuscated, self-contained OGG Vorbis byte stream.
    ///
    /// Requires the track format to be [`ScdFormat::OggVorbis`]; returns
    /// [`Error::OggBytesNotApplicable`] for all other formats.
    ///
    /// The deobfuscation is handled by [`ogg_vorbis::deobfuscate`]: version-2 tracks that
    /// already start with `OggS` are returned unchanged (modern game data passthrough).
    pub fn to_ogg_bytes(&self) -> Result<Vec<u8>> {
        match *self.extra_data() {
            ExtraData::OggVorbis { header, .. } => {
                let bytes = self.raw_bytes();
                ogg_vorbis::deobfuscate(&header, bytes)
            }
            _ => Err(Error::OggBytesNotApplicable),
        }
    }
}

fn decode_msadpcm(track: &Track<'_>) -> Result<Vec<i16>> {
    let fmt: AdpcmWaveFormat = match *track.extra_data() {
        ExtraData::MsAdpcm(f) => f,
        _ => {
            return Err(Error::InvalidMsAdpcmBlock {
                reason: "MsAdpcm track missing AdpcmWaveFormat extra-data",
            });
        }
    };
    msadpcm::decode_to_pcm16(&fmt, track.raw_bytes())
}
