///|
fn rule_text(rule : RuleKind) -> String {
  match rule {
    ParallelPerfect => "ParallelPerfect"
    HiddenPerfect => "HiddenPerfect"
    VoiceCrossing => "VoiceCrossing"
    DissonancePlacement => "DissonancePlacement"
    MelodyRange => "MelodyRange"
    LargeLeap => "LargeLeap"
    LeapRecovery => "LeapRecovery"
    SpeciesRhythm => "SpeciesRhythm"
    CadenceShape => "CadenceShape"
  }
}

///|
fn severity_text(severity : Severity) -> String {
  match severity {
    Error => "Error"
    Warning => "Warning"
    Info => "Info"
  }
}

///|
fn issue_index(issue : SequenceIssue) -> Int {
  match issue {
    EmptySequence => -1
    NegativePitch(index~) => index
    InvalidDuration(index~) => index
    NonMonotonicOnset(index~) => index
    Overlap(index~) => index
    DuplicateOnset(index~) => index
    ExcessiveGap(index~) => index
  }
}

///|
fn csv_escape(value : String) -> String {
  if value.contains(",") || value.contains("\"") || value.contains("\n") {
    "\"\{value}\""
  } else {
    value
  }
}

///|
pub fn finding_csv(finding : Finding) -> String {
  "\{csv_escape(rule_text(finding.rule))},\{csv_escape(severity_text(finding.severity))},\{finding.index},\{csv_escape(finding.message)}"
}

///|
pub fn analysis_csv(analysis : Analysis) -> String {
  let lines : Array[String] = ["rule,severity,index,message"]
  for finding in analysis.findings {
    lines.push(finding_csv(finding))
  }
  lines.join("\n")
}

///|
pub fn summary_csv(summary : AnalysisSummary) -> String {
  "total,errors,warnings,infos,structural,harmonic,score\n\{summary.total},\{summary.errors},\{summary.warnings},\{summary.infos},\{summary.structural},\{summary.harmonic},\{summary.score}"
}

///|
pub fn analysis_markdown(analysis : Analysis) -> String {
  let summary = summarize(analysis)
  let lines : Array[String] = [
    "## Counterpoint analysis",
    "",
    "| Metric | Value |",
    "| --- | ---: |",
    "| Grade | \{summary.grade()} |",
    "| Score | \{summary.score} |",
    "| Errors | \{summary.errors} |",
    "| Warnings | \{summary.warnings} |",
    "| Infos | \{summary.infos} |",
    "",
    "| Index | Severity | Rule | Message |",
    "| ---: | --- | --- | --- |",
  ]
  for finding in analysis.findings {
    lines.push(
      "| \{finding.index} | \{severity_text(finding.severity)} | \{rule_text(finding.rule)} | \{finding.message} |",
    )
  }
  lines.join("\n")
}

///|
pub fn sequence_markdown(report : SequenceReport) -> String {
  let lines : Array[String] = [
    "## Sequence validation",
    "",
    "- Notes: \{report.note_count}",
    "- Duration: \{report.duration}",
    "- Valid: \{report.is_valid()}",
    "- Issues: \{report.error_count()}",
  ]
  for issue in report.issues {
    lines.push("- issue at \{issue_index(issue)}")
  }
  lines.join("\n")
}

///|
pub fn analysis_title(analysis : Analysis) -> String {
  let summary = summarize(analysis)
  "counterpoint \{summary.grade()} (\{summary.score}/100)"
}

///|
pub fn report_line_count(analysis : Analysis) -> Int {
  if analysis.findings.length() == 0 {
    1
  } else {
    analysis.findings.length() + 1
  }
}

///|
pub fn compact_report(analysis : Analysis) -> String {
  let summary = summarize(analysis)
  "\{summary.grade()}:\{summary.score} e=\{summary.errors} w=\{summary.warnings}"
}