///|
/// A bounded ring of integer telemetry samples for device dashboards.
pub(all) struct TelemetryRing {
  values : Array[Int]
  capacity : Int
  mut cursor : Int
  mut count : Int
}

///|
pub fn TelemetryRing::new(capacity : Int) -> Result[TelemetryRing, ModbusError] {
  if capacity < 1 {
    Err(CapacityExceeded)
  } else {
    let values : Array[Int] = []
    for _ in 0.. Int {
  self.capacity
}

///|
pub fn TelemetryRing::length(self : TelemetryRing) -> Int {
  self.count
}

///|
pub fn TelemetryRing::is_full(self : TelemetryRing) -> Bool {
  self.count == self.capacity
}

///|
/// Add a sample and overwrite the oldest sample when the ring is full.
pub fn TelemetryRing::record(self : TelemetryRing, value : Int) -> Unit {
  self.values[self.cursor] = value
  self.cursor = (self.cursor + 1) % self.capacity
  if self.count < self.capacity {
    self.count += 1
  }
}

///|
fn TelemetryRing::physical_index(self : TelemetryRing, logical : Int) -> Int {
  let first = if self.count == self.capacity { self.cursor } else { 0 }
  (first + logical) % self.capacity
}

///|
/// Read a sample from oldest to newest order.
pub fn TelemetryRing::at(
  self : TelemetryRing,
  logical : Int,
) -> Result[Int, ModbusError] {
  if logical < 0 || logical >= self.count {
    Err(Incomplete)
  } else {
    Ok(self.values[self.physical_index(logical)])
  }
}

///|
pub fn TelemetryRing::latest(self : TelemetryRing) -> Result[Int, ModbusError] {
  if self.count == 0 {
    Err(Incomplete)
  } else {
    self.at(self.count - 1)
  }
}

///|
pub fn TelemetryRing::previous(
  self : TelemetryRing,
) -> Result[Int, ModbusError] {
  if self.count < 2 {
    Err(Incomplete)
  } else {
    self.at(self.count - 2)
  }
}

///|
pub fn TelemetryRing::delta(self : TelemetryRing) -> Result[Int, ModbusError] {
  match (self.latest(), self.previous()) {
    (Ok(latest), Ok(previous)) => Ok(latest - previous)
    (Err(error), _) => Err(error)
    (_, Err(error)) => Err(error)
  }
}

///|
pub fn TelemetryRing::sum(self : TelemetryRing) -> Int {
  let mut result = 0
  for index in 0.. Result[Int, ModbusError] {
  if self.count == 0 {
    return Err(Incomplete)
  }
  let mut result = self.values[self.physical_index(0)]
  for index in 1.. Result[Int, ModbusError] {
  if self.count == 0 {
    return Err(Incomplete)
  }
  let mut result = self.values[self.physical_index(0)]
  for index in 1.. result {
      result = value
    }
  }
  Ok(result)
}

///|
pub fn TelemetryRing::average(self : TelemetryRing) -> Float {
  if self.count == 0 {
    0.0
  } else {
    Float::from_int(self.sum()) / Float::from_int(self.count)
  }
}

///|
pub fn TelemetryRing::snapshot(self : TelemetryRing) -> Array[Int] {
  let result : Array[Int] = []
  for index in 0.. Unit {
  self.cursor = 0
  self.count = 0
}

///|
/// A closed interval used for a device alarm threshold.
pub(all) struct Threshold {
  low : Int
  high : Int
}

///|
pub fn Threshold::new(low : Int, high : Int) -> Result[Threshold, ModbusError] {
  if low > high {
    Err(InvalidData)
  } else {
    Ok({ low, high })
  }
}

///|
pub fn Threshold::low(self : Threshold) -> Int {
  self.low
}

///|
pub fn Threshold::high(self : Threshold) -> Int {
  self.high
}

///|
pub(all) enum ThresholdState {
  Below
  Within
  Above
} derive(Debug, Eq)

///|
pub fn Threshold::classify(self : Threshold, value : Int) -> ThresholdState {
  if value < self.low {
    Below
  } else if value > self.high {
    Above
  } else {
    Within
  }
}

///|
/// Stateful threshold monitor that counts state transitions.
pub(all) struct AlarmMonitor {
  threshold : Threshold
  mut state : ThresholdState
  mut transitions : Int
  mut last_value : Int?
}

///|
pub fn AlarmMonitor::new(threshold : Threshold) -> AlarmMonitor {
  { threshold, state: Within, transitions: 0, last_value: None }
}

///|
pub fn AlarmMonitor::threshold(self : AlarmMonitor) -> Threshold {
  self.threshold
}

///|
pub fn AlarmMonitor::state(self : AlarmMonitor) -> ThresholdState {
  self.state
}

///|
pub fn AlarmMonitor::transitions(self : AlarmMonitor) -> Int {
  self.transitions
}

///|
pub fn AlarmMonitor::last_value(self : AlarmMonitor) -> Int? {
  self.last_value
}

///|
pub fn AlarmMonitor::observe(
  self : AlarmMonitor,
  value : Int,
) -> ThresholdState {
  let next = self.threshold.classify(value)
  if self.last_value is Some(_) && next != self.state {
    self.transitions += 1
  }
  self.state = next
  self.last_value = Some(value)
  next
}

///|
pub fn AlarmMonitor::reset(self : AlarmMonitor) -> Unit {
  self.state = Within
  self.transitions = 0
  self.last_value = None
}