///|
fn riff_prefix(total_minus_8 : Int) -> Array[Int] {
  [
    82,
    73,
    70,
    70,
    total_minus_8 & 0xff,
    (total_minus_8 >> 8) & 0xff,
    (total_minus_8 >> 16) & 0xff,
    (total_minus_8 >> 24) & 0xff,
    87,
    65,
    86,
    69,
  ]
}

///|
fn fmt_chunk(
  audio_format : Int,
  channels : Int,
  sample_rate : Int,
  bits : Int,
) -> Array[Int] {
  let block_align = channels * bits / 8
  let byte_rate = sample_rate * block_align
  [
    102,
    109,
    116,
    32,
    16,
    0,
    0,
    0,
    audio_format & 0xff,
    (audio_format >> 8) & 0xff,
    channels & 0xff,
    (channels >> 8) & 0xff,
    sample_rate & 0xff,
    (sample_rate >> 8) & 0xff,
    (sample_rate >> 16) & 0xff,
    (sample_rate >> 24) & 0xff,
    byte_rate & 0xff,
    (byte_rate >> 8) & 0xff,
    (byte_rate >> 16) & 0xff,
    (byte_rate >> 24) & 0xff,
    block_align & 0xff,
    (block_align >> 8) & 0xff,
    bits & 0xff,
    (bits >> 8) & 0xff,
  ]
}

///|
fn data_chunk(data : Array[Int]) -> Array[Int] {
  let size = data.length()
  let out = [
    100,
    97,
    116,
    97,
    size & 0xff,
    (size >> 8) & 0xff,
    (size >> 16) & 0xff,
    (size >> 24) & 0xff,
  ]
  for byte in data {
    out.push(byte)
  }
  if size % 2 != 0 {
    out.push(0)
  }
  out
}

///|
fn concat_arrays(a : Array[Int], b : Array[Int]) -> Array[Int] {
  let out : Array[Int] = []
  for value in a {
    out.push(value)
  }
  for value in b {
    out.push(value)
  }
  out
}

///|
fn concat3(a : Array[Int], b : Array[Int], c : Array[Int]) -> Array[Int] {
  concat_arrays(concat_arrays(a, b), c)
}

///|
pub fn fixture_pcm16_mono() -> Array[Int] {
  let payload = [0, 0, 255, 127, 0, 128, 0, 64]
  let body = concat_arrays(fmt_chunk(1, 1, 8000, 16), data_chunk(payload))
  concat_arrays(riff_prefix(4 + body.length()), body)
}

///|
pub fn fixture_pcm8_stereo() -> Array[Int] {
  let payload = [128, 128, 255, 0, 192, 64, 128, 128]
  let body = concat_arrays(fmt_chunk(1, 2, 11025, 8), data_chunk(payload))
  concat_arrays(riff_prefix(4 + body.length()), body)
}

///|
pub fn fixture_pcm24_mono() -> Array[Int] {
  let payload = [0, 0, 0, 255, 255, 127, 0, 0, 128]
  let body = concat_arrays(fmt_chunk(1, 1, 48000, 24), data_chunk(payload))
  concat_arrays(riff_prefix(4 + body.length()), body)
}

///|
pub fn fixture_float32_mono() -> Array[Int] {
  let payload = [0, 0, 0, 0, 0, 0, 128, 63, 0, 0, 128, 191]
  let body = concat_arrays(fmt_chunk(3, 1, 44100, 32), data_chunk(payload))
  concat_arrays(riff_prefix(4 + body.length()), body)
}

///|
pub fn fixture_with_info_and_cue() -> Array[Int] {
  let info_payload = [
    73, 78, 70, 79, 73, 78, 65, 77, 5, 0, 0, 0, 116, 111, 110, 101, 0, 0,
  ]
  let list = [
    76,
    73,
    83,
    84,
    info_payload.length() & 0xff,
    (info_payload.length() >> 8) & 0xff,
    0,
    0,
  ]
  for byte in info_payload {
    list.push(byte)
  }
  let cue_payload = [
    1, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 100, 97, 116, 97, 0, 0, 0, 0, 0, 0, 0, 0,
    2, 0, 0, 0,
  ]
  let cue = [
    99,
    117,
    101,
    32,
    cue_payload.length() & 0xff,
    (cue_payload.length() >> 8) & 0xff,
    0,
    0,
  ]
  for byte in cue_payload {
    cue.push(byte)
  }
  let payload = [0, 0, 0, 0]
  let body = concat3(fmt_chunk(1, 1, 8000, 16), list, cue)
  let all_body = concat_arrays(body, data_chunk(payload))
  concat_arrays(riff_prefix(4 + all_body.length()), all_body)
}

///|
pub fn fixture_not_riff() -> Array[Int] {
  [78, 79, 80, 69, 0, 0, 0, 0, 87, 65, 86, 69]
}

///|
pub fn fixture_missing_data() -> Array[Int] {
  let body = fmt_chunk(1, 1, 8000, 16)
  concat_arrays(riff_prefix(4 + body.length()), body)
}

///|
pub fn fixture_unsupported_alaw() -> Array[Int] {
  let payload = [0, 1, 2, 3]
  let body = concat_arrays(fmt_chunk(6, 1, 8000, 8), data_chunk(payload))
  concat_arrays(riff_prefix(4 + body.length()), body)
}

///|
pub fn fixture_truncated_data() -> Array[Int] {
  let payload = [0, 0, 1]
  let body = concat_arrays(fmt_chunk(1, 1, 8000, 16), data_chunk(payload))
  concat_arrays(riff_prefix(4 + body.length()), body)
}