///|
pub struct FlowField {
  width : Int
  height : Int
  vectors : Array[FlowVector]
} derive(Debug, Eq)

///|
pub fn FlowField::new(width~ : Int, height~ : Int) -> FlowField {
  { width: max_i(0, width), height: max_i(0, height), vectors: [] }
}

///|
pub fn FlowField::from_vectors(
  size : Size,
  vectors : Array[FlowVector],
) -> FlowField {
  { width: size.width, height: size.height, vectors }
}

///|
pub fn FlowField::constant(size : Size, dx : Int, dy : Int) -> FlowField {
  FlowField::from_vectors(size, constant_flow(size, 1, dx~, dy~))
}

///|
pub fn FlowField::size(self : FlowField) -> Size {
  Size::new(width=self.width, height=self.height)
}

///|
pub fn FlowField::at(self : FlowField, x : Int, y : Int) -> FlowVector {
  for vector in self.vectors {
    if vector.x == x && vector.y == y {
      return vector
    }
  }
  FlowVector::new(x~, y~, dx=0, dy=0)
}

///|
pub fn FlowField::magnitude_image(self : FlowField) -> GrayImage {
  let out = GrayImage::new(width=self.width, height=self.height)
  for vector in self.vectors {
    out.set(vector.x, vector.y, Gray::new(flow_magnitude_l1(vector) * 16))
  }
  out
}

///|
pub fn FlowField::direction_image(self : FlowField) -> GrayImage {
  let out = GrayImage::new(width=self.width, height=self.height)
  for vector in self.vectors {
    let direction = if vector.dx > 0 {
      255
    } else if vector.dx < 0 {
      64
    } else if vector.dy > 0 {
      192
    } else {
      128
    }
    out.set(vector.x, vector.y, Gray::new(direction))
  }
  out
}

///|
pub fn FlowField::mean(self : FlowField) -> Point {
  flow_mean(self.vectors)
}

///|
pub fn FlowField::to_json(self : FlowField) -> String {
  let sb = StringBuilder()
  sb.write_string(
    "{\"width\":\{self.width},\"height\":\{self.height},\"vectors\":[",
  )
  for i, vector in self.vectors {
    if i > 0 {
      sb.write_string(",")
    }
    sb.write_string(
      "{\"x\":\{vector.x},\"y\":\{vector.y},\"dx\":\{vector.dx},\"dy\":\{vector.dy}}",
    )
  }
  sb.write_string("]}")
  sb.to_string()
}

///|
pub fn warp_by_flow(
  source : GrayImage,
  field : FlowField,
  fill : Gray,
) -> GrayImage {
  let out = GrayImage::new(width=source.width, height=source.height, fill~)
  for vector in field.vectors {
    let target_x = vector.x + vector.dx
    let target_y = vector.y + vector.dy
    if out.in_bounds(target_x, target_y) {
      out.set(target_x, target_y, source.get(vector.x, vector.y))
    }
  }
  out
}

///|
pub fn inverse_warp_by_flow(
  source : GrayImage,
  field : FlowField,
  fill : Gray,
) -> GrayImage {
  let out = GrayImage::new(width=source.width, height=source.height, fill~)
  for y in 0.. Int {
  let count = min_i(expected.length(), actual.length())
  if count == 0 {
    0
  } else {
    let mut total = 0
    for i in 0.. Int {
  if field.width * field.height == 0 {
    0
  } else {
    field.vectors.length() * 1000 / (field.width * field.height)
  }
}

///|
pub fn translate_flow(size : Size, dx : Int, dy : Int, step : Int) -> FlowField {
  FlowField::from_vectors(size, constant_flow(size, step, dx~, dy~))
}

///|
pub fn compose_flow(first : FlowField, second : FlowField) -> FlowField {
  let out = FlowField::new(width=first.width, height=first.height)
  for vector in first.vectors {
    let second_vector = second.at(vector.x + vector.dx, vector.y + vector.dy)
    out.vectors.push(
      FlowVector::new(
        x=vector.x,
        y=vector.y,
        dx=vector.dx + second_vector.dx,
        dy=vector.dy + second_vector.dy,
      ),
    )
  }
  out
}

///|
pub fn scale_flow(
  field : FlowField,
  numerator : Int,
  denominator : Int,
) -> FlowField {
  let den = if denominator == 0 { 1 } else { denominator }
  let out = FlowField::new(width=field.width, height=field.height)
  for vector in field.vectors {
    out.vectors.push(
      FlowVector::new(
        x=vector.x,
        y=vector.y,
        dx=vector.dx * numerator / den,
        dy=vector.dy * numerator / den,
      ),
    )
  }
  out
}

///|
pub fn scene_with_flow(scene : SceneOutput, field : FlowField) -> SceneOutput {
  SceneOutput::new(
    plan=scene.plan,
    image=scene.image,
    mask=scene.mask,
    boxes=scene.boxes,
    flow=field.vectors,
  )
}