/// Bounded AV1 tile-group payload extraction.
///
/// The frame-header parser still exposes the legacy one-tile payload directly.
/// This helper is a separate, side-effect-free boundary for the next multi-tile
/// stage: it validates the tile-count and size-prefix envelope, then returns
/// independent byte arrays for each tile.  It does not attempt entropy decoding.

///|
/// A validated tile payload and its position in a row-major tile group.
pub(all) struct Av1TilePayload {
  index : Int
  row : Int
  col : Int
  offset : Int
  length : Int
  bytes : Array[Byte]
}

///|
/// Extract tile payloads from a tile-group byte range.
///
/// `tile_size_bytes` is the AV1 `tile_size_bytes` value (1..4).  Every tile
/// except the last is preceded by a little-endian `tile_size_minus_1` field of
/// that width; the last tile consumes the remaining bytes.  A zero value is
/// accepted only for a single tile and means that the caller supplied the
/// already isolated legacy payload.  All arithmetic is checked before slicing
/// so malformed prefixes cannot expose out-of-range data.
pub fn av1_extract_tile_payloads(
  payload : Array[Byte],
  tile_cols : Int,
  tile_rows : Int,
  tile_size_bytes : Int,
) -> Array[Av1TilePayload]? {
  if tile_cols <= 0 || tile_rows <= 0 || tile_cols > 1024 || tile_rows > 1024 {
    return None
  }
  if tile_size_bytes < 0 ||
    tile_size_bytes > 4 ||
    (tile_size_bytes == 0 && (tile_cols != 1 || tile_rows != 1)) {
    return None
  }
  let tile_count = tile_cols * tile_rows
  if tile_count <= 0 || tile_count > 1_048_576 || payload.length() == 0 {
    return None
  }
  if tile_count == 1 && tile_size_bytes == 0 {
    let bytes : Array[Byte] = []
    for b in payload {
      bytes.push(b)
    }
    return Some([
      { index: 0, row: 0, col: 0, offset: 0, length: bytes.length(), bytes, },
    ])
  }
  if tile_size_bytes == 0 {
    return None
  }
  let mut cursor = 0
  let out : Array[Av1TilePayload] = []
  for index in 0.. payload.length() {
      return None
    }
    let length = if prefix == 0 {
      payload.length() - cursor
    } else {
      let mut minus_one = 0
      for i in 0..= payload.length() - cursor - prefix {
        return None
      }
      minus_one + 1
    }
    let data_start = cursor + prefix
    if length <= 0 || data_start + length > payload.length() {
      return None
    }
    let bytes : Array[Byte] = []
    for i in data_start..<(data_start + length) {
      bytes.push(payload[i])
    }
    out.push({
      index,
      row: index / tile_cols,
      col: index % tile_cols,
      offset: data_start,
      length,
      bytes,
    })
    cursor = data_start + length
  }
  if cursor != payload.length() {
    return None
  }
  Some(out)
}