///|
pub(all) enum RecoveryKind {
  MissingDelimiter
  UnexpectedToken
  MissingDeclaration
  MissingModuleEnd
  InvalidExpression
} derive(Eq, Debug)

///|
pub fn classify_error(message : String) -> RecoveryKind {
  if message.contains("endmodule") {
    MissingModuleEnd
  } else if message.contains("semicolon") || message.contains("';'") {
    MissingDelimiter
  } else if message.contains("expression") {
    InvalidExpression
  } else if message.contains("declaration") {
    MissingDeclaration
  } else {
    UnexpectedToken
  }
}

///|
pub fn ParseResult::error_kinds(
  self : ParseResult,
) -> Array[(RecoveryKind, Int)] {
  let result : Array[(RecoveryKind, Int)] = []
  for error in self.errors {
    let kind = classify_error(error.message)
    let mut found = false
    for index, entry in result {
      let (existing, count) = entry
      if existing == kind {
        result[index] = (existing, count + 1)
        found = true
      }
    }
    if !found {
      result.push((kind, 1))
    }
  }
  result
}

///|
pub fn ParseResult::recovered_module_count(self : ParseResult) -> Int {
  self.design.modules.length()
}

///|
pub fn ParseResult::recovered_item_count(self : ParseResult) -> Int {
  self.design.item_count()
}

///|
pub fn ParseResult::recovery_quality(self : ParseResult) -> String {
  if self.errors.length() == 0 {
    "clean"
  } else if self.design.modules.length() == 0 {
    "no-module-recovery"
  } else if self.errors.length() <= self.design.modules.length() {
    "good-recovery"
  } else {
    "partial-recovery"
  }
}

///|
pub fn ParseResult::first_module_span(self : ParseResult) -> @source.Span? {
  if self.design.modules.length() == 0 {
    None
  } else {
    Some(self.design.modules[0].span)
  }
}

///|
pub fn ParseResult::last_module_span(self : ParseResult) -> @source.Span? {
  if self.design.modules.length() == 0 {
    None
  } else {
    Some(self.design.modules[self.design.modules.length() - 1].span)
  }
}

///|
pub fn ParseResult::contains_recovered_module(
  self : ParseResult,
  name : String,
) -> Bool {
  self.design.module_names().contains(name)
}

///|
pub fn ParseResult::recovery_summary(self : ParseResult) -> String {
  "quality=" +
  self.recovery_quality() +
  ", errors=" +
  self.error_count().to_string() +
  ", modules=" +
  self.recovered_module_count().to_string() +
  ", items=" +
  self.recovered_item_count().to_string()
}