///|
/// Symmetric key comparison plus both original conversion results.
pub struct TextComparison {
  left_result : ConversionResult
  right_result : ConversionResult
  left_key_value : String
  right_key_value : String
  left_initials_value : String
  right_initials_value : String
  distance_value : DistanceResult
  similarity_value : Double
  forward_score_value : MatchExplanation
  reverse_score_value : MatchExplanation
} derive(Eq, Debug)

///|
fn normalized_distance_similarity(
  left : String,
  right : String,
  distance : DistanceResult,
) -> Double {
  let scale = if left.length() > right.length() {
    left.length()
  } else {
    right.length()
  }
  if scale == 0 {
    1.0
  } else {
    bounded_unit(1.0 - distance.cost() / scale.to_double())
  }
}

///|
/// Converts and compares two texts with the same explicit configuration.
pub fn compare_texts(
  left_source : String,
  right_source : String,
  lexicon : Lexicon,
  options : ConvertOptions,
  profile : FuzzyProfile,
) -> Result[TextComparison, PinyinError] {
  let left = match convert_text(left_source, lexicon, options) {
    Err(error) => return Err(error)
    Ok(value) => value
  }
  let right = match convert_text(right_source, lexicon, options) {
    Err(error) => return Err(error)
    Ok(value) => value
  }
  let left_key = generate_index_key(left, Compact).key()
  let right_key = generate_index_key(right, Compact).key()
  let left_initials = generate_index_key(left, Initials).key()
  let right_initials = generate_index_key(right, Initials).key()
  let distance = weighted_pinyin_distance(left_key, right_key, profile)
  let forward_candidate = match_candidate(right_source, right)
  let reverse_candidate = match_candidate(left_source, left)
  Ok({
    left_result: left,
    right_result: right,
    left_key_value: left_key,
    right_key_value: right_key,
    left_initials_value: left_initials,
    right_initials_value: right_initials,
    distance_value: distance,
    similarity_value: normalized_distance_similarity(
      left_key, right_key, distance,
    ),
    forward_score_value: score_match(left_key, forward_candidate, profile),
    reverse_score_value: score_match(right_key, reverse_candidate, profile),
  })
}

///|
pub fn TextComparison::left_conversion(
  self : TextComparison,
) -> ConversionResult {
  self.left_result
}

///|
pub fn TextComparison::right_conversion(
  self : TextComparison,
) -> ConversionResult {
  self.right_result
}

///|
pub fn TextComparison::left_key(self : TextComparison) -> String {
  self.left_key_value
}

///|
pub fn TextComparison::right_key(self : TextComparison) -> String {
  self.right_key_value
}

///|
pub fn TextComparison::left_initials(self : TextComparison) -> String {
  self.left_initials_value
}

///|
pub fn TextComparison::right_initials(self : TextComparison) -> String {
  self.right_initials_value
}

///|
pub fn TextComparison::distance(self : TextComparison) -> DistanceResult {
  self.distance_value
}

///|
pub fn TextComparison::similarity(self : TextComparison) -> Double {
  self.similarity_value
}

///|
pub fn TextComparison::same_full_pinyin(self : TextComparison) -> Bool {
  self.left_key_value == self.right_key_value
}

///|
pub fn TextComparison::same_initials(self : TextComparison) -> Bool {
  compact_query(self.left_initials_value) ==
  compact_query(self.right_initials_value)
}

///|
pub fn TextComparison::forward_score(self : TextComparison) -> MatchExplanation {
  self.forward_score_value
}

///|
pub fn TextComparison::reverse_score(self : TextComparison) -> MatchExplanation {
  self.reverse_score_value
}