///|
/// One element or text event with its full XML path.
pub struct XmlField {
  path : String
  attributes : Array[(String, String)]
  text : String?
}

///|
pub fn XmlField::get_attribute(self : XmlField, name : String) -> String? {
  for pair in self.attributes {
    if pair.0 == name {
      return Some(pair.1)
    }
  }
  None
}

///|
fn join_path(path : Array[String]) -> String {
  let output = StringBuilder()
  for i = 0; i < path.length(); i = i + 1 {
    if i > 0 {
      output.write_char('/')
    }
    output.write_string(path[i])
  }
  output.to_string()
}

///|
pub fn flatten_xml(xml_text : String) -> Array[XmlField] raise XmlParseError {
  let reader = XmlReader::from_string(xml_text)
  let path : Array[String] = []
  let fields : Array[XmlField] = []
  for ;; {
    match reader.read_event().kind {
      Start(element) => {
        path.push(element.name)
        fields.push({
          path: join_path(path),
          attributes: element.attributes,
          text: None,
        })
      }
      Empty(element) => {
        path.push(element.name)
        fields.push({
          path: join_path(path),
          attributes: element.attributes,
          text: None,
        })
        let _ = path.remove(path.length() - 1)
      }
      Text(content) => {
        fields.push({
          path: join_path(path),
          attributes: [],
          text: Some(content),
        })
      }
      End(_) => {
        if path.length() > 0 {
          let _ = path.remove(path.length() - 1)
        }
      }
      Eof => break
    }
  }
  fields
}

///|
pub fn first_text_by_suffix(
  fields : Array[XmlField],
  suffix : String,
) -> String? {
  for field in fields {
    if field.path.has_suffix(suffix) {
      match field.text {
        Some(text) =>
          if text != "" {
            return Some(text)
          }
        None => ()
      }
    }
  }
  None
}

///|
pub fn texts_by_suffix(fields : Array[XmlField], suffix : String) -> Array[String] {
  let values : Array[String] = []
  for field in fields {
    if field.path.has_suffix(suffix) {
      match field.text {
        Some(text) =>
          if text != "" {
            values.push(text)
          }
        None => ()
      }
    }
  }
  values
}

///|
pub fn fields_by_suffix(
  fields : Array[XmlField],
  suffix : String,
) -> Array[XmlField] {
  let values : Array[XmlField] = []
  for field in fields {
    if field.path.has_suffix(suffix) {
      values.push(field)
    }
  }
  values
}

///|
pub fn elements_by_suffix(
  fields : Array[XmlField],
  suffix : String,
) -> Array[XmlField] {
  let values : Array[XmlField] = []
  for field in fields {
    if field.path.has_suffix(suffix) && field.text is None {
      values.push(field)
    }
  }
  values
}