///|
pub(all) struct AnalysisOutcome {
  source : @source.SourceFile
  parsed : @parser.ParseResult
  diagnostics : Array[@diagnostic.Diagnostic]
  index : @semantic.SemanticIndex?
}

///|
pub fn analyze_source(
  source : @source.SourceFile,
  config : @lint.LintConfig,
) -> AnalysisOutcome {
  let parsed = @parser.parse(source)
  let diagnostics : Array[@diagnostic.Diagnostic] = []
  for error in parsed.errors {
    diagnostics.push(
      @diagnostic.Diagnostic::new(
        "RTL001",
        @diagnostic.Error,
        error.message,
        error.span,
      ).with_file(source.name()),
    )
  }
  let index : @semantic.SemanticIndex? = if parsed.errors.length() == 0 {
    let value = @semantic.index(parsed.design)
    for diagnostic in @lint.run(value, config) {
      diagnostics.push(diagnostic.with_file(source.name()))
    }
    Some(value)
  } else {
    None
  }
  { source, parsed, diagnostics, index }
}

///|
pub fn analyze_text(
  file_name : String,
  text : String,
  config : @lint.LintConfig,
) -> AnalysisOutcome {
  analyze_source(@source.SourceFile::new(file_name, text), config)
}

///|
pub fn AnalysisOutcome::report(
  self : AnalysisOutcome,
) -> @diagnostic.DiagnosticReport {
  @diagnostic.DiagnosticReport::new(self.diagnostics)
}

///|
pub fn AnalysisOutcome::has_errors(self : AnalysisOutcome) -> Bool {
  self.report().has_errors()
}

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

///|
pub fn AnalysisOutcome::summary(self : AnalysisOutcome) -> String {
  self.parsed.summary() + ", " + self.report().summary()
}

///|
pub fn AnalysisOutcome::semantic_summary(self : AnalysisOutcome) -> String {
  match self.index {
    Some(index) => index.analytics_summary()
    None => "semantic analysis skipped after parse errors"
  }
}

///|
pub fn AnalysisOutcome::ir(self : AnalysisOutcome) -> String {
  match self.index {
    Some(index) => index.to_ir_json()
    None => "{\"modules\":[],\"errors\":true}\n"
  }
}

///|
pub fn AnalysisOutcome::markdown(
  self : AnalysisOutcome,
  title : String,
) -> String {
  self.report().markdown(title)
}

///|
pub fn AnalysisOutcome::render(
  self : AnalysisOutcome,
  format : CliFormat,
) -> String {
  match format {
    Text => @diagnostic.render_text(self.diagnostics, self.source)
    Json =>
      @diagnostic.render_report_json(
        self.diagnostics,
        self.source,
        self.source.name(),
      )
    Sarif => @diagnostic.render_sarif(self.diagnostics, self.source)
  }
}