///|
pub(all) struct PairValidation {
  cantus : SequenceReport
  counterpoint : SequenceReport
  analysis : Analysis
  summary : AnalysisSummary
  profile : String
} derive(Debug, Eq, ToJson)

///|
pub fn analyze_with_profile(
  cantus : Array[Note],
  counter : Array[Note],
  profile : RuleProfileConfig,
) -> Analysis {
  let options = {
    species: First,
    strict_cadence: profile.require_cadence,
    max_melody_span: profile.max_span,
    max_leap: profile.max_leap,
  }
  analyze_counterpoint(cantus, counter, options~)
}

///|
pub fn validate_pair(
  cantus : Array[Note],
  counter : Array[Note],
  profile : RuleProfileConfig,
) -> PairValidation {
  let analysis = analyze_with_profile(cantus, counter, profile)
  {
    cantus: sequence_report(cantus),
    counterpoint: sequence_report(counter),
    analysis,
    summary: summarize(analysis),
    profile: profile.label(),
  }
}

///|
pub fn PairValidation::passed(self : PairValidation) -> Bool {
  self.cantus.is_valid() &&
  self.counterpoint.is_valid() &&
  self.summary.passed()
}

///|
pub fn PairValidation::quality_score(self : PairValidation) -> Int {
  score_for(self.analysis, profile_config(Classroom))
}

///|
pub fn PairValidation::markdown(self : PairValidation) -> String {
  let lines : Array[String] = [
    "# Pair validation",
    "",
    "- Profile: \{self.profile}",
    "- Passed: \{self.passed()}",
    "- Score: \{self.quality_score()}",
    "",
    sequence_markdown(self.cantus),
    "",
    sequence_markdown(self.counterpoint),
    "",
    analysis_markdown(self.analysis),
  ]
  lines.join("\n")
}

///|
pub fn compare_profiles(
  cantus : Array[Note],
  counter : Array[Note],
) -> Array[AnalysisSummary] {
  let result : Array[AnalysisSummary] = []
  for profile in [Classroom, Strict, Renaissance] {
    result.push(
      summarize(analyze_with_profile(cantus, counter, profile_config(profile))),
    )
  }
  result
}

///|
pub fn profile_labels() -> Array[String] {
  ["classroom", "strict", "renaissance", "custom"]
}

///|
pub fn valid_profiles() -> Array[RuleProfileConfig] {
  [
    RuleProfileConfig::classroom(),
    RuleProfileConfig::strict(),
    RuleProfileConfig::renaissance(),
  ]
}