///|
pub fn media_candidate_line(candidate : MediaCandidate) -> String {
  let buf = StringBuilder()
  buf.write_string(candidate.offer.value)
  buf.write_string(" q=")
  buf.write_string(quality_to_string(candidate.quality))
  buf.write_string(" via ")
  buf.write_string(candidate.matched_range.raw)
  buf.write_string(" specificity=")
  buf.write_string(candidate.specificity.to_string())
  buf.write_string(" params=")
  buf.write_string(candidate.parameter_matches.to_string())
  buf.to_string()
}

///|
pub fn language_candidate_line(candidate : LanguageCandidate) -> String {
  let buf = StringBuilder()
  buf.write_string(candidate.offer.tag)
  buf.write_string(" q=")
  buf.write_string(quality_to_string(candidate.quality))
  buf.write_string(" via ")
  buf.write_string(candidate.matched_range.raw)
  buf.write_string(" specificity=")
  buf.write_string(candidate.specificity.to_string())
  buf.to_string()
}

///|
pub fn encoding_candidate_line(candidate : EncodingCandidate) -> String {
  let buf = StringBuilder()
  buf.write_string(candidate.offer.coding)
  buf.write_string(" q=")
  buf.write_string(quality_to_string(candidate.quality))
  buf.write_string(" via ")
  buf.write_string(candidate.matched_range.raw)
  buf.write_string(" specificity=")
  buf.write_string(candidate.specificity.to_string())
  buf.to_string()
}

///|
pub fn charset_candidate_line(candidate : CharsetCandidate) -> String {
  let buf = StringBuilder()
  buf.write_string(candidate.offer.canonical)
  buf.write_string(" q=")
  buf.write_string(quality_to_string(candidate.quality))
  buf.write_string(" via ")
  buf.write_string(candidate.matched_range.raw)
  buf.write_string(" specificity=")
  buf.write_string(candidate.specificity.to_string())
  buf.to_string()
}

///|
pub fn variant_candidate_line(candidate : VariantCandidate) -> String {
  let buf = StringBuilder()
  buf.write_string(candidate.variant.id)
  buf.write_string(" q=")
  buf.write_string(quality_to_string(candidate.quality))
  buf.write_string(" media=")
  buf.write_string(candidate.variant.media)
  match candidate.variant.language {
    Some(value) => {
      buf.write_string(" language=")
      buf.write_string(value)
    }
    None => ()
  }
  match candidate.variant.encoding {
    Some(value) => {
      buf.write_string(" encoding=")
      buf.write_string(value)
    }
    None => ()
  }
  match candidate.variant.charset {
    Some(value) => {
      buf.write_string(" charset=")
      buf.write_string(value)
    }
    None => ()
  }
  buf.to_string()
}

///|
pub fn explain_media_candidates(
  accept_header : StringView,
  offers : Array[String],
) -> Result[Array[String], ParseError] {
  match negotiate_media(accept_header, offers) {
    Ok(decision) =>
      Ok(decision.matches.map(candidate => media_candidate_line(candidate)))
    Err(err) => Err(err)
  }
}

///|
pub fn explain_language_candidates(
  accept_language : StringView,
  offers : Array[String],
) -> Result[Array[String], ParseError] {
  match negotiate_language(accept_language, offers) {
    Ok(decision) =>
      Ok(decision.matches.map(candidate => language_candidate_line(candidate)))
    Err(err) => Err(err)
  }
}

///|
pub fn explain_encoding_candidates(
  accept_encoding : StringView,
  offers : Array[String],
) -> Result[Array[String], ParseError] {
  match negotiate_encoding(accept_encoding, offers) {
    Ok(decision) =>
      Ok(decision.matches.map(candidate => encoding_candidate_line(candidate)))
    Err(err) => Err(err)
  }
}

///|
pub fn explain_charset_candidates(
  accept_charset : StringView,
  offers : Array[String],
) -> Result[Array[String], ParseError] {
  match negotiate_charset(accept_charset, offers) {
    Ok(decision) =>
      Ok(decision.matches.map(candidate => charset_candidate_line(candidate)))
    Err(err) => Err(err)
  }
}

///|
pub fn explain_variant_candidates(decision : VariantDecision) -> Array[String] {
  decision.matches.map(candidate => variant_candidate_line(candidate))
}

///|
pub fn format_lines(lines : Array[String]) -> String {
  let buf = StringBuilder()
  for i in 0.. 0 {
      buf.write_char('\n')
    }
    buf.write_string(lines[i])
  }
  buf.to_string()
}

///|
pub fn negotiation_matrix_report(
  preferences : RequestPreferences,
  variants : Array[ServerVariant],
) -> Result[String, ParseError] {
  match negotiate_variant(preferences, variants) {
    Ok(decision) => {
      let lines = explain_variant_candidates(decision)
      let buf = StringBuilder()
      buf.write_string(format_variant_decision(decision))
      if lines.length() > 0 {
        buf.write_char('\n')
        buf.write_string(format_lines(lines))
      }
      Ok(buf.to_string())
    }
    Err(err) => Err(err)
  }
}