///|
/// Production-facing motion metrics used to compare export candidates.
pub struct MotionMetrics2 {
  path_length : Double
  peak_speed : Double
  peak_acceleration : Double
  average_speed : Double
  settled_time : Double
  samples : Int
} derive(Debug)

///|
pub fn MotionMetrics2::path_length(self : MotionMetrics2) -> Double {
  self.path_length
}

///|
pub fn MotionMetrics2::peak_speed(self : MotionMetrics2) -> Double {
  self.peak_speed
}

///|
pub fn MotionMetrics2::peak_acceleration(self : MotionMetrics2) -> Double {
  self.peak_acceleration
}

///|
pub fn MotionMetrics2::average_speed(self : MotionMetrics2) -> Double {
  self.average_speed
}

///|
pub fn MotionMetrics2::settled_time(self : MotionMetrics2) -> Double {
  self.settled_time
}

///|
pub fn MotionMetrics2::samples(self : MotionMetrics2) -> Int {
  self.samples
}

///|
pub fn analyze_point_series2(
  series : PointSeries2,
  target : Point2,
  tolerance : Double,
) -> MotionMetrics2 {
  let values = series.values()
  let times = series.times()
  if values.length() == 0 {
    return {
      path_length: 0.0,
      peak_speed: 0.0,
      peak_acceleration: 0.0,
      average_speed: 0.0,
      settled_time: 0.0,
      samples: 0,
    }
  }
  let mut length = 0.0
  let mut peak_speed = 0.0
  let mut peak_acceleration = 0.0
  let mut previous_speed = 0.0
  let mut settled = if values[values.length() - 1].distance(target) <=
    tolerance.abs() {
    times[times.length() - 1]
  } else {
    0.0
  }
  for index in 1.. Array[Double] {
  [
    second.path_length - first.path_length,
    second.peak_speed - first.peak_speed,
    second.peak_acceleration - first.peak_acceleration,
    second.average_speed - first.average_speed,
    second.settled_time - first.settled_time,
  ]
}

///|
pub fn metric_within_budget2(
  metrics : MotionMetrics2,
  maximum_speed : Double,
  maximum_acceleration : Double,
) -> Bool {
  metrics.peak_speed <= maximum_speed &&
  metrics.peak_acceleration <= maximum_acceleration
}

///|
pub struct MotionBudget2 {
  maximum_speed : Double
  maximum_acceleration : Double
  maximum_path_length : Double
} derive(Debug)

///|
pub fn motion_budget2(
  maximum_speed : Double,
  maximum_acceleration : Double,
  maximum_path_length : Double,
) -> MotionBudget2 {
  {
    maximum_speed: maximum_speed.max(0.0),
    maximum_acceleration: maximum_acceleration.max(0.0),
    maximum_path_length: maximum_path_length.max(0.0),
  }
}

///|
pub fn MotionBudget2::maximum_speed(self : MotionBudget2) -> Double {
  self.maximum_speed
}

///|
pub fn MotionBudget2::maximum_acceleration(self : MotionBudget2) -> Double {
  self.maximum_acceleration
}

///|
pub fn MotionBudget2::maximum_path_length(self : MotionBudget2) -> Double {
  self.maximum_path_length
}

///|
pub fn MotionBudget2::accepts(
  self : MotionBudget2,
  metrics : MotionMetrics2,
) -> Bool {
  metrics.peak_speed <= self.maximum_speed &&
  metrics.peak_acceleration <= self.maximum_acceleration &&
  metrics.path_length <= self.maximum_path_length
}

///|
pub fn MotionBudget2::violations(
  self : MotionBudget2,
  metrics : MotionMetrics2,
) -> Array[Double] {
  let result : Array[Double] = []
  if metrics.peak_speed > self.maximum_speed {
    result.push(metrics.peak_speed - self.maximum_speed)
  }
  if metrics.peak_acceleration > self.maximum_acceleration {
    result.push(metrics.peak_acceleration - self.maximum_acceleration)
  }
  if metrics.path_length > self.maximum_path_length {
    result.push(metrics.path_length - self.maximum_path_length)
  }
  result
}