///|
priv struct Parser {
  source : String
  mut position : Int
}

///|
fn Parser::new(source : String) -> Parser {
  { source, position: 0 }
}

///|
fn Parser::peek(self : Parser) -> Char? {
  self.source.view().get_char(self.position)
}

///|
fn Parser::advance(self : Parser) -> Unit {
  self.position = self.position + 1
}

///|
fn Parser::skip_space(self : Parser) -> Unit {
  while self.peek() is Some(ch) && ch.is_whitespace() {
    self.advance()
  }
}

///|
fn Parser::token(self : Parser) -> String {
  let out = StringBuilder::new()
  while self.peek() is Some(ch) &&
        !ch.is_whitespace() &&
        ch != ',' &&
        ch != '{' &&
        ch != '}' {
    out.write_char(ch)
    self.advance()
  }
  out.to_string()
}

///|
fn is_ascii_digit(ch : Char) -> Bool {
  ch >= '0' && ch <= '9'
}

///|
fn is_exact_selector(selector : String) -> Bool {
  if !selector.has_prefix("=") {
    return false
  }
  if selector.length() < 2 {
    return false
  }
  let mut has_digit = false
  for index, ch in selector {
    if index == 0 {
      continue
    }
    if index == 1 && ch == '-' {
      continue
    }
    if !is_ascii_digit(ch) {
      return false
    }
    has_digit = true
  }
  has_digit
}

///|
fn is_plural_category(selector : String) -> Bool {
  selector == "zero" ||
  selector == "one" ||
  selector == "two" ||
  selector == "few" ||
  selector == "many" ||
  selector == "other"
}

///|
fn validate_selector(
  kind : String,
  selector : String,
  position : Int,
) -> Unit raise MessageError {
  if kind != "select" &&
    !is_plural_category(selector) &&
    !is_exact_selector(selector) {
    raise ParseError(
      position~,
      message="Invalid plural selector '\{selector}'.",
    )
  }
}

///|
fn Parser::expect(self : Parser, expected : Char) -> Unit raise MessageError {
  match self.peek() {
    Some(ch) if ch == expected => self.advance()
    _ =>
      raise ParseError(
        position=self.position,
        message="Expected '\{expected}'.",
      )
  }
}

///|
fn Parser::flush_text(nodes : Array[MessageNode], text : StringBuilder) -> Unit {
  if !text.is_empty() {
    nodes.push(TextNode(text.to_string()))
    text.reset()
  }
}

///|
fn Parser::nodes_until(
  self : Parser,
  close? : Char,
) -> Array[MessageNode] raise MessageError {
  let nodes : Array[MessageNode] = []
  let text = StringBuilder::new()
  while self.peek() is Some(ch) {
    match close {
      Some(end) if ch == end => {
        Parser::flush_text(nodes, text)
        self.advance()
        return nodes
      }
      _ => ()
    }
    match ch {
      '{' => {
        Parser::flush_text(nodes, text)
        self.advance()
        nodes.push(self.braced())
      }
      '#' => {
        Parser::flush_text(nodes, text)
        nodes.push(PoundNode)
        self.advance()
      }
      '\\' => {
        self.advance()
        match self.peek() {
          Some(escaped) => {
            text.write_char(escaped)
            self.advance()
          }
          None =>
            raise ParseError(position=self.position, message="Dangling escape.")
        }
      }
      _ => {
        text.write_char(ch)
        self.advance()
      }
    }
  }
  Parser::flush_text(nodes, text)
  match close {
    Some(_) =>
      raise ParseError(
        position=self.position,
        message="Unclosed message branch.",
      )
    None => nodes
  }
}

///|
fn Parser::braced(self : Parser) -> MessageNode raise MessageError {
  self.skip_space()
  let name = self.token()
  if name == "" {
    raise ParseError(position=self.position, message="Expected argument name.")
  }
  self.skip_space()
  match self.peek() {
    Some('}') => {
      self.advance()
      ArgumentNode(name)
    }
    Some(',') => {
      self.advance()
      self.skip_space()
      let kind = self.token()
      if kind == "selectorordinal" {
        raise ParseError(
          position=self.position,
          message="Did you mean 'selectordinal' instead of 'selectorordinal'?",
        )
      }
      if kind != "plural" && kind != "selectordinal" && kind != "select" {
        raise ParseError(
          position=self.position,
          message="Unsupported formatter '\{kind}'.",
        )
      }
      self.skip_space()
      self.expect(',')
      let cases : Map[String, Array[MessageNode]] = Map([])
      while self.peek() is Some(ch) && ch != '}' {
        self.skip_space()
        let selector_position = self.position
        let selector = self.token()
        if selector == "" {
          raise ParseError(
            position=self.position,
            message="Expected choice selector.",
          )
        }
        validate_selector(kind, selector, selector_position)
        if cases.contains(selector) {
          raise ParseError(
            position=selector_position,
            message="Duplicate selector '\{selector}'.",
          )
        }
        self.skip_space()
        self.expect('{')
        cases[selector] = self.nodes_until(close='}')
        self.skip_space()
      }
      self.expect('}')
      if !cases.contains("other") {
        raise ParseError(
          position=self.position,
          message="Choice needs an other branch.",
        )
      }
      ChoiceNode(name, kind, cases)
    }
    _ =>
      raise ParseError(position=self.position, message="Expected '}' or ','.")
  }
}

///|
/// Parse a message template using the supported ICU MessageFormat subset.
pub fn parse_message(template : String) -> Message raise MessageError {
  let parser = Parser::new(template)
  { nodes: parser.nodes_until() }
}