/// AV1 scalar loop-restoration kernels and frame driver.
///
/// Wiener filters each output pixel as a separable 7-tap horizontal
/// then vertical convolution over the immutable post-CDEF/upscaled
/// source, with the horizontal result clipped to an intermediate
/// range before the vertical pass. SGR computes per-pixel box-filtered
/// projections `flt0`/`flt1` from the same source and combines them
/// with the unshifted center pixel through the coded weights
/// `w0 + w1 + w2 = 128`. Both kernels read their taps through
/// `Av1RestorationSourceView`, so internal stripe boundaries resolve to
/// the saved deblocked rows and frame edges repeat the post-CDEF edge
/// rows (AV1 spec §7.17; go-av1 decode/lr.go:236-268 and decode/sgr.go).
///
/// Filtering follows the audit §7 rectangles: split the visible plane
/// into U-sized cells with the `<1.5U` tail rule, shift each nominal
/// vertical cell by `8>>ss_y` into `[max(0, y0-O), y1 < H ? y1-O : H)`,
/// and process the rectangle as intersections with the global stripes.
/// The unit's parameters cover every stripe piece of its rectangle;
/// None units leave the LR input untouched.

///|
/// SGR parameter sets indexed by the 4-bit `ep`: (r0, eps0, r1, eps1).
let av1_sgr_params_table : Array[(Int, Int, Int, Int)] = [
  (2, 12, 1, 4),
  (2, 15, 1, 6),
  (2, 18, 1, 8),
  (2, 21, 1, 9),
  (2, 24, 1, 10),
  (2, 29, 1, 11),
  (2, 36, 1, 12),
  (2, 45, 1, 13),
  (2, 56, 1, 14),
  (2, 68, 1, 15),
  (0, 0, 1, 5),
  (0, 0, 1, 8),
  (0, 0, 1, 11),
  (0, 0, 1, 14),
  (2, 30, 0, 0),
  (2, 75, 0, 0),
]

///|
/// Look up one SGR parameter set; None outside the 4-bit range.
fn av1_sgr_params(set : Int) -> (Int, Int, Int, Int)? {
  if set < 0 || set >= av1_sgr_params_table.length() {
    return None
  }
  Some(av1_sgr_params_table[set])
}

///|
/// Rounded right shift with a half added first; non-positive shifts
/// pass the value through (go-av1 decode/sgr.go:49-54).
fn av1_lr_round_n(x : Int, n : Int) -> Int {
  if n <= 0 {
    x
  } else {
    (x + (1 << (n - 1))) >> n
  }
}

///|
/// Clamp to the inclusive range.
fn av1_lr_clip(lo : Int, hi : Int, v : Int) -> Int {
  if v < lo {
    lo
  } else if v > hi {
    hi
  } else {
    v
  }
}

///|
/// Expand the three coded Wiener taps of one axis into the symmetric
/// 7-tap filter `[c0, c1, c2, 128-2*(c0+c1+c2), c2, c1, c0]`. A chroma
/// tap 0 of zero makes the 7-tap loop equivalent to the 5-tap filter.
fn av1_wiener_coeff(taps : Array[Int]) -> Array[Int] {
  let filter : Array[Int] = Array::make(7, 0)
  filter[3] = 128
  for i in 0..<3 {
    let c = taps[i]
    filter[i] = c
    filter[6 - i] = c
    filter[3] = filter[3] - 2 * c
  }
  filter
}

///|
/// One unit's cell along an axis: full units span `index*U` to
/// `(index+1)*U`; the last cell extends to the plane edge, which is
/// the `<1.5U` tail rule of the unit count.
fn av1_lr_unit_cell(
  unit : Int,
  plane_size : Int,
  count : Int,
  index : Int,
) -> (Int, Int) {
  let start = index * unit
  let end = if index + 1 < count { (index + 1) * unit } else { plane_size }
  (start, end)
}

///|
/// Wiener filter over one `[x0, x0+w) x [y0, y0+h)` piece of a
/// stripe. The horizontal pass produces `h+6` intermediate rows so the
/// vertical taps can read three rows above and below the piece; every
/// source sample resolves through the stripe source view.
fn av1_wiener_filter_piece(
  view : Av1RestorationSourceView,
  plane : Int,
  x0 : Int,
  y0 : Int,
  w : Int,
  h : Int,
  stripe : Int,
  vertical : Array[Int],
  horizontal : Array[Int],
  bit_depth : Int,
) -> Array[Int] {
  let round0 = if bit_depth == 12 { 5 } else { 3 }
  let inter_round = if bit_depth == 12 { 9 } else { 11 }
  let vfilter = av1_wiener_coeff(vertical)
  let hfilter = av1_wiener_coeff(horizontal)
  let offset = 1 << (bit_depth + 7 - round0 - 1)
  let limit = (1 << (bit_depth + 1 + 7 - round0)) - 1
  let hi = (1 << bit_depth) - 1
  let intermediate : Array[Int] = Array::make((h + 6) * w, 0)
  for r in 0..<(h + 6) {
    for c in 0.. Array[Int]? {
  if r <= 0 || r > 8 || eps < 0 || eps > 255 {
    return None
  }
  let n = (2 * r + 1) * (2 * r + 1)
  let n2e = n * n * eps
  if n2e == 0 {
    return None
  }
  let s = ((1 << 20) + n2e / 2) / n2e
  // A and B carry a one-sample border: index (i+1)*(w+2) + (j+1).
  let a_arr : Array[Int] = Array::make((h + 2) * (w + 2), 0)
  let b_arr : Array[Int] = Array::make((h + 2) * (w + 2), 0)
  for i in -1..<(h + 1) {
    for j in -1..<(w + 1) {
      let mut a = 0
      let mut b = 0
      for dy in -r..<(r + 1) {
        for dx in -r..<(r + 1) {
          let c = av1_restoration_source_sample(
            view,
            plane,
            x0 + j + dx,
            y0 + i + dy,
            stripe,
          )
          a = a + c * c
          b = b + c
        }
      }
      a = av1_lr_round_n(a, 2 * (bit_depth - 8))
      let d = av1_lr_round_n(b, bit_depth - 8)
      let diff = a * n - d * d
      let p = if diff > 0 { diff } else { 0 }
      // dav1d's p/s and mean products use unsigned arithmetic. Both can
      // exceed signed Int: high-contrast 3x3 windows for p*s, and bright
      // low-variance 12-bit windows for b2. Keep the complete product until
      // rounding; the shifted statistics fit Int again.
      let z = ((p.to_int64() * s.to_int64() + (1L << 19)) >> 20).to_int()
      let a2 = if z >= 255 {
        256
      } else if z == 0 {
        1
      } else {
        ((z << 8) + z / 2) / (z + 1)
      }
      let one_over_n = ((1 << 12) + n / 2) / n
      let b2 = ((1 << 8) - a2).to_int64() * b.to_int64() * one_over_n.to_int64()
      a_arr[(i + 1) * (w + 2) + (j + 1)] = a2
      b_arr[(i + 1) * (w + 2) + (j + 1)] = ((b2 + (1L << 11)) >> 12).to_int()
    }
  }
  let out : Array[Int] = Array::make(w * h, 0)
  for i in 0.. Array[Int]? {
  if xqd.length() != 2 {
    return None
  }
  let (r0, eps0, r1, eps1) = match av1_sgr_params(set) {
    Some(value) => value
    None => return None
  }
  let w0 = xqd[0]
  let w1 = xqd[1]
  let w2 = (1 << 7) - w0 - w1
  let hi = (1 << bit_depth) - 1
  let flt0 = if r0 != 0 {
    match
      av1_sgr_box_pass(
        view, plane, x0, y0, w, h, stripe, r0, eps0, 0, bit_depth,
      ) {
      Some(value) => value
      None => return None
    }
  } else {
    Array::make(0, 0)
  }
  let flt1 = if r1 != 0 {
    match
      av1_sgr_box_pass(
        view, plane, x0, y0, w, h, stripe, r1, eps1, 1, bit_depth,
      ) {
      Some(value) => value
      None => return None
    }
  } else {
    Array::make(0, 0)
  }
  let out : Array[Int] = Array::make(w * h, 0)
  for i in 0.. Array[Int]? {
  if plane < 0 || plane >= layout.config.modes.length() {
    return None
  }
  let mode = layout.config.modes[plane]
  if mode is Av1RestorationNone {
    return Some(plane_data)
  }
  let shift_x = if plane != 0 && subsampling_x { 1 } else { 0 }
  let shift_y = if plane != 0 && subsampling_y { 1 } else { 0 }
  let plane_width = (layout.upscaled_width + (1 << shift_x) - 1) >> shift_x
  let plane_height = (layout.frame_height + (1 << shift_y) - 1) >> shift_y
  if plane_width <= 0 || plane_height <= 0 {
    return None
  }
  let unit = layout.config.unit_sizes[plane]
  let cols = layout.unit_cols[plane]
  let rows = layout.unit_rows[plane]
  if cols <= 0 || rows <= 0 || units.length() != cols * rows {
    return None
  }
  if plane >= view.store.planes.length() {
    return None
  }
  let stripes = view.store.planes[plane]
  let o = if shift_y != 0 { 4 } else { 8 }
  let out : Array[Int] = plane_data.copy()
  for r in 0.. o { ny0 - o } else { 0 }
    let v_end = if ny1 < plane_height { ny1 - o } else { plane_height }
    if v_end <= v_start {
      continue
    }
    for c in 0.. {
          if vertical.length() != 3 || horizontal.length() != 3 {
            return None
          }
          Some(
            av1_wiener_collect_pieces(
              view, plane, nx0, w, v_start, v_end, stripes, vertical, horizontal,
              bit_depth,
            ),
          )
        }
        Some(Av1RestorationUnitSgrproj(set, xqd)) => {
          if xqd.length() != 2 {
            return None
          }
          if xqd[0] < -96 || xqd[0] > 31 || xqd[1] < -32 || xqd[1] > 95 {
            return None
          }
          av1_sgr_collect_pieces(
            view, plane, nx0, w, v_start, v_end, stripes, set, xqd, bit_depth,
          )
        }
        _ => continue
      }
      match filtered {
        Some(pieces) =>
          for piece in pieces {
            let (py0, py1, data) = piece
            let ph = py1 - py0
            for i in 0.. return None
      }
    }
  }
  Some(out)
}

///|
/// Wiener-filter every stripe piece of one unit rectangle and return
/// the non-empty pieces as (y0, y1, data) triples.
fn av1_wiener_collect_pieces(
  view : Av1RestorationSourceView,
  plane : Int,
  nx0 : Int,
  w : Int,
  v_start : Int,
  v_end : Int,
  stripes : Av1RestorationPlaneStripes,
  vertical : Array[Int],
  horizontal : Array[Int],
  bit_depth : Int,
) -> Array[(Int, Int, Array[Int])] {
  let pieces : Array[(Int, Int, Array[Int])] = []
  for k in 0.. stripes.starts[k] {
      v_start
    } else {
      stripes.starts[k]
    }
    let py1 = if v_end < stripes.ends[k] { v_end } else { stripes.ends[k] }
    if py1 <= py0 {
      continue
    }
    let data = av1_wiener_filter_piece(
      view,
      plane,
      nx0,
      py0,
      w,
      py1 - py0,
      k,
      vertical,
      horizontal,
      bit_depth,
    )
    pieces.push((py0, py1, data))
  }
  pieces
}

///|
/// SGR-filter every stripe piece of one unit rectangle.
fn av1_sgr_collect_pieces(
  view : Av1RestorationSourceView,
  plane : Int,
  nx0 : Int,
  w : Int,
  v_start : Int,
  v_end : Int,
  stripes : Av1RestorationPlaneStripes,
  set : Int,
  xqd : Array[Int],
  bit_depth : Int,
) -> Array[(Int, Int, Array[Int])]? {
  let pieces : Array[(Int, Int, Array[Int])] = []
  for k in 0.. stripes.starts[k] {
      v_start
    } else {
      stripes.starts[k]
    }
    let py1 = if v_end < stripes.ends[k] { v_end } else { stripes.ends[k] }
    if py1 <= py0 {
      continue
    }
    let data = match
      av1_sgr_filter_piece(
        view,
        plane,
        nx0,
        py0,
        w,
        py1 - py0,
        k,
        set,
        xqd,
        bit_depth,
      ) {
      Some(value) => value
      None => return None
    }
    pieces.push((py0, py1, data))
  }
  Some(pieces)
}

///|
/// Filter every plane of one frame into fresh output planes. The
/// source planes stay immutable; None-mode planes are returned as-is.
/// Returns None on invalid unit parameters, unit maps or geometry.
fn av1_restoration_filter_frame(
  view : Av1RestorationSourceView,
  units : Array[Array[Av1RestorationUnit?]],
  layout : Av1RestorationLayout,
  bit_depth : Int,
  monochrome : Bool,
  subsampling_x? : Bool = false,
  subsampling_y? : Bool = false,
) -> Array[Array[Int]]? {
  let num_planes = if monochrome { 1 } else { 3 }
  if units.length() != num_planes ||
    view.frame.length() != num_planes ||
    view.frame_strides.length() != num_planes ||
    layout.config.modes.length() != num_planes {
    return None
  }
  let out : Array[Array[Int]] = []
  for plane in 0.. value
      None => return None
    }
    out.push(filtered)
  }
  Some(out)
}