/// AV1 inverse quantization matrices (AV1 sections 5.9.14 and 7.12.3).

///|
/// Per-plane matrix levels signalled by quantization_params. Levels 0..14
/// select the normative inverse matrix; level 15 selects uniform weights.
/// U and V select independently from the common chroma matrix family.
pub(all) struct Av1QuantMatrix {
  y : Int
  u : Int
  v : Int
} derive(Eq, Debug)

///|
fn av1_quant_matrix_valid(matrix : Av1QuantMatrix?) -> Bool {
  match matrix {
    None => true
    Some(matrix) =>
      matrix.y >= 0 &&
      matrix.y <= 15 &&
      matrix.u >= 0 &&
      matrix.u <= 15 &&
      matrix.v >= 0 &&
      matrix.v <= 15
  }
}

///|
/// LosslessArray is derived from the nominal segment qindex and all five
/// plane deltas, independently of the superblock delta-Q. Its caller passes
/// that flag explicitly; an effective qindex of zero alone is insufficient.
fn av1_qmatrix_level(
  matrix : Av1QuantMatrix?,
  plane : Int,
  tx_type : Av1TxType,
  lossless : Bool,
) -> Int {
  if lossless {
    return 15
  }
  match tx_type {
    Av1TxDctDct
    | Av1TxAdstDct
    | Av1TxDctAdst
    | Av1TxAdstAdst
    | Av1TxFlipadstDct
    | Av1TxDctFlipadst
    | Av1TxFlipadstFlipadst
    | Av1TxAdstFlipadst
    | Av1TxFlipadstAdst => ()
    _ => return 15
  }
  match matrix {
    None => 15
    Some(matrix) =>
      if plane == 0 {
        matrix.y
      } else if plane == 1 {
        matrix.u
      } else {
        matrix.v
      }
  }
}

///|
/// Fetch a row-major coefficient weight in the adjusted transform domain.
/// The generated square tables keep the lower triangle; 16x16 samples every
/// second row/column of 32x32. Rectangles keep their wider orientation and
/// transpose on lookup. This is the normative Qm_Offset layout expressed
/// without duplicating transposed matrices or expanding their 64-wide tails.
fn av1_qmatrix_weight(
  level : Int,
  plane : Int,
  width : Int,
  height : Int,
  pos : Int,
) -> Int {
  if level == 15 {
    return 32
  }
  let width = if width > 32 { 32 } else { width }
  let height = if height > 32 { 32 } else { height }
  let row = pos / width
  let col = pos % width
  let matrix_index = level * 2 + (if plane == 0 { 0 } else { 1 })
  if width == height {
    let step = if width == 16 { 2 } else { 1 }
    let r = row * step
    let c = col * step
    let hi = if r > c { r } else { c }
    let lo = if r > c { c } else { r }
    let index = hi * (hi + 1) / 2 + lo
    let data = if width == 4 {
      av1_qmatrix_4x4_t[matrix_index]
    } else if width == 8 {
      av1_qmatrix_8x8_t[matrix_index]
    } else {
      av1_qmatrix_32x32_t[matrix_index]
    }
    return data[index].to_int()
  }
  let wide = if width > height { width } else { height }
  let short = if width > height { height } else { width }
  let index = if width > height { pos } else { col * wide + row }
  let data = if wide == 8 {
    av1_qmatrix_8x4[matrix_index]
  } else if wide == 16 && short == 4 {
    av1_qmatrix_16x4[matrix_index]
  } else if wide == 16 {
    av1_qmatrix_16x8[matrix_index]
  } else if short == 8 {
    av1_qmatrix_32x8[matrix_index]
  } else {
    av1_qmatrix_32x16[matrix_index]
  }
  data[index].to_int()
}

///|
/// AV1 scales the scalar quantizer before multiplying by the coefficient.
/// The largest product (29247 * 255) fits in Int for every supported depth.
fn av1_qmatrix_quant(q : Int, weight : Int) -> Int {
  (q * weight + 16) >> 5
}

///|
/// Section 7.12.1: the denominator depends on original transform area, not
/// adjusted coefficient area. In particular, 16x64/64x16 use 2, not 4.
fn av1_dequant_denom(width : Int, height : Int) -> Int64 {
  let area = width * height
  if area > 1024 {
    4L
  } else if area > 256 {
    2L
  } else {
    1L
  }
}