///|
/// Minimal scalar VCD fixture used in examples and boundary tests.
pub fn minimal_fixture() -> String {
  (
    #|$timescale 1ns $end
    #|$scope module top $end
    #|$var wire 1 ! clk $end
    #|$upscope $end
    #|$enddefinitions $end
    #|$dumpvars
    #|0!
    #|$end
    #|#5
    #|1!
    #|#10
    #|0!
  )
}

///|
/// Clock fixture with stable alternating periods.
pub fn clock_fixture(period : Int, cycles : Int) -> String {
  let safe_period = Int::max(1, period)
  let safe_cycles = Int::max(1, cycles)
  let out = StringBuilder()
  out.write_string("$timescale 1ns $end\n$scope module top $end\n")
  out.write_string(
    "$var wire 1 ! clk $end\n$upscope $end\n$enddefinitions $end\n",
  )
  for cycle in 0.. String {
  let safe = Int::max(1, count)
  let out = StringBuilder()
  out.write_string("$timescale 1ns $end\n$scope module top $end\n")
  out.write_string(
    "$var wire 4 \" count $end\n$upscope $end\n$enddefinitions $end\n",
  )
  for time in 0.. String {
  (
    #|$date generated $end
    #|$version moonbit-vcd fixtures $end
    #|$timescale 10ps $end
    #|$scope module top $end
    #|$scope module dut $end
    #|$var wire 1 ! ready $end
    #|$var real 1 r temperature $end
    #|$upscope $end
    #|$upscope $end
    #|$enddefinitions $end
    #|#0
    #|0!
    #|r20.5 r
    #|#10
    #|1!
    #|r21.0 r
  )
}

///|
/// Fixture with every scalar logical state.
pub fn four_state_fixture() -> String {
  (
    #|$timescale 1ns $end
    #|$scope module top $end
    #|$var wire 1 ! state $end
    #|$upscope $end
    #|$enddefinitions $end
    #|#0
    #|x!
    #|#1
    #|z!
    #|#2
    #|0!
    #|#3
    #|1!
  )
}

///|
/// Fixture suitable for testing malformed directive handling.
pub fn malformed_fixture() -> String {
  (
    #|$timescale 1ns $end
    #|$scope module top $end
    #|$var wire 0 ! invalid $end
    #|$enddefinitions $end
    #|#bad
  )
}

///|
/// Build a collection of independent fixtures for table-driven tests.
pub fn fixture_catalog() -> Array[(String, String)] {
  [
    ("minimal", minimal_fixture()),
    ("clock", clock_fixture(10, 4)),
    ("counter", counter_fixture(8)),
    ("mixed", mixed_fixture()),
    ("four-state", four_state_fixture()),
    ("malformed", malformed_fixture()),
  ]
}

///|
/// Generate a multi-signal fixture for parser and index benchmarks.
pub fn multi_signal_fixture(signal_count : Int, time_count : Int) -> String {
  let signals = Int::max(1, signal_count)
  let times = Int::max(1, time_count)
  let out = StringBuilder()
  out.write_string("$timescale 1ns $end\n$scope module bench $end\n")
  for index in 0.. String {
  (
    #|$date fixture $end
    #|$version moonbit-vcd $end
    #|$timescale 1ns $end
    #|$scope module top $end
    #|$scope module cpu $end
    #|$scope module alu $end
    #|$var wire 1 ! carry $end
    #|$var wire 8 " result $end
    #|$upscope $end
    #|$upscope $end
    #|$scope module io $end
    #|$var real 1 r voltage $end
    #|$upscope $end
    #|$upscope $end
    #|$enddefinitions $end
    #|#0
    #|0!
    #|b00000000 "
    #|r1.2 r
    #|#20
    #|1!
    #|b10101010 "
    #|r1.8 r
  )
}

///|
/// Generate a fixture containing every supported scalar transition class.
pub fn edge_fixture() -> String {
  (
    #|$timescale 1ns $end
    #|$scope module top $end
    #|$var wire 1 ! signal $end
    #|$upscope $end
    #|$enddefinitions $end
    #|#0
    #|0!
    #|#2
    #|1!
    #|#4
    #|x!
    #|#6
    #|z!
    #|#8
    #|0!
  )
}

///|
/// Return expected structural facts for a named generated fixture.
pub fn fixture_expectations(name : String) -> (Int, Int) {
  match name {
    "minimal" => (1, 3)
    "four-state" => (1, 4)
    "mixed" => (2, 4)
    "hierarchy" => (3, 6)
    "edges" => (1, 5)
    _ => (0, 0)
  }
}

///|
/// Return the size of a generated fixture without parsing it.
pub fn fixture_size(name : String) -> Int {
  match name {
    "minimal" => minimal_fixture().length()
    "mixed" => mixed_fixture().length()
    "four-state" => four_state_fixture().length()
    "hierarchy" => hierarchy_fixture().length()
    "edges" => edge_fixture().length()
    _ => 0
  }
}