///|
/// Container for child nodes in internal nodes
/// Each child is stored with its accumulated text information
pub struct NodeChildren {
  mut children : Array[(TextInfo, Node)]
}

///|
/// Create a new empty NodeChildren container
pub fn NodeChildren::new() -> NodeChildren {
  { children: [] }
}

///|
/// Create NodeChildren with initial capacity
pub fn NodeChildren::with_capacity(capacity : Int) -> NodeChildren {
  { children: Array::make(capacity, (TextInfo::new(), Node::new_empty())) }
}

///|
/// Get the number of children
pub fn NodeChildren::length(self : NodeChildren) -> Int {
  self.children.length()
}

///|
/// Check if container is empty
pub fn NodeChildren::children_is_empty(self : NodeChildren) -> Bool {
  self.children.length() == 0
}

///|
/// Add a child node with its text info
pub fn NodeChildren::push(
  self : NodeChildren,
  info : TextInfo,
  node : Node,
) -> Unit {
  self.children.push((info, node))
}

///|
/// Get child at index
pub fn NodeChildren::get(self : NodeChildren, index : Int) -> (TextInfo, Node) {
  self.children[index]
}

///|
/// Get text info at index
pub fn NodeChildren::get_info(self : NodeChildren, index : Int) -> TextInfo {
  let (info, _) = self.children[index]
  info
}

///|
/// Get node at index
pub fn NodeChildren::get_node(self : NodeChildren, index : Int) -> Node {
  let (_, node) = self.children[index]
  node
}

///|
/// Set child at index
pub fn NodeChildren::set(
  self : NodeChildren,
  index : Int,
  info : TextInfo,
  node : Node,
) -> Unit {
  self.children[index] = (info, node)
}

///|
/// Insert child at index
pub fn NodeChildren::children_insert(
  self : NodeChildren,
  index : Int,
  info : TextInfo,
  node : Node,
) -> Unit {
  // Create new array with one more element
  let new_children = Array::make(
    self.children.length() + 1,
    (TextInfo::new(), Node::new_empty()),
  )

  // Copy elements before insertion point
  for i = 0; i < index; i = i + 1 {
    new_children[i] = self.children[i]
  }

  // Insert new element
  new_children[index] = (info, node)

  // Copy elements after insertion point
  for i = index; i < self.children.length(); i = i + 1 {
    new_children[i + 1] = self.children[i]
  }
  self.children = new_children
}

///|
/// Remove and return child at index
pub fn NodeChildren::children_remove(
  self : NodeChildren,
  index : Int,
) -> (TextInfo, Node) {
  let result = self.children[index]

  // Create new array with one less element
  let new_children = Array::make(
    self.children.length() - 1,
    (TextInfo::new(), Node::new_empty()),
  )

  // Copy elements before removal point
  for i = 0; i < index; i = i + 1 {
    new_children[i] = self.children[i]
  }

  // Copy elements after removal point
  for i = index + 1; i < self.children.length(); i = i + 1 {
    new_children[i - 1] = self.children[i]
  }
  self.children = new_children
  result
}

///|
/// Split children at index, returning the right part
pub fn NodeChildren::split_off(self : NodeChildren, at : Int) -> NodeChildren {
  let right_len = self.children.length() - at
  let right_children = Array::make(
    right_len,
    (TextInfo::new(), Node::new_empty()),
  )

  // Copy right part
  for i = 0; i < right_len; i = i + 1 {
    right_children[i] = self.children[at + i]
  }

  // Truncate left part
  let left_children = Array::make(at, (TextInfo::new(), Node::new_empty()))
  for i = 0; i < at; i = i + 1 {
    left_children[i] = self.children[i]
  }
  self.children = left_children
  { children: right_children }
}

///|
/// Combine all text info from children
pub fn NodeChildren::total_info(self : NodeChildren) -> TextInfo {
  let mut result = TextInfo::new()
  for i = 0; i < self.children.length(); i = i + 1 {
    let (info, _) = self.children[i]
    result = result + info
  }
  result
}

///|
/// Find the child that contains the given character index
/// Returns (child_index, accumulated_info_before_child)
pub fn NodeChildren::find_child_at_char(
  self : NodeChildren,
  char_idx : UInt64,
) -> (Int, TextInfo) {
  let mut acc_info = TextInfo::new()
  for i = 0; i < self.children.length(); i = i + 1 {
    let (info, _) = self.children[i]
    if char_idx < acc_info.chars + info.chars {
      return (i, acc_info)
    }
    acc_info = acc_info + info
  }

  // Return last child if index is at the end
  if self.children.length() > 0 {
    let last_idx = self.children.length() - 1
    let total = self.total_info()
    let (last_info, _) = self.children[last_idx]
    (last_idx, total - last_info)
  } else {
    (0, TextInfo::new())
  }
}

///|
/// Find the child that contains the given UTF-16 code unit index
pub fn NodeChildren::find_child_at_utf16_cu(
  self : NodeChildren,
  utf16_idx : UInt64,
) -> (Int, TextInfo) {
  let mut acc_info = TextInfo::new()
  for i = 0; i < self.children.length(); i = i + 1 {
    let (info, _) = self.children[i]
    if utf16_idx < acc_info.utf16_cu + info.utf16_cu {
      return (i, acc_info)
    }
    acc_info = acc_info + info
  }

  // Return last child if index is at the end
  if self.children.length() > 0 {
    let last_idx = self.children.length() - 1
    let total = self.total_info()
    let (last_info, _) = self.children[last_idx]
    (last_idx, total - last_info)
  } else {
    (0, TextInfo::new())
  }
}

///|
/// Find the child that contains the given line break index
pub fn NodeChildren::find_child_at_line_break(
  self : NodeChildren,
  line_idx : UInt64,
) -> (Int, TextInfo) {
  let mut acc_info = TextInfo::new()
  for i = 0; i < self.children.length(); i = i + 1 {
    let (info, _) = self.children[i]
    if line_idx <= acc_info.line_breaks + info.line_breaks {
      return (i, acc_info)
    }
    acc_info = acc_info + info
  }

  // Return last child if index is at the end
  if self.children.length() > 0 {
    let last_idx = self.children.length() - 1
    let total = self.total_info()
    let (last_info, _) = self.children[last_idx]
    (last_idx, total - last_info)
  } else {
    (0, TextInfo::new())
  }
}