///|
/// A branch in a `select`, `plural`, or `selectordinal` message argument.
pub struct MessageCase {
  selector : String
  nodes : Array[MessageNode]
} derive(Eq, Debug)

///|
/// The parsed representation of a localized message template.
///
/// Keeping the syntax tree public lets applications cache compiled templates,
/// inspect their arguments, and build tooling without reparsing source text.
pub(all) enum MessageNode {
  Text(String)
  Argument(String)
  Select(String, Array[MessageCase])
  Plural(String, Int, PluralKind, Array[MessageCase])
} derive(Eq, Debug)

///|
pub fn MessageCase::new(
  selector : String,
  nodes : Array[MessageNode],
) -> MessageCase {
  { selector, nodes }
}

///|
pub fn MessageCase::selector(self : MessageCase) -> String {
  self.selector
}

///|
pub fn MessageCase::nodes(self : MessageCase) -> Array[MessageNode] {
  self.nodes.copy()
}

///|
/// A reusable parsed message template.
pub struct MessageTemplate {
  source : String
  nodes : Array[MessageNode]
} derive(Eq, Debug)

///|
pub fn MessageTemplate::source(self : MessageTemplate) -> String {
  self.source
}

///|
pub fn MessageTemplate::nodes(self : MessageTemplate) -> Array[MessageNode] {
  self.nodes.copy()
}

///|
/// Collects argument names in first-use order without duplicates.
pub fn MessageTemplate::arguments(self : MessageTemplate) -> Array[String] {
  let output : Array[String] = []
  collect_message_arguments(self.nodes, output)
  output
}

///|
fn collect_message_arguments(
  nodes : Array[MessageNode],
  output : Array[String],
) -> Unit {
  for node in nodes {
    match node {
      Text(_) => ()
      Argument(name) => push_unique_argument(output, name)
      Select(name, cases) => {
        push_unique_argument(output, name)
        for branch in cases {
          collect_message_arguments(branch.nodes, output)
        }
      }
      Plural(name, _, _, cases) => {
        push_unique_argument(output, name)
        for branch in cases {
          collect_message_arguments(branch.nodes, output)
        }
      }
    }
  }
}

///|
fn push_unique_argument(output : Array[String], name : String) -> Unit {
  for current in output {
    if current == name {
      return
    }
  }
  output.push(name)
}

///|
/// Returns the number of syntax-tree nodes, including nested branch content.
pub fn MessageTemplate::node_count(self : MessageTemplate) -> Int {
  count_message_nodes(self.nodes)
}

///|
fn count_message_nodes(nodes : Array[MessageNode]) -> Int {
  let mut count = 0
  for node in nodes {
    count = count + 1
    match node {
      Select(_, cases) | Plural(_, _, _, cases) =>
        for branch in cases {
          count = count + count_message_nodes(branch.nodes)
        }
      _ => ()
    }
  }
  count
}