///|
pub fn generate_run_plan(matrix : ScenarioMatrix) -> Array[RunPlanStep] {
  let result : Array[RunPlanStep] = []
  let mut index = 0
  let mut spec_index = 0
  while spec_index < matrix.specs.length() {
    let spec = matrix.specs[spec_index]
    let mut target_index = 0
    while target_index < spec.targets.length() {
      result.push(RunPlanStep::{
        index,
        scenario_id: spec.id,
        target: spec.targets[target_index],
        command: spec.command,
        expected: spec.expected,
        timeout_ms: spec.timeout_ms,
        required: spec.required,
      })
      index += 1
      target_index += 1
    }
    spec_index += 1
  }
  result
}

///|
pub fn run_plan_markdown(steps : Array[RunPlanStep]) -> String {
  let mut out = "# Scenario Run Plan\n\n"
  out = out + "| # | ID | Target | Expected | Timeout | Required | Command |\n"
  out = out + "| ---: | --- | --- | --- | ---: | --- | --- |\n"
  let mut index = 0
  while index < steps.length() {
    let step = steps[index]
    out = out +
      "| " +
      step.index.to_string() +
      " | `" +
      step.scenario_id +
      "` | `"
    out = out + step.target.name() + "` | " + step.expected.name() + " | "
    out = out +
      step.timeout_ms.to_string() +
      " ms | " +
      bool_text(step.required)
    out = out + " | `" + step.command + "` |\n"
    index += 1
  }
  out
}

///|
pub fn run_plan_json(steps : Array[RunPlanStep]) -> String {
  let mut out = "["
  let mut index = 0
  while index < steps.length() {
    if index > 0 {
      out = out + ","
    }
    let step = steps[index]
    out = out + "{\"index\":" + step.index.to_string() + ","
    out = out + "\"id\":\"" + json_escape(step.scenario_id) + "\","
    out = out + "\"target\":\"" + step.target.name() + "\","
    out = out + "\"expected\":\"" + step.expected.name() + "\","
    out = out + "\"timeout_ms\":" + step.timeout_ms.to_string() + ","
    out = out + "\"required\":" + bool_text(step.required) + ","
    out = out + "\"command\":\"" + json_escape(step.command) + "\"}"
    index += 1
  }
  out + "]"
}

///|
pub fn run_plan_shell(steps : Array[RunPlanStep]) -> String {
  let mut out = "# Generated by CakeCheck Scenario Matrix\n"
  let mut index = 0
  while index < steps.length() {
    let step = steps[index]
    out = out +
      "# [" +
      step.target.name() +
      "] " +
      step.scenario_id +
      " expected=" +
      step.expected.name() +
      " timeout_ms=" +
      step.timeout_ms.to_string() +
      "\n"
    out = out + step.command + "\n"
    index += 1
  }
  out
}

///|
pub fn run_plan_for_target(
  steps : Array[RunPlanStep],
  target : ScenarioTarget,
) -> Array[RunPlanStep] {
  let result : Array[RunPlanStep] = []
  let mut index = 0
  while index < steps.length() {
    if steps[index].target == target {
      result.push(steps[index])
    }
    index += 1
  }
  result
}

///|
pub fn run_plan_required_count(steps : Array[RunPlanStep]) -> Int {
  let mut count = 0
  let mut index = 0
  while index < steps.length() {
    if steps[index].required {
      count += 1
    }
    index += 1
  }
  count
}