///|
/// A probe location used for diagnostics.
pub(all) enum Probe {
  CellProbe(Int, Int)
  PointProbe(Point)
} derive(Debug)

///|
/// Construct an integer-cell probe.
pub fn Probe::cell(x~ : Int, y~ : Int) -> Probe {
  CellProbe(x, y)
}

///|
/// Construct a floating-point probe.
pub fn Probe::point(x~ : Double, y~ : Double) -> Probe {
  PointProbe(Point::new(x~, y~))
}

///|
/// Sample one probe using nearest-cell or bilinear interpolation.
pub fn Simulation::sample_probe(self : Simulation, probe : Probe) -> Cell {
  match probe {
    CellProbe(x, y) => self.cell(x~, y~)
    PointProbe(point) => {
      let density = self.density_field().sample(x=point.x, y=point.y)
      let velocity = self.velocity_field().sample(x=point.x, y=point.y)
      { rho: density, ux: velocity.x, uy: velocity.y }
    }
  }
}

///|
/// Sample a complete line of macroscopic cells.
pub fn Simulation::sample_line(
  self : Simulation,
  axis~ : Axis,
  index~ : Int,
) -> Array[Cell] {
  let result = Array::new()
  match axis {
    Horizontal => {
      let y = index.clamp(min=0, max=self.size.height - 1)
      for x in 0.. {
      let x = index.clamp(min=0, max=self.size.width - 1)
      for y in 0.. Array[Double] {
  let cells = self.sample_line(axis~, index~)
  let result = Array::new()
  for cell in cells {
    result.push(
      match component {
        "rho" => cell.rho
        "ux" => cell.ux
        "uy" => cell.uy
        "speed" => cell.speed()
        _ => 0.0
      },
    )
  }
  result
}

///|
/// Export a line profile with a component name.
pub fn Simulation::sample_line_csv(
  self : Simulation,
  axis~ : Axis,
  index~ : Int,
  component~ : String,
) -> String {
  let values = self.sample_component(axis~, index~, component~)
  let builder = StringBuilder(size_hint=values.length() * 16 + 16)
  builder.write_string("index,\{component}\n")
  for i, value in values {
    builder.write_string("\{i},\{value}\n")
  }
  builder.to_string()
}