///|
struct LexiconEntry {
  phrase_value : String
  code_values : Array[Int]
  reading_values : Array[Syllable]
} derive(Eq, Debug)

///|
struct LexiconNode {
  children : Map[Int, Int]
  entries : Array[Int]
}

///|
/// Mutable construction surface with validation on every insertion.
pub struct LexiconBuilder {
  values : Array[LexiconEntry]
}

///|
/// Immutable phrase resolver backed by a flattened Trie.
pub struct Lexicon {
  nodes : Array[LexiconNode]
  values : Array[LexiconEntry]
}

///|
/// Evidence returned for one longest phrase match.
pub struct LexiconMatch {
  phrase_value : String
  consumed : Int
  reading_values : Array[Syllable]
} derive(Eq, Debug)

///|
fn copy_syllables(values : Array[Syllable]) -> Array[Syllable] {
  let output : Array[Syllable] = []
  for value in values {
    output.push(value)
  }
  output
}

///|
pub fn LexiconBuilder::new() -> LexiconBuilder {
  { values: [] }
}

///|
/// Adds one phrase after validating scalar and reading counts.
pub fn LexiconBuilder::add(
  self : LexiconBuilder,
  phrase : String,
  readings : Array[String],
) -> Result[Unit, PinyinError] {
  let codes = text_code_points(phrase)
  if codes.length() == 0 {
    return Err(InvalidLexiconEntry(phrase, "empty_phrase"))
  }
  if codes.length() != readings.length() {
    return Err(InvalidLexiconEntry(phrase, "reading_count_mismatch"))
  }
  for existing in self.values {
    if existing.phrase_value == phrase {
      return Err(ConflictingLexiconEntry(phrase))
    }
  }
  let parsed : Array[Syllable] = []
  for reading in readings {
    match parse_syllable(reading) {
      Err(error) => return Err(error)
      Ok(value) => parsed.push(value)
    }
  }
  self.values.push({
    phrase_value: phrase,
    code_values: codes,
    reading_values: parsed,
  })
  Ok(())
}

///|
/// Freezes entries into deterministic Trie nodes.
pub fn LexiconBuilder::build(self : LexiconBuilder) -> Lexicon {
  let entries : Array[LexiconEntry] = []
  for value in self.values {
    entries.push({
      phrase_value: value.phrase_value,
      code_values: value.code_values.copy(),
      reading_values: copy_syllables(value.reading_values),
    })
  }
  let nodes : Array[LexiconNode] = [{ children: Map([]), entries: [] }]
  for entry_index = 0
      entry_index < entries.length()
      entry_index = entry_index + 1 {
    let mut node_index = 0
    for code in entries[entry_index].code_values {
      match nodes[node_index].children.get(code) {
        Some(next) => node_index = next
        None => {
          let next = nodes.length()
          nodes.push({ children: Map([]), entries: [] })
          nodes[node_index].children.set(code, next)
          node_index = next
        }
      }
    }
    nodes[node_index].entries.push(entry_index)
  }
  { nodes, values: entries }
}

///|
/// Returns the longest phrase beginning at a scalar offset.
pub fn Lexicon::longest_match(
  self : Lexicon,
  source : Array[Int],
  start : Int,
) -> LexiconMatch? {
  if start < 0 || start >= source.length() {
    return None
  }
  let mut node_index = 0
  let mut cursor = start
  let mut best_entry = -1
  let mut best_length = 0
  while cursor < source.length() {
    match self.nodes[node_index].children.get(source[cursor]) {
      None => break
      Some(next) => {
        node_index = next
        cursor = cursor + 1
        if self.nodes[node_index].entries.length() > 0 {
          best_entry = self.nodes[node_index].entries[0]
          best_length = cursor - start
        }
      }
    }
  }
  if best_entry < 0 {
    None
  } else {
    let entry = self.values[best_entry]
    Some({
      phrase_value: entry.phrase_value,
      consumed: best_length,
      reading_values: copy_syllables(entry.reading_values),
    })
  }
}

///|
pub fn LexiconMatch::phrase(self : LexiconMatch) -> String {
  self.phrase_value
}

///|
pub fn LexiconMatch::length(self : LexiconMatch) -> Int {
  self.consumed
}

///|
pub fn LexiconMatch::readings(self : LexiconMatch) -> Array[Syllable] {
  copy_syllables(self.reading_values)
}