///|
pub(all) struct Kernel {
  width : Int
  height : Int
  values : Array[Double]
} derive(Debug, Eq, ToJson)

///|
pub fn Kernel::new(
  width~ : Int,
  height~ : Int,
  values~ : Array[Double],
) -> Kernel raise ThermalError {
  if width <= 0 || height <= 0 || values.length() != width * height {
    raise InvalidDimensions(width~, height~, values=values.length())
  }
  { width, height, values: values.copy() }
}

///|
pub fn Kernel::box(size~ : Int) -> Kernel raise ThermalError {
  let side = Int::max(1, size)
  Kernel::new(
    width=side,
    height=side,
    values=Array::make(side * side, 1.0 / (side * side).to_double()),
  )
}

///|
pub fn ThermalMatrix::convolve(
  matrix : ThermalMatrix,
  kernel : Kernel,
) -> ThermalMatrix {
  let values : Array[Double] = []
  let cx = kernel.width / 2
  let cy = kernel.height / 2
  for y in 0.. ThermalMatrix raise ThermalError {
  let kernel = Kernel::new(width=3, height=3, values=[
    0.0, 1.0, 0.0, 1.0, -4.0, 1.0, 0.0, 1.0, 0.0,
  ])
  matrix.convolve(kernel)
}

///|
pub fn ThermalMatrix::sharpen(
  matrix : ThermalMatrix,
  amount? : Double = 1.0,
) -> ThermalMatrix raise ThermalError {
  let edges = matrix.laplacian()
  let values : Array[Double] = []
  for i, value in matrix.values {
    values.push(value - edges.values[i] * amount)
  }
  { width: matrix.width, height: matrix.height, values }
}

///|
pub fn ThermalMatrix::rescale(
  matrix : ThermalMatrix,
  width~ : Int,
  height~ : Int,
) -> ThermalMatrix raise ThermalError {
  if width <= 0 || height <= 0 {
    raise InvalidDimensions(width~, height~, values=0)
  }
  let values : Array[Double] = []
  for y in 0..