///|
/// Native-depth reconstructed frame. Coded planes retain the MI padding until
/// every tile has been assembled and the frame filters have completed.
priv struct Av1FramePlanes {
  width : Int
  height : Int
  coded_width : Int
  bit_depth : Int
  full_range : Bool
  matrix_coefficients : Int
  color_primaries : Int
  transfer_characteristics : Int
  /// Chroma subsampling shifts (0 or 1) for planes 1 and 2.
  sub_x : Int
  sub_y : Int
  planes : Array[Array[Int]]
}

///|
/// Per-axis chroma divisors for a plane: (1, 1) for luma, 2^sub otherwise.
/// The coded luma extent is always a multiple of eight, so a shift is exact.
fn av1_frame_plane_divisor(frame : Av1FramePlanes, plane : Int) -> (Int, Int) {
  if plane == 0 {
    (1, 1)
  } else {
    (1 << frame.sub_x, 1 << frame.sub_y)
  }
}

///|
fn av1_frame_crop(frame : Av1FramePlanes, plane : Int) -> Array[Int] {
  let (divisor_x, divisor_y) = av1_frame_plane_divisor(frame, plane)
  let width = (frame.width + divisor_x - 1) / divisor_x
  let height = (frame.height + divisor_y - 1) / divisor_y
  let stride = frame.coded_width / divisor_x
  Array::makei(width * height, fn(i) {
    frame.planes[plane][i / width * stride + i % width]
  })
}

///|
fn av1_frame_to_rgba(frame : Av1FramePlanes) -> Image? {
  if frame.planes.length() == 1 {
    let samples = av1_frame_crop(frame, 0)
    let image = Image::new(frame.width, frame.height)
    let shift = frame.bit_depth - 8
    let black = if frame.full_range { 0 } else { 16 << shift }
    let range = if frame.full_range {
      (1 << frame.bit_depth) - 1
    } else {
      219 << shift
    }
    for i in 0.. Array[Int]? {
  if length <= 0 ||
    length > MAX_IMAGE_PIXELS ||
    (superblock_pixels != 64 && superblock_pixels != 128) ||
    starts.length() < 2 ||
    starts.length() > 1025 ||
    starts[0] != 0 {
    return None
  }
  let count = (length - 1) / superblock_pixels + 1
  if starts[starts.length() - 1] != count {
    return None
  }
  let output : Array[Int] = [0]
  for i in 1.. count {
      return None
    }
    output.push(
      if i == starts.length() - 1 {
        length
      } else {
        starts[i] * superblock_pixels
      },
    )
  }
  Some(output)
}

///|
fn av1_frame_pixel_starts_valid(
  starts : Array[Int],
  length : Int,
  frame_filters : Bool,
) -> Bool {
  if starts.length() < 2 ||
    starts.length() > 1025 ||
    starts[0] != 0 ||
    starts[starts.length() - 1] != length {
    return false
  }
  for i in 1.. length {
      return false
    }
    if i + 1 < starts.length() &&
      (starts[i] % 8 != 0 || (frame_filters && starts[i] % 64 != 0)) {
      return false
    }
  }
  true
}

///|
/// Apply film grain to the visible picture after reconstruction.
///
/// Grain synthesis (AV1 §7.18.3) runs on the output picture, so the planes are
/// cropped first. The grained samples are scattered back into the same padded
/// layout, leaving the crop-on-convert path unchanged.
fn av1_frame_apply_grain(
  frame : Av1FramePlanes,
  params : Av1FilmGrainParams,
  monochrome : Bool,
  subsampling_x : Bool,
  subsampling_y : Bool,
) -> Av1FramePlanes? {
  if !params.apply_grain {
    return Some(frame)
  }
  let cropped : Array[Array[Int]] = []
  for p in 0.. None
    Some(_) => {
      let display = {
        ..frame,
        planes: frame.planes.map(samples => samples.copy()),
      }
      for p in 0.. (Av1FramePlanes, Av1RestorationStripeStore?, Av1FrameCdfs?)? {
  let output_width = if upscaled_width == 0 { width } else { upscaled_width }
  let sub_x = if subsampling_x { 1 } else { 0 }
  let sub_y = if subsampling_y { 1 } else { 0 }
  if width <= 0 ||
    height <= 0 ||
    width > MAX_IMAGE_PIXELS / height ||
    output_width < width ||
    output_width > 65536 ||
    output_width > MAX_IMAGE_PIXELS / height ||
    output_width > width * 2 ||
    (bit_depth != 8 && bit_depth != 10 && bit_depth != 12) ||
    !av1_frame_pixel_starts_valid(
      col_starts,
      width,
      cdef is Some(_) || loop_filter is Some(_) || output_width != width,
    ) ||
    !av1_frame_pixel_starts_valid(
      row_starts,
      height,
      cdef is Some(_) || loop_filter is Some(_) || output_width != width,
    ) {
    return None
  }
  let coded_lossless = av1_frame_coded_lossless(
    base_q_idx,
    quant_deltas,
    segmentation.unwrap_or(av1_segmentation_empty()),
  )
  if cdef is Some(config) {
    if coded_lossless || !av1_cdef_config_valid(config, monochrome~) {
      return None
    }
  }
  if restoration is Some(config) {
    if !av1_restoration_config_valid(config, monochrome~) {
      return None
    }
  }
  let mut filter_active = false
  if loop_filter is Some(config) {
    if coded_lossless || !av1_loop_filter_config_valid(config) {
      return None
    }
    if delta_lf_present {
      filter_active = true
    }
    if segmentation is Some(params) {
      for id in 0..<8 {
        for feature in 1..<5 {
          if av1_segment_feature(params, id, feature) {
            filter_active = true
          }
        }
      }
    }
    for plane in 0..<(if monochrome { 1 } else { 3 }) {
      for direction in 0..<2 {
        // Any reference or mode delta can lift an edge, so the frame is only
        // inert when none of the eight strengths selects a non-zero level.
        for reference in 0..<8 {
          for mode_type in 0..<2 {
            let level = match
              av1_loop_filter_edge_level(
                config, plane, direction, reference, mode_type,
              ) {
              Some(value) => value
              None => 0
            }
            if level > 0 {
              filter_active = true
            }
          }
        }
      }
    }
  }
  let coded_width = av1_partition_coded_extent(width)
  let coded_height = av1_partition_coded_extent(height)
  if coded_width > MAX_IMAGE_PIXELS / coded_height {
    return None
  }
  let tiles = match
    av1_extract_tile_payloads(
      payload,
      col_starts.length() - 1,
      row_starts.length() - 1,
      tile_size_bytes,
    ) {
    Some(value) => value
    None => return None
  }
  if context_update_tile_id < 0 || context_update_tile_id >= tiles.length() {
    return None
  }
  let chroma_coded_w = (coded_width + (1 << sub_x) - 1) >> sub_x
  let chroma_coded_h = (coded_height + (1 << sub_y) - 1) >> sub_y
  let planes : Array[Array[Int]] = [Array::make(coded_width * coded_height, 0)]
  if !monochrome {
    planes.push(Array::make(chroma_coded_w * chroma_coded_h, 0))
    planes.push(Array::make(chroma_coded_w * chroma_coded_h, 0))
  }
  let tx_sizes : Array[Array[Int]] = []
  if filter_active {
    for plane in 0..
      av1_restoration_layout(
        config,
        width,
        output_width,
        height,
        monochrome,
        !monochrome && sub_x == 1,
        !monochrome && sub_y == 1,
        superres_denom,
      )
    None => None
  }
  // Per-plane unit map indexed by unit_row * unit_cols + unit_col. None
  // marks an unread grid position, distinct from a decoded-None unit.
  let lr_units : Array[Array[Av1RestorationUnit?]] = []
  match lr_layout {
    Some(layout) =>
      for plane in 0.. ()
  }
  for tile in tiles {
    let left = col_starts[tile.col]
    let top = row_starts[tile.row]
    let tile_width = col_starts[tile.col + 1] - left
    let tile_height = row_starts[tile.row + 1] - top
    let tile_cols4 = av1_partition_coded_extent(tile_width) / 4
    let tile_rows4 = av1_partition_coded_extent(tile_height) / 4
    let previous_tile = if previous_seg_ids.length() == segment_ids.length() {
      Array::makei(tile_cols4 * tile_rows4, i => {
        previous_seg_ids[(top / 4 + i / tile_cols4) * mi_cols +
        left / 4 +
        i % tile_cols4]
      })
    } else {
      []
    }
    let tile_inter = match inter {
      Some(config) =>
        Some(
          av1_inter_info(
            config,
            top / 4,
            left / 4,
            (top + av1_partition_coded_extent(tile_height)) / 4,
            (left + av1_partition_coded_extent(tile_width)) / 4,
            cdfs~,
          ),
        )
      None => None
    }
    let state = match
      av1_decode_intra_tile_state(
        tile_width,
        tile_height,
        tile.bytes,
        base_q_idx,
        disable_cdf_update,
        allow_screen_content_tools,
        use_128x128_superblock,
        bit_depth,
        reduced_tx_set,
        enable_filter_intra,
        tx_mode_select,
        cdef~,
        monochrome~,
        enable_intra_edge_filter~,
        track_loop_filter=filter_active,
        restoration_layout=lr_layout,
        tile_origin_row_mi=top / 4,
        tile_origin_col_mi=left / 4,
        quant_deltas~,
        sub_x~,
        sub_y~,
        inter=tile_inter,
        segmentation~,
        delta_q_present~,
        delta_q_res~,
        quant_matrix~,
        allow_intrabc~,
        frame_mi_cols=mi_cols,
        frame_mi_rows=coded_height / 4,
        previous_seg_ids=previous_tile,
        delta_lf_present~,
        delta_lf_res~,
        delta_lf_multi~,
        // Every tile starts from the same frame context; only the selected
        // tile contributes adapted rows to the frame-end snapshot.
        frame_cdfs=cdfs,
        cdfs_out=if tile.index == context_update_tile_id {
          cdfs_out
        } else {
          None
        },
      ) {
      Some(value) => value
      None => return None
    }
    for p in 0.. Some(config.field)
    None => None
  }
  if filter_active &&
    !av1_loop_filter_frame(
      loop_filter.unwrap(),
      width,
      height,
      coded_width,
      coded_height,
      bit_depth,
      planes,
      tx_sizes,
      sub_x~,
      sub_y~,
      field~,
      block_map=Some({
        cols: mi_cols,
        rows: coded_height / 4,
        segment_ids,
        deltas: lf_deltas,
        segmentation: segmentation.unwrap_or(av1_segmentation_empty()),
      }),
    ) {
    return None
  }
  // Save the deblocked rows at every internal stripe boundary before
  // CDEF modifies them: loop restoration reads these rows instead of
  // the neighbouring stripe's post-CDEF output. Only active when some
  // plane carries a non-None restoration mode; a failed save aborts the
  // decode because the boundary context would be lost.
  let stripe_store : Av1RestorationStripeStore? = match restoration {
    Some(config) =>
      if av1_restoration_active(config) {
        match
          av1_restoration_save_stripes(
            planes,
            coded_width,
            width,
            output_width,
            height,
            bit_depth,
            monochrome,
            sub_x~,
            sub_y~,
          ) {
          Some(value) => Some(value)
          None => return None
        }
      } else {
        None
      }
    None => None
  }
  let filtered = match cdef {
    None => planes
    Some(config) =>
      match
        av1_cdef_filter_frame(
          coded_width,
          coded_height,
          planes,
          skips,
          indices,
          config,
          bit_depth,
          sub_x~,
          sub_y~,
        ) {
        Some(value) => value
        None => return None
      }
  }
  let base : Av1FramePlanes = {
    width,
    height,
    coded_width,
    bit_depth,
    full_range,
    matrix_coefficients,
    color_primaries,
    transfer_characteristics,
    sub_x,
    sub_y,
    planes: filtered,
  }
  let frame = if output_width == width {
    base
  } else {
    match av1_frame_upscale(base, output_width) {
      Some(value) => value
      None => return None
    }
  }
  // Apply loop restoration on the upscaled post-CDEF frame. The source
  // view keeps the saved deblocked rows and the unit parameters come
  // from the per-tile entropy state; an active configuration always
  // has a saved store, and filtering failure aborts the decode.
  let frame = match (stripe_store, lr_layout) {
    (Some(store), Some(layout)) => {
      let strides : Array[Int] = Array::makei(frame.planes.length(), fn(i) {
        if i == 0 {
          frame.coded_width
        } else {
          frame.coded_width >> frame.sub_x
        }
      })
      let view : Av1RestorationSourceView = {
        store,
        frame: frame.planes,
        frame_strides: strides,
      }
      match
        av1_restoration_filter_frame(
          view,
          lr_units,
          layout,
          bit_depth,
          monochrome,
          subsampling_x=!monochrome && frame.sub_x == 1,
          subsampling_y=!monochrome && frame.sub_y == 1,
        ) {
        Some(value) => {
          let planes = frame.planes
          for plane in 0.. return None
      }
    }
    _ => frame
  }
  // The frame context after decoding: the context_update_tile_id tile's final
  // rows when a capture was requested, otherwise nothing to hand back.
  let frame_cdfs : Av1FrameCdfs? = match cdfs_out {
    Some(sink) => if sink.length() == 0 { None } else { sink[0] }
    None => None
  }
  Some((frame, stripe_store, frame_cdfs))
}

///|
/// Upscale complete native rows after deblocking and CDEF. Source MI padding
/// participates in the horizontal taps; output padding is only buffer layout.
fn av1_frame_upscale(frame : Av1FramePlanes, width : Int) -> Av1FramePlanes? {
  let coded_width = av1_partition_coded_extent(width)
  let coded_height = av1_partition_coded_extent(frame.height)
  if coded_width > MAX_IMAGE_PIXELS / coded_height {
    return None
  }
  let planes : Array[Array[Int]] = []
  for plane in 0.. value
      None => return None
    }
    let padded = Array::makei(stride * (coded_height / divisor_y), i => {
      let row = if i / stride < height { i / stride } else { height - 1 }
      let col = if i % stride < target_width {
        i % stride
      } else {
        target_width - 1
      }
      samples[row * target_width + col]
    })
    planes.push(padded)
  }
  Some({
    width,
    height: frame.height,
    coded_width,
    planes,
    bit_depth: frame.bit_depth,
    full_range: frame.full_range,
    matrix_coefficients: frame.matrix_coefficients,
    color_primaries: frame.color_primaries,
    transfer_characteristics: frame.transfer_characteristics,
    sub_x: frame.sub_x,
    sub_y: frame.sub_y,
  })
}

///|
fn av1_decode_frame_planes(
  data : Array[Byte],
  allow_monochrome? : Bool = false,
  map? : Av1FrameMap,
  sequence? : Av1SequenceInfo,
  operating_point? : Int = 0,
  spatial_layer? : Int? = None,
  budget? : DecodeBudget? = None,
) -> Av1FramePlanes? {
  let state = match map {
    Some(value) => value
    None => av1_frame_map()
  }
  let (_, displayed) = match
    av1_decode_obu_planes(
      data,
      state,
      sequence,
      allow_monochrome,
      operating_point~,
      spatial_layer~,
      budget~,
    ) {
    Some(value) => value
    None => return None
  }
  if displayed.is_empty() {
    None
  } else {
    Some(displayed[0])
  }
}

///|
/// Decode and save one assembled frame. Presentation (show_frame and grain)
/// belongs to the OBU sequencer, so hidden frames take this exact same path.
fn av1_decode_parsed_frame_planes(
  header : Av1FrameHeaderInfo,
  seq : Av1SequenceInfo,
  state : Av1FrameMap,
  budget? : DecodeBudget? = None,
) -> Av1FramePlanes? {
  let width = if header.upscaled_width > header.frame_width {
    header.upscaled_width
  } else {
    header.frame_width
  }
  if !decode_reserve(budget, width, header.frame_height) {
    return None
  }
  // An inter frame is only decodable once every tool it signals can be acted
  // on; a frame that uses one the block stage lacks is refused whole rather
  // than decoded as a prefix of itself.
  let inter = if header.frame_is_intra {
    None
  } else if !av1_inter_frame_supported(header, seq) {
    decode_fail(
      budget,
      Unsupported,
      "The signaled inter-picture tools are not supported",
    )
    return None
  } else {
    match av1_inter_frame_of(header, seq, state) {
      Some(value) => Some(value)
      None => {
        decode_fail(
          budget,
          MissingReference,
          "Inter-picture reference state is unavailable or incompatible",
        )
        return None
      }
    }
  }
  let frame_height = header.frame_height
  let sb_size = if seq.use_128x128_superblock { 128 } else { 64 }
  let inherited = Some(av1_frame_initial_cdfs(state, header))
  let cols = match
    av1_frame_tile_starts(
      header.tile_col_starts_sb,
      sb_size,
      header.frame_width,
    ) {
    Some(value) => value
    None => return None
  }
  let rows = match
    av1_frame_tile_starts(header.tile_row_starts_sb, sb_size, frame_height) {
    Some(value) => value
    None => return None
  }
  let cdfs_sink : Array[Av1FrameCdfs?] = []
  let segment_sink : Array[Array[Int]] = []
  let previous_segments = if header.primary_ref_frame >= 0 &&
    header.primary_ref_frame < av1_primary_ref_none {
    let slot = header.ref_frame_idx[header.primary_ref_frame]
    if state.valid[slot] &&
      state.frame_widths[slot] == header.frame_width &&
      state.frame_heights[slot] == header.frame_height {
      state.saved_segment_ids[slot]
    } else {
      []
    }
  } else {
    []
  }
  let (decoded, _stripe_store, frame_cdfs) = match
    av1_decode_tile_planes(
      header.frame_width,
      frame_height,
      cols,
      rows,
      header.tile_payload,
      header.tile_size_bytes,
      header.base_q_idx,
      header.disable_cdf_update,
      header.allow_screen_content_tools,
      seq.full_range,
      seq.matrix_coefficients,
      seq.use_128x128_superblock,
      seq.bit_depth,
      header.reduced_tx_set,
      seq.enable_filter_intra,
      header.tx_mode_select,
      header.cdef,
      color_primaries=seq.color_primaries,
      transfer_characteristics=seq.transfer_characteristics,
      monochrome=seq.monochrome,
      enable_intra_edge_filter=seq.enable_intra_edge_filter,
      loop_filter=header.loop_filter,
      restoration=header.restoration,
      upscaled_width=header.upscaled_width,
      superres_denom=header.superres_denom,
      quant_deltas=header.quant_deltas,
      delta_q_present=header.delta_q_present,
      delta_q_res=header.delta_q_res,
      subsampling_x=seq.subsampling_x,
      subsampling_y=seq.subsampling_y,
      inter~,
      segmentation=Some(header.segmentation),
      quant_matrix=header.quant_matrix,
      allow_intrabc=header.allow_intrabc,
      previous_seg_ids=previous_segments,
      delta_lf_present=header.delta_lf_present,
      delta_lf_res=header.delta_lf_res,
      delta_lf_multi=header.delta_lf_multi,
      segment_ids_out=Some(segment_sink),
      cdfs=inherited,
      context_update_tile_id=header.context_update_tile_id,
      cdfs_out=Some(cdfs_sink),
    ) {
    Some(value) => value
    None => return None
  }
  // The reference buffer keeps the grain-free reconstruction: AV1 applies film
  // grain only on the displayed picture, so a later frame must not predict
  // from noisy samples.
  av1_frame_map_update(
    state,
    header,
    decoded,
    seq,
    cdfs=frame_cdfs,
    field=inter.map(fn(info) { info.field }),
    segment_ids=if segment_sink.length() > 0 { segment_sink[0] } else { [] },
  )
  Some(decoded)
}