///|
pub(all) struct ElementCount {
  symbol : String
  count : Int
} derive(Debug, Eq)

///|
pub(all) struct Formula {
  elements : Array[ElementCount]
} derive(Debug, Eq)

///|
pub fn parse_formula(text : String) -> Formula raise ThermoError {
  let chars = text.to_array()
  let elements : Array[ElementCount] = []
  let mut index = 0
  while index < chars.length() {
    let column = index + 1
    let first = chars[index]
    if !first.is_ascii_uppercase() {
      raise ThermoError::ParseError(
        line=1,
        column~,
        message="expected uppercase element symbol",
      )
    }
    let mut symbol = first.to_string()
    index += 1
    if index < chars.length() && chars[index].is_ascii_lowercase() {
      symbol = symbol + chars[index].to_string()
      index += 1
    }
    let mut count = 1
    if index < chars.length() && chars[index].is_ascii_digit() {
      count = 0
      while index < chars.length() && chars[index].is_ascii_digit() {
        count = count * 10 + chars[index].to_int() - '0'.to_int()
        index += 1
      }
      if count <= 0 {
        raise ThermoError::ParseError(
          line=1,
          column~,
          message="expected positive element count",
        )
      }
    }
    let mut found : Int? = None
    for i, element in elements {
      if element.symbol == symbol {
        found = Some(i)
      }
    }
    match found {
      Some(i) => elements[i] = { symbol, count: elements[i].count + count }
      None => elements.push({ symbol, count })
    }
  }
  if elements.length() == 0 {
    raise ThermoError::ParseError(
      line=1,
      column=1,
      message="expected uppercase element symbol",
    )
  }
  { elements, }
}

///|
pub fn Formula::count(self : Formula, symbol : String) -> Int {
  for element in self.elements {
    if element.symbol == symbol {
      return element.count
    }
  }
  0
}