// Copyright 2026 CBTC ATP Project Authors.
// Licensed under the Apache License, Version 2.0.

///|
fn measure(name : String, iterations : Int, work : () -> Int) -> Unit {
  let start = @bench.monotonic_clock_start()
  let checksum = work()
  let elapsed_us = @bench.monotonic_clock_end(start)
  let elapsed_s = elapsed_us / 1_000_000.0
  let ops_per_second = if elapsed_s <= 0.0 {
    0.0
  } else {
    iterations.to_double() / elapsed_s
  }
  println(
    "workload=" +
    name +
    ",iterations=" +
    iterations.to_string() +
    ",elapsed_us=" +
    elapsed_us.to_string() +
    ",ops_per_second=" +
    ops_per_second.to_string() +
    ",checksum=" +
    checksum.to_string(),
  )
}

///|
fn benchmark_tree(iterations : Int) -> Int {
  let segments : Array[@types.TrackSegment] = Array::new()
  for i = 0; i < 64; i = i + 1 {
    let start = (i * 250).to_double()
    segments.push(
      @types.TrackSegment::new(
        i,
        start,
        start + 250.0,
        30.0,
        0.0,
        0.0,
        false,
        0,
        0.0,
        0.0,
      ),
    )
  }
  let tree = @line_profile.SegmentTree::build(segments)
  let mut checksum = 0
  for i = 0; i < iterations; i = i + 1 {
    let position = (i % 16000).to_double()
    match tree.find_at(position) {
      Some(segment) => checksum = checksum + segment.segment_id
      None => ()
    }
  }
  checksum
}

///|
fn benchmark_packet(iterations : Int) -> Int {
  let parser = @balise.BaliseTelegramParser::new()
  let bytes = [
    0x00, 0x0C, 0x00, 0x05, 0x03, 0xE8, 0x13, 0x88, 0x05, 0x48, 0x64, 0x00, 0x10,
  ]
  let mut checksum = 0
  for i = 0; i < iterations; i = i + 1 {
    match parser.parse_packet_12(bytes, i.to_int64()) {
      Some(ma) => checksum = checksum + ma.ma_id
      None => ()
    }
  }
  checksum
}

///|
fn benchmark_braking(iterations : Int) -> Int {
  let integrator = @dynamics.BrakeIntegrator::new(
    @types.TrainParams::default_high_speed(),
  )
  let mut checksum = 0
  for _i = 0; _i < iterations; _i = _i + 1 {
    let curve = integrator.compute_ebd_curve(
      12000.0,
      0.0,
      1000.0,
      0.0,
      @types.RailCondition::from_int(1),
      5.0,
    )
    checksum = checksum + curve.points.length()
  }
  checksum
}

///|
fn benchmark_cycle(iterations : Int) -> Int {
  let odometer = @types.OdometerBounds::new(1000.0, 0.2, false, false)
  let state_a = @types.TrainState::new(
    1000,
    odometer,
    20.0,
    0.0,
    @types.CabMode::from_int(2),
    1,
    false,
    false,
    false,
  )
  let state_b = @types.TrainState::new(
    1000,
    odometer,
    20.0,
    0.0,
    @types.CabMode::from_int(2),
    1,
    false,
    false,
    false,
  )
  let ssp = @line_profile.StaticSpeedProfile::new(
    [@types.PhysicalSpeedSegment::new(0.0, 20000.0, 40.0)],
    [@types.GradientSegment::new(0.0, 20000.0, 0.0)],
    [],
    [],
  )
  let ma = @line_profile.MaResolver::new(5000)
  ma.update_ma(
    @types.MovementAuthority::new(1, 0, 0.0, 12000.0, 0.0, 0.0, 40.0),
    0,
  )
  let input = @runtime.AtpCycleInput::new(
    1000.0, 20.0, odometer, 1000, state_a, state_b,
  )
  let engine = @runtime.AtpCycleEngine::new(
    @types.SystemConfig::default(),
    @types.TrainParams::default_high_speed(),
    0.5,
    1.0,
  )
  let mut checksum = 0
  for _i = 0; _i < iterations; _i = _i + 1 {
    let decision = engine.evaluate(
      input,
      ssp,
      @line_profile.TsrManager::new(),
      ma,
      @types.RailCondition::from_int(1),
    )
    checksum = checksum + decision.action.code()
  }
  checksum
}

///|
fn main {
  let tree_iterations = 20000
  let packet_iterations = 20000
  let braking_iterations = 100
  let cycle_iterations = 1000
  measure("segment_tree_query", tree_iterations, () => {
    benchmark_tree(tree_iterations)
  })
  measure("packet_12_decode", packet_iterations, () => {
    benchmark_packet(packet_iterations)
  })
  measure("ebd_curve", braking_iterations, () => {
    benchmark_braking(braking_iterations)
  })
  measure("full_atp_cycle", cycle_iterations, () => {
    benchmark_cycle(cycle_iterations)
  })
}