///|
/// Complete interleaved f32 audio. Encoder delay, padding and Xing frames remain.
pub struct Audio {
  sample_rate : Int
  channels : Int
  samples : Array[Float]
}

///|
pub(all) suberror Mp3Error {
  InvalidHeader(Int64)
  InvalidTag
  TruncatedFrame(Int64, Int)
  UnsupportedVersion
  UnsupportedFreeFormat
  OutputLimit
  NoAudio
  DecodeFailure(Int64, @layer3.DecodeError)
  FramingFailure(Int64, @framing.FrameError)
  FormatChange(Int64)
  InvalidLimits
  InvalidInputOffset(Int)
  InputFinished
  FailedDecoder
} derive(Eq, Show, Debug)

///|
/// Decode all supported Layer III versions using the incremental state machine.
/// max_output_samples counts interleaved samples and applies only to this helper.
pub fn decode_all(
  data : Bytes,
  limits? : Limits = Limits::default(),
) -> Audio raise Mp3Error {
  collect_audio(Decoder::new(limits~), data)
}

///|
/// Legacy strict entry point restricted to MPEG-1 with coded bitrate.
pub fn decode_mpeg1(
  data : Bytes,
  max_output_samples? : Int = 64 * 1024 * 1024,
  max_initial_scan? : Int = 65536,
) -> Audio raise Mp3Error {
  if max_output_samples < 0 || max_initial_scan < 0 {
    raise OutputLimit
  }
  let limits = { ..Limits::default(), max_output_samples, max_initial_scan, }
  let decoder = Decoder::new(limits~)
  decoder.mpeg1_only = true
  collect_audio(decoder, data)
}

///|
fn collect_audio(decoder : Decoder, data : Bytes) -> Audio raise Mp3Error {
  let output : Array[Float] = []
  let mut sample_rate = 0
  let mut channels = 0
  let mut position = 0
  while true {
    position += decoder.push(data, offset=position)
    if position == data.length() {
      decoder.finish_input()
    }
    while true {
      match decoder.next_frame() {
        NeedMoreInput => break
        EndOfInput => {
          if channels == 0 {
            raise NoAudio
          }
          return { sample_rate, channels, samples: output, }
        }
        Frame(frame) => {
          if frame.samples.length() >
            decoder.limits.max_output_samples - output.length() {
            raise OutputLimit
          }
          sample_rate = frame.sample_rate
          channels = frame.channels
          for sample in frame.samples {
            output.push(sample)
          }
        }
      }
    }
  } nobreak {
    raise NoAudio
  }
}