/// Raw frame-level loop-restoration configuration from the uncompressed
/// header. Modes use the internal AV1 filter enum, not the two-bit header
/// codes: `00` selects None, `01` Switchable, `10` Wiener and `11` Sgrproj.
/// Monochrome configurations carry a single plane and no UV syntax. A missing
/// configuration means the frame omitted LR or declared all planes None; it
/// never downgrades a declared active filter.
///
/// Unit sizes are per-plane pixel sizes in the restored upscaled frame, not
/// MI counts. Luma derives from the superblock size with the header bits;
/// chroma planes may halve it once for 4:2:0. When every plane is None no
/// size bits occur and libaom implicitly assigns U256 to all planes.

///|
pub(all) enum Av1RestorationType {
  Av1RestorationNone
  Av1RestorationWiener
  Av1RestorationSgrproj
  Av1RestorationSwitchable
} derive(Eq, Debug)

///|
pub(all) struct Av1RestorationConfig {
  /// One entry per actual plane, in Y, U, V order.
  modes : Array[Av1RestorationType]
  /// Matching per-plane restoration unit size in pixels.
  unit_sizes : Array[Int]
}

///|
/// The unit-count rule from `av1_lr_count_units`: a trailing remainder smaller
/// than half a unit is absorbed by the last unit; the equality case creates
/// another one. Width 95 with U64 counts one unit, width 96 counts two. The
/// maximum with one handles planes smaller than half a unit.
fn av1_lr_count_units(unit_size : Int, plane_size : Int) -> Int {
  let count = (plane_size + (unit_size >> 1)) / unit_size
  if count > 1 {
    count
  } else {
    1
  }
}

///|
fn av1_restoration_config_valid(
  config : Av1RestorationConfig,
  monochrome? : Bool = false,
) -> Bool {
  let planes = if monochrome { 1 } else { 3 }
  if config.modes.length() != planes || config.unit_sizes.length() != planes {
    return false
  }
  for size in config.unit_sizes {
    if size != 32 && size != 64 && size != 128 && size != 256 {
      return false
    }
  }
  if monochrome {
    return true
  }
  // Both chroma planes share one unit size, either the luma size or its
  // single 4:2:0 halving; 4:4:4 would compare equality instead.
  if config.unit_sizes[1] != config.unit_sizes[2] {
    return false
  }
  config.unit_sizes[1] == config.unit_sizes[0] ||
  config.unit_sizes[1] * 2 == config.unit_sizes[0]
}

///|
/// True when any actual plane requests a filter. Unit sizes remain valid for
/// an all-None configuration; they are simply never applied.
pub fn av1_restoration_active(config : Av1RestorationConfig) -> Bool {
  for mode in config.modes {
    if mode is Av1RestorationNone {
      continue
    }
    return true
  }
  false
}

///|
/// Exact restoration grid for one frame, mirroring `av1_alloc_restoration_
/// struct`. Plane dimensions exclude coded MI padding: luma is the full
/// upscaled width and frame height, chroma rounds each up to its subsampled
/// size. `superres_denom` is retained for the tile pre-SB corner mapping.
pub(all) struct Av1RestorationLayout {
  config : Av1RestorationConfig
  frame_width : Int
  upscaled_width : Int
  frame_height : Int
  superres_denom : Int
  unit_cols : Array[Int]
  unit_rows : Array[Int]
}

///|
fn av1_restoration_layout(
  config : Av1RestorationConfig,
  frame_width : Int,
  upscaled_width : Int,
  frame_height : Int,
  monochrome : Bool,
  subsampling_x : Bool,
  subsampling_y : Bool,
  superres_denom : Int,
) -> Av1RestorationLayout? {
  if !av1_restoration_config_valid(config, monochrome~) ||
    frame_width <= 0 ||
    frame_width > 65536 ||
    upscaled_width < frame_width ||
    upscaled_width > 65536 ||
    frame_height <= 0 ||
    frame_height > 65536 ||
    (superres_denom != 8 && (superres_denom < 9 || superres_denom > 16)) {
    return None
  }
  let unit_cols : Array[Int] = []
  let unit_rows : Array[Int] = []
  for plane in 0..> shift_x
    let plane_height = (frame_height + (1 << shift_y) - 1) >> shift_y
    let unit = config.unit_sizes[plane]
    unit_cols.push(av1_lr_count_units(unit, plane_width))
    unit_rows.push(av1_lr_count_units(unit, plane_height))
  }
  Some({
    config,
    frame_width,
    upscaled_width,
    frame_height,
    superres_denom,
    unit_cols,
    unit_rows,
  })
}