/// Pure MoonBit monochrome reconstruction. Entropy, prediction,
/// transforms and frame filtering share the color decoder's native-depth path.

///|
/// Match libavif's UNORM alpha conversion: round(src * dstMax / srcMax).
/// This preserves both endpoints and low alpha values at 10/12-bit depth.
/// Reference: libavif v1.4.2 src/alpha.c, avifReformatAlpha.
fn av1_alpha_samples_to_bytes(
  samples : Array[Int],
  bit_depth : Int,
) -> Array[Byte] {
  let maximum = (1 << bit_depth) - 1
  samples.map(fn(value) { ((value * 255 + maximum / 2) / maximum).to_byte() })
}

///|
/// Decode a complete monochrome intra tile, including AC, lossless transforms
/// and supported intra modes. Frame filters run before quantizing alpha to eight bits.
/// Width is coded; optional upscaled_width is applied before alpha conversion.
pub fn av1_decode_alpha_tile(
  width : Int,
  height : Int,
  tile_payload : Array[Byte],
  base_q_idx : Int,
  use_128x128_superblock? : Bool = false,
  allow_screen_content_tools? : Bool = false,
  bit_depth? : Int = 8,
  disable_cdf_update? : Bool = false,
  tx_mode_select? : Bool = false,
  reduced_tx_set? : Bool = false,
  enable_filter_intra? : Bool = false,
  enable_intra_edge_filter? : Bool = false,
  cdef? : Av1CdefConfig? = None,
  loop_filter? : Av1LoopFilterConfig? = None,
  restoration? : Av1RestorationConfig? = None,
  upscaled_width? : Int = 0,
  quant_deltas? : Av1QuantDeltas = {
    y_dc: 0,
    u_dc: 0,
    u_ac: 0,
    v_dc: 0,
    v_ac: 0,
  },
) -> Array[Byte]? {
  let (frame, _stripe_store, _cdfs) = match
    av1_decode_tile_planes(
      width,
      height,
      [0, width],
      [0, height],
      tile_payload,
      0,
      base_q_idx,
      disable_cdf_update,
      allow_screen_content_tools,
      true,
      2,
      use_128x128_superblock,
      bit_depth,
      reduced_tx_set,
      enable_filter_intra,
      tx_mode_select,
      cdef,
      monochrome=true,
      enable_intra_edge_filter~,
      loop_filter~,
      restoration~,
      upscaled_width~,
      quant_deltas~,
    ) {
    Some(value) => value
    None => return None
  }
  Some(av1_alpha_samples_to_bytes(av1_frame_crop(frame, 0), frame.bit_depth))
}

///|
/// Decode the first displayed monochrome frame through shared AV1 reconstruction.
/// Alpha represents coverage, so its final conversion always uses full UNORM.
fn av1_decode_alpha_limited(
  data : Array[Byte],
  limits? : DecodeLimits? = None,
) -> Array[Byte]? {
  if !decode_input_allowed(data.length(), limits) {
    return None
  }
  let frame = match
    av1_decode_frame_planes(
      data,
      allow_monochrome=true,
      budget=decode_budget(limits),
    ) {
    Some(value) => value
    None => return None
  }
  if frame.planes.length() != 1 {
    return None
  }
  Some(av1_alpha_samples_to_bytes(av1_frame_crop(frame, 0), frame.bit_depth))
}

///|
/// Decode the validated auxiliary alpha item from a complete AVIF container.
/// The result is a row-major 8-bit alpha plane, rounded from its native depth.
fn avif_decode_alpha_limited(
  data : Array[Byte],
  limits? : DecodeLimits? = None,
) -> Array[Byte]? {
  if !decode_input_allowed(data.length(), limits) {
    return None
  }
  let frame = match
    avif_decode_alpha_frame(data, budget=decode_budget(limits)) {
    Some(value) => value
    None => return None
  }
  Some(
    av1_alpha_samples_to_bytes(av1_alpha_full_samples(frame), frame.bit_depth),
  )
}

///|
/// Expand limited alpha in its original precision before UNORM narrowing.
/// libavif's read path applies this independently of the color item's range.
fn av1_alpha_full_samples(frame : Av1FramePlanes) -> Array[Int] {
  let samples = av1_frame_crop(frame, 0)
  if frame.full_range {
    return samples
  }
  samples.map(value => av1_alpha_expand_limited_sample(value, frame.bit_depth))
}

///|
/// Expand alpha before output-depth quantization, shared by RGBA8 and RGBA16.
fn av1_alpha_expand_limited_sample(value : Int, depth : Int) -> Int {
  let maximum = (1 << depth) - 1
  let black = 16 << (depth - 8)
  let span = 219 << (depth - 8)
  let expanded = ((value - black) * maximum + span / 2) / span
  if expanded < 0 {
    0
  } else if expanded > maximum {
    maximum
  } else {
    expanded
  }
}

///|
fn avif_decode_alpha_frame(
  data : Array[Byte],
  budget? : DecodeBudget? = None,
) -> Av1FramePlanes? {
  let limits = budget.map(value => value.limits)
  let descriptor = match avif_alpha_item_descriptor_limited(data, limits~) {
    Some(value) => value
    None => return None
  }
  if avif_grid_descriptor_for_item(data, Some(descriptor.item_id)) is Some(grid) {
    let frame = match avif_decode_grid_frame(data, grid, budget~) {
      Some(value) => value
      None => return None
    }
    if frame.planes.length() != 1 ||
      frame.width != descriptor.width ||
      frame.height != descriptor.height ||
      frame.bit_depth != descriptor.bit_depth {
      return None
    }
    return Some(frame)
  }
  let payload = match avif_alpha_item_payload_limited(data, limits~) {
    Some(value) => value
    None => return None
  }
  // Sequence dimensions are maxima. Check the selected frame's actual
  // dimensions below, after honoring this auxiliary item's layer selectors.
  let sequence = match av1_sequence_info(payload) {
    Some(value) => value
    None => return None
  }
  if sequence.width < descriptor.width ||
    sequence.height < descriptor.height ||
    sequence.bit_depth != descriptor.bit_depth ||
    !sequence.monochrome {
    return None
  }
  let (operating_point, spatial_layer) = match
    avif_item_layer_selection(data, descriptor.item_id) {
    Some(value) => value
    None => return None
  }
  let frame = match
    av1_decode_frame_planes(
      payload,
      allow_monochrome=true,
      operating_point~,
      spatial_layer~,
      budget~,
    ) {
    Some(value) => value
    None => return None
  }
  // The auxiliary item dimensions are authoritative at the container
  // boundary.  Reject a stream whose sequence header decodes to another
  // size instead of returning a plausible but misaligned plane.
  if descriptor.width <= 0 ||
    descriptor.height <= 0 ||
    frame.width != descriptor.width ||
    frame.height != descriptor.height ||
    frame.bit_depth != descriptor.bit_depth ||
    frame.planes.length() != 1 {
    return None
  }
  Some(frame)
}

///|
/// Decode a primary AVIF image and compose its validated auxiliary alpha item.
/// A valid image without an alpha item keeps the existing opaque result; an
/// alpha item that cannot be decoded is reported as `None` rather than dropped.
fn avif_decode_rgba_limited(
  data : Array[Byte],
  limits? : DecodeLimits? = None,
) -> Image? {
  let (frame, alpha, premultiplied) = match
    avif_decode_image_planes(data, limits~) {
    Some(value) => value
    None => return None
  }
  match alpha {
    None => av1_frame_to_rgba(frame)
    Some(alpha) => av1_frame_with_alpha(frame, alpha, premultiplied)
  }
}

///|
/// Shared native/RGBA container boundary. Validate alpha before exposing
/// either output so a broken auxiliary item cannot become an opaque image.
fn avif_decode_image_planes(
  data : Array[Byte],
  limits? : DecodeLimits? = None,
  budget? : DecodeBudget? = None,
) -> (Av1FramePlanes, Av1FramePlanes?, Bool)? {
  if !decode_input_allowed(data.length(), limits) {
    return None
  }
  let budget = match budget {
    Some(value) => Some(value)
    None => decode_budget(limits)
  }
  decode_context(budget, "alpha_metadata", None)
  let alpha_descriptor = avif_alpha_descriptor_checked(data, limits~) catch {
    AuxParseError::Unsupported => {
      decode_fail(
        budget,
        Unsupported,
        "Unsupported auxiliary-alpha container syntax",
      )
      return None
    }
    _ => return None
  }
  let frame = match avif_decode_frame(data, budget~) {
    Some(value) => value
    None => return None
  }
  match alpha_descriptor {
    None => Some((frame, None, false))
    Some(descriptor) => {
      if descriptor.width != frame.width || descriptor.height != frame.height {
        return None
      }
      let alpha = match avif_decode_alpha_frame(data, budget~) {
        Some(value) => value
        None => return None
      }
      if alpha.bit_depth != frame.bit_depth {
        return None
      }
      Some((frame, Some(alpha), descriptor.premultiplied))
    }
  }
}

///|
/// Produce straight RGBA using libavif's nearest-chroma conversion contract.
/// Standard matrices use its RGB8 unpremultiply path. Identity at high depth
/// and YCgCo retain native alpha through matrix conversion and final rounding.
fn av1_frame_with_alpha(
  color : Av1FramePlanes,
  alpha : Av1FramePlanes,
  premultiplied : Bool,
) -> Image? {
  if alpha.width != color.width ||
    alpha.height != color.height ||
    alpha.bit_depth != color.bit_depth ||
    alpha.planes.length() != 1 {
    return None
  }
  let native = av1_alpha_full_samples(alpha)
  let bytes = av1_alpha_samples_to_bytes(native, alpha.bit_depth)
  let matrix = color.matrix_coefficients
  let monochrome = color.planes.length() == 1
  let fused = premultiplied &&
    (
      color.color_primaries == 10 ||
      !(matrix == 1 ||
      matrix == 2 ||
      matrix == 4 ||
      matrix == 5 ||
      matrix == 6 ||
      matrix == 7 ||
      matrix == 9 ||
      matrix == 12 ||
      (matrix == 0 && color.bit_depth == 8 && color.full_range))
    )
  let converted = if fused {
    av1_yuv_highbd_to_rgba_core(
      color.width,
      color.height,
      av1_frame_crop(color, 0),
      if monochrome {
        []
      } else {
        av1_frame_crop(color, 1)
      },
      if monochrome {
        []
      } else {
        av1_frame_crop(color, 2)
      },
      color.bit_depth,
      subsampling_x=color.sub_x == 1,
      subsampling_y=color.sub_y == 1,
      full_range=color.full_range,
      matrix_coefficients=color.matrix_coefficients,
      color_primaries=color.color_primaries,
      transfer_characteristics=color.transfer_characteristics,
      native_alpha=Some(native),
      monochrome~,
    )
  } else {
    av1_frame_to_rgba(color)
  }
  let image = match converted {
    Some(value) => value
    None => return None
  }
  for i in 0.. Image? {
  if alpha.length() != image.width * image.height {
    return None
  }
  let out = Image::new(image.width, image.height)
  for y in 0..