///|
fn PdfDocument::pdf_structure_parent_tree_catalog(
  self : PdfDocument,
) -> @syntax.PdfObject? {
  Some(self.catalog()) catch {
    _ => None
  }
}

///|
fn PdfDocument::pdf_structure_root_parent_tree_entries(
  self : PdfDocument,
) -> Array[(Int, @syntax.PdfObject)] {
  match self.pdf_structure_parent_tree_catalog() {
    Some(catalog) =>
      match self.lookup_direct(pdf_structure_struct_tree_root_key(), catalog) {
        Some(root) =>
          match self.lookup_direct(pdf_structure_parent_tree_key(), root) {
            Some(parent_tree) => self.read_number_tree(parent_tree)
            None => []
          }
        None => []
      }
    None => []
  }
}

///|
fn pdf_structure_changed_parent_tree_key(
  changes : @hashmap.HashMap[Int, Int],
  old_key : Int,
) -> Int? {
  changes.get(old_key)
}

///|
fn pdf_structure_apply_parent_tree_changes(
  entries : ArrayView[(Int, @syntax.PdfObject)],
  changes : ArrayView[(Int, Int)],
) -> Array[(Int, @syntax.PdfObject)] {
  let change_map = pdf_merge_parent_tree_change_map(changes)
  let output : Array[(Int, @syntax.PdfObject)] = Array(
    capacity=entries.length(),
  )
  for entry in entries {
    let (old_key, value) = entry
    match pdf_structure_changed_parent_tree_key(change_map, old_key) {
      Some(new_key) =>
        output.push(
          (new_key, pdf_merge_renumber_struct_parent_object(value, change_map)),
        )
      None => ()
    }
  }
  output
}

///|
fn PdfDocument::pdf_structure_replace_parent_tree(
  self : PdfDocument,
  parent_tree : @syntax.PdfObject,
) -> Unit raise @core.PdfError {
  match self.trailer_dict().lookup_immediate(pdf_structure_root_key()) {
    Some(root) =>
      if self.direct(root) != PdfNull {
        self.replace_chain(
          [
            pdf_structure_root_key(),
            pdf_structure_struct_tree_root_key(),
            pdf_structure_parent_tree_key(),
          ],
          parent_tree,
        )
      }
    None => {
      let catalog = self.lookup_object_or_null(self.root())
      match catalog.lookup_immediate(pdf_structure_struct_tree_root_key()) {
        Some(PdfIndirect(root_number)) => {
          let root = self.lookup_object_or_null(root_number)
          self.add_object_given_number(
            root_number,
            root.add_dict_entry(pdf_structure_parent_tree_key(), parent_tree),
          )
        }
        Some(root) =>
          self.add_object_given_number(
            self.root(),
            catalog.add_dict_entry(
              pdf_structure_struct_tree_root_key(),
              root.add_dict_entry(pdf_structure_parent_tree_key(), parent_tree),
            ),
          )
        None => ()
      }
    }
  }
}

///|
/// Renumber structure parent-tree keys across documents before merging.
///
/// The documents are mutated in place. Parent-tree keys are reassigned to a
/// single increasing sequence, and structure parent references inside each
/// document are updated to match the rebuilt number trees.
pub fn pdf_renumber_parent_trees(
  documents : ArrayView[PdfDocument],
) -> Unit raise @core.PdfError {
  if documents.length() > 1 {
    let all_changes : Array[Array[(Int, Int)]] = Array(
      capacity=documents.length(),
    )
    let all_entries : Array[Array[(Int, @syntax.PdfObject)]] = Array(
      capacity=documents.length(),
    )
    let mut next_key = 0
    for document in documents {
      let entries = document.pdf_structure_root_parent_tree_entries()
      let changes : Array[(Int, Int)] = Array(capacity=entries.length())
      for entry in entries {
        let (old_key, _) = entry
        changes.push((old_key, next_key))
        next_key += 1
      }
      all_entries.push(entries)
      all_changes.push(changes)
    }
    for index in 0.. 0 {
        document.pdf_merge_renumber_struct_parent_entries(changes)
        document.pdf_structure_replace_parent_tree(
          document.build_number_tree(
            pdf_structure_apply_parent_tree_changes(all_entries[index], changes),
          ),
        )
      }
    }
  }
}