///|
/// Deterministic benchmark counters emitted by the command-line benchmark.
pub(all) struct BenchmarkResult {
  name : String
  iterations : Int
  successful : Int
  failed : Int
  bytes : Int
}

///|
pub fn BenchmarkResult::success_rate(self : BenchmarkResult) -> Float {
  if self.iterations == 0 {
    0.0
  } else {
    Float::from_int(self.successful) / Float::from_int(self.iterations)
  }
}

///|
pub fn BenchmarkResult::operations(self : BenchmarkResult) -> Int {
  self.successful + self.failed
}

///|
pub fn BenchmarkResult::summary(self : BenchmarkResult) -> String {
  self.name +
  " iterations=" +
  self.iterations.to_string() +
  " successful=" +
  self.successful.to_string() +
  " failed=" +
  self.failed.to_string() +
  " bytes=" +
  self.bytes.to_string()
}

///|
/// Run repeated CRC work over a representative RTU payload.
pub fn benchmark_crc(iterations : Int) -> Result[BenchmarkResult, ModbusError] {
  if iterations < 1 {
    return Err(InvalidQuantity)
  }
  let payload : Array[Byte] = [1, 3, 0, 0, 0, 32]
  let mut successful = 0
  let mut failed = 0
  let mut bytes = 0
  for _ in 0.. Result[BenchmarkResult, ModbusError] {
  if iterations < 1 {
    return Err(InvalidQuantity)
  }
  let frame = read_holding(1, 0, 32)
  let mut successful = 0
  let mut failed = 0
  let mut bytes = 0
  for _ in 0..
        if frames_equal(frame, decoded) {
          successful += 1
        } else {
          failed += 1
        }
      Err(_) => failed += 1
    }
  }
  Ok({ name: "rtu-round-trip", iterations, successful, failed, bytes })
}

///|
/// Run encode/decode round trips for the TCP transport.
pub fn benchmark_tcp_round_trip(
  iterations : Int,
) -> Result[BenchmarkResult, ModbusError] {
  if iterations < 1 {
    return Err(InvalidQuantity)
  }
  let frame = read_holding(1, 0, 32)
  let mut successful = 0
  let mut failed = 0
  let mut bytes = 0
  for index in 0..
        if transaction_id == index.to_uint16() && frames_equal(frame, decoded) {
          successful += 1
        } else {
          failed += 1
        }
      Err(_) => failed += 1
    }
  }
  Ok({ name: "tcp-round-trip", iterations, successful, failed, bytes })
}

///|
/// Run a device read workload over the in-memory server path.
pub fn benchmark_device_reads(
  iterations : Int,
) -> Result[BenchmarkResult, ModbusError] {
  if iterations < 1 {
    return Err(InvalidQuantity)
  }
  let device = match Device::new(1) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let mut successful = 0
  let mut failed = 0
  let mut bytes = 0
  for index in 0.. {
        successful += 1
        bytes += response.pdu.data.length()
      }
      Err(_) => failed += 1
    }
  }
  Ok({ name: "device-read", iterations, successful, failed, bytes })
}

///|
/// Run the standard local benchmark suite.
pub fn benchmark_suite(
  iterations : Int,
) -> Result[Array[BenchmarkResult], ModbusError] {
  let out : Array[BenchmarkResult] = []
  match benchmark_crc(iterations) {
    Ok(value) => out.push(value)
    Err(error) => return Err(error)
  }
  match benchmark_rtu_round_trip(iterations) {
    Ok(value) => out.push(value)
    Err(error) => return Err(error)
  }
  match benchmark_tcp_round_trip(iterations) {
    Ok(value) => out.push(value)
    Err(error) => return Err(error)
  }
  match benchmark_device_reads(iterations) {
    Ok(value) => out.push(value)
    Err(error) => return Err(error)
  }
  Ok(out)
}