///|
/// A compact validation summary for dashboards and release checks.
pub(all) struct ValidationSummary {
  duplicate_modules : Int
  unresolved_instances : Int
  width_mismatches : Int
  undriven_internal_signals : Int
} derive(Eq, Debug)

///|
pub fn ValidationSummary::total(self : ValidationSummary) -> Int {
  self.duplicate_modules +
  self.unresolved_instances +
  self.width_mismatches +
  self.undriven_internal_signals
}

///|
pub fn ValidationSummary::is_clean(self : ValidationSummary) -> Bool {
  self.total() == 0
}

///|
pub fn ValidationSummary::describe(self : ValidationSummary) -> String {
  "duplicate_modules=" +
  self.duplicate_modules.to_string() +
  ", unresolved_instances=" +
  self.unresolved_instances.to_string() +
  ", width_mismatches=" +
  self.width_mismatches.to_string() +
  ", undriven_internal_signals=" +
  self.undriven_internal_signals.to_string()
}

///|
pub fn SemanticIndex::validate(self : SemanticIndex) -> ValidationSummary {
  let mut duplicate_modules = 0
  let mut unresolved_instances = 0
  let mut width_mismatches = 0
  for issue in self.issues {
    match issue.code {
      "RTL101" => duplicate_modules += 1
      "RTL102" => unresolved_instances += 1
      "RTL204" => width_mismatches += 1
      _ => ()
    }
  }
  let mut undriven_internal_signals = 0
  for mod in self.modules {
    for signal in mod.signals {
      if !signal.from_port && !signal.has_driver() {
        undriven_internal_signals += 1
      }
    }
  }
  {
    duplicate_modules,
    unresolved_instances,
    width_mismatches,
    undriven_internal_signals,
  }
}

///|
/// Return all semantic issues with a particular code.
pub fn SemanticIndex::issues_for_code(
  self : SemanticIndex,
  code : String,
) -> Array[SemanticIssue] {
  let result : Array[SemanticIssue] = []
  for issue in self.issues {
    if issue.code == code {
      result.push(issue)
    }
  }
  result
}

///|
/// Return the distinct semantic issue codes in source order.
pub fn SemanticIndex::issue_codes(self : SemanticIndex) -> Array[String] {
  let result : Array[String] = []
  for issue in self.issues {
    if !result.contains(issue.code) {
      result.push(issue.code)
    }
  }
  result
}