///|
/// Bounded candidate strings produced from recorded token readings.
pub struct VariantExpansion {
  variant_values : Array[String]
  truncated_value : Bool
} derive(Eq, Debug)

///|
fn append_unique_variant(values : Array[String], candidate : String) -> Unit {
  for value in values {
    if value == candidate {
      return
    }
  }
  values.push(candidate)
}

///|
fn token_variant_values(
  token : ConvertedToken,
  style : ToneStyle,
) -> Result[Array[String], PinyinError] {
  if token.kind() == LiteralToken {
    return Ok([token.rendered()])
  }
  let values : Array[String] = []
  for candidate in token.candidates() {
    let parsed = match parse_syllable(candidate) {
      Err(error) => return Err(error)
      Ok(value) => value
    }
    append_unique_variant(values, parsed.format(style, Lowercase))
  }
  if values.length() == 0 {
    return Err(InvalidGeneratedData("pinyin_token_without_candidates"))
  }
  Ok(values)
}

///|
/// Expands recorded alternatives without performing contextual inference.
pub fn expand_conversion_variants(
  result : ConversionResult,
  style : ToneStyle,
  separator : String,
  limit : Int,
) -> Result[VariantExpansion, PinyinError] {
  if limit <= 0 {
    return Err(InvalidVariantLimit(limit))
  }
  if separator.length() == 0 {
    return Err(InvalidSeparator("empty"))
  }
  let mut variants : Array[String] = [""]
  let mut truncated = false
  let mut previous_kind : TokenKind? = None
  for token in result.tokens() {
    let token_values = match token_variant_values(token, style) {
      Err(error) => return Err(error)
      Ok(values) => values
    }
    if variants.length() > limit / token_values.length() {
      truncated = true
    }
    let next : Array[String] = []
    for prefix in variants {
      for value in token_values {
        if next.length() >= limit {
          truncated = true
          break
        }
        let boundary = if previous_kind == Some(PinyinToken) &&
          token.kind() == PinyinToken {
          separator
        } else {
          ""
        }
        append_unique_variant(next, prefix + boundary + value)
      }
      if next.length() >= limit {
        break
      }
    }
    variants = next
    previous_kind = Some(token.kind())
  }
  Ok({ variant_values: variants, truncated_value: truncated })
}

///|
pub fn VariantExpansion::values(self : VariantExpansion) -> Array[String] {
  self.variant_values.copy()
}

///|
pub fn VariantExpansion::generated(self : VariantExpansion) -> Int {
  self.variant_values.length()
}

///|
pub fn VariantExpansion::truncated(self : VariantExpansion) -> Bool {
  self.truncated_value
}