///|
pub fn XmlNode::as_out(self : XmlNode) -> Out {
  match self {
    Element(value) => XmlElement(value)
    Fragment(value) => XmlFragment(value)
    Text(value) => XmlText(value)
  }
}

///|
pub fn XmlNode::branch_id(self : XmlNode) -> BranchId {
  self.as_out().branch_id().unwrap()
}

///|
pub fn XmlNode::is_deleted(self : XmlNode) -> Bool {
  self.as_out().is_deleted()
}

///|
fn XmlNode::adjacent(self : XmlNode, forward : Bool) -> XmlNode? {
  let (doc, parent) = self.as_out().location().unwrap()
  guard parent is Nested(id) else { return None }
  let mut cursor = doc.find(id)
  while cursor is Some(item) {
    let next = if forward { item.right } else { item.left }
    guard next is Some(id) else { return None }
    cursor = doc.find(id)
    if cursor is Some(item) && !item.deleted && item.content is Shared(kind) {
      return doc.shared_output(id, kind).as_xml()
    }
  }
  None
}

///|
pub fn XmlNode::next_sibling(self : XmlNode) -> XmlNode? {
  self.adjacent(true)
}

///|
pub fn XmlNode::prev_sibling(self : XmlNode) -> XmlNode? {
  self.adjacent(false)
}

///|
/// Following siblings; backwards=true visits preceding siblings nearest first.
pub fn XmlNode::siblings(
  self : XmlNode,
  backwards~ : Bool = false,
) -> Iter[XmlNode] {
  let result = []
  let mut current = self.adjacent(!backwards)
  while current is Some(node) {
    result.push(node)
    current = node.adjacent(!backwards)
  }
  result.iter()
}

///|
pub fn XmlFragment::get(self : XmlFragment, index : Int32) -> XmlNode? {
  let array : ArrayRef = { doc: self.doc, name: self.name, }
  match array.get(index) {
    Some(value) => value.as_xml()
    None => None
  }
}

///|
pub fn XmlFragment::first_child(self : XmlFragment) -> XmlNode? {
  self.get(0)
}

///|
/// Depth-first traversal, excluding this fragment and deleted subtrees.
pub fn XmlFragment::successors(self : XmlFragment) -> Iter[XmlNode] {
  let result = []
  let pending = self.children().rev()
  while pending.pop() is Some(node) {
    result.push(node)
    let children = match node {
      Element(value) => value.as_fragment().children()
      Fragment(value) => value.children()
      Text(_) => []
    }
    for child in children.rev() {
      pending.push(child)
    }
  }
  result.iter()
}

///|
pub fn XmlFragment::insert(
  self : XmlFragment,
  index : Int32,
  value : In,
) -> XmlNode {
  guard value is (XmlElement(_, _, _) | XmlFragment(_) | XmlText(_, _)) else {
    abort("XML child must be an XML node")
  }
  self.doc.with_mutation(fn() {
    guard value.content(false) is Shared(kind) else { abort("XML node type") }
    let id = self.insert_shared(index, kind)
    value.populate(self.doc, id).as_xml().unwrap()
  })
}

///|
pub fn XmlFragment::push_back(self : XmlFragment, value : In) -> XmlNode {
  self.insert(self.length(), value)
}

///|
pub fn XmlFragment::push_front(self : XmlFragment, value : In) -> XmlNode {
  self.insert(0, value)
}

///|
pub fn XmlFragment::remove(self : XmlFragment, index : Int32) -> Unit {
  self.remove_range(index, 1)
}