///|
/// Non-owning reader over byte arrays. Values outside 0..255 are masked so
/// generated fixtures and host-provided byte arrays behave consistently.
pub(all) struct ByteReader {
  bytes : Array[Int]
  offset : Int
} derive(Eq, @debug.Debug)

///|
pub fn ByteReader::new(bytes : Array[Int]) -> ByteReader {
  { bytes, offset: 0 }
}

///|
pub fn ByteReader::at(bytes : Array[Int], offset : Int) -> ByteReader {
  { bytes, offset }
}

///|
pub fn ByteReader::length(self : ByteReader) -> Int {
  self.bytes.length()
}

///|
pub fn ByteReader::remaining(self : ByteReader) -> Int {
  self.bytes.length() - self.offset
}

///|
pub fn ByteReader::can_read(self : ByteReader, count : Int) -> Bool {
  count >= 0 && self.offset >= 0 && self.offset + count <= self.bytes.length()
}

///|
pub fn ByteReader::slice_can_read(
  self : ByteReader,
  offset : Int,
  count : Int,
) -> Bool {
  offset >= 0 && count >= 0 && offset + count <= self.bytes.length()
}

///|
pub fn ByteReader::byte_at(self : ByteReader, offset : Int) -> Int {
  if self.slice_can_read(offset, 1) {
    self.bytes[offset] & 0xff
  } else {
    0
  }
}

///|
pub fn ByteReader::u16_le_at(self : ByteReader, offset : Int) -> Int {
  self.byte_at(offset) | (self.byte_at(offset + 1) << 8)
}

///|
pub fn ByteReader::u32_le_at(self : ByteReader, offset : Int) -> Int {
  self.byte_at(offset) |
  (self.byte_at(offset + 1) << 8) |
  (self.byte_at(offset + 2) << 16) |
  (self.byte_at(offset + 3) << 24)
}

///|
pub fn ByteReader::fourcc_at(self : ByteReader, offset : Int) -> String {
  if !self.slice_can_read(offset, 4) {
    ""
  } else {
    String::from_array([
      self.byte_at(offset).to_uint16().unsafe_to_char(),
      self.byte_at(offset + 1).to_uint16().unsafe_to_char(),
      self.byte_at(offset + 2).to_uint16().unsafe_to_char(),
      self.byte_at(offset + 3).to_uint16().unsafe_to_char(),
    ])
  }
}

///|
pub fn ByteReader::matches_u16(
  self : ByteReader,
  offset : Int,
  value : Int,
) -> Bool {
  self.slice_can_read(offset, 2) && self.u16_le_at(offset) == value
}

///|
pub fn ByteReader::ascii_z_at(
  self : ByteReader,
  offset : Int,
  limit : Int,
) -> String {
  if offset < 0 || limit <= 0 || offset >= self.length() {
    ""
  } else {
    let end = if offset + limit <= self.length() {
      offset + limit
    } else {
      self.length()
    }
    for i in offset..= 32 && b <= 126 {
        continue acc + String::from_array([b.to_uint16().unsafe_to_char()])
      } else {
        continue acc
      }
    } nobreak {
      acc
    }
  }
}

///|
pub fn ByteReader::ascii_fixed_at(
  self : ByteReader,
  offset : Int,
  size : Int,
) -> String {
  if size <= 0 || !self.slice_can_read(offset, size) {
    ""
  } else {
    for i in 0..= 32 && b <= 126 {
        continue acc + String::from_array([b.to_uint16().unsafe_to_char()])
      } else {
        continue acc
      }
    } nobreak {
      acc
    }
  }
}

///|
pub fn ByteReader::slice(
  self : ByteReader,
  offset : Int,
  size : Int,
) -> Array[Int] {
  if !self.slice_can_read(offset, size) {
    []
  } else {
    Array::makei(size, i => self.byte_at(offset + i))
  }
}