///|
fn PdfDocument::pdf_page_content_references(
  self : PdfDocument,
  content : ArrayView[@syntax.PdfObject],
) -> Array[@syntax.PdfObject] {
  let refs : Array[@syntax.PdfObject] = Array(capacity=content.length())
  for object in content {
    match object {
      PdfIndirect(_) => refs.push(object)
      _ => refs.push(PdfIndirect(self.add_object(object)))
    }
  }
  refs
}

///|
fn PdfDocument::pdf_page_object(
  self : PdfDocument,
  page : @page.PdfPage,
  parent : Int,
) -> @syntax.PdfObject raise @core.PdfError {
  let rest_entries = pdf_page_rest_entries(page.rest)
  let entries : Array[(@core.PdfName, @syntax.PdfObject)] = Array(
    capacity=6 + rest_entries.length(),
  )
  entries.push((pdf_page_type_key(), PdfNameObject(pdf_page_type_page())))
  entries.push((pdf_page_parent_key(), PdfIndirect(parent)))
  entries.push((pdf_page_resources_key(), page.resources))
  entries.push((pdf_page_mediabox_key(), page.mediabox))
  match @page.pdf_page_int_of_rotation(page.rotate) {
    0 => ()
    value => entries.push((pdf_page_rotate_key(), PdfInteger(value)))
  }
  let content_refs = self.pdf_page_content_references(page.content)
  if content_refs.length() > 0 {
    entries.push((pdf_page_contents_key(), PdfArray(content_refs)))
  }
  for entry in rest_entries {
    entries.push(entry)
  }
  PdfDictionary(entries)
}

///|
fn pdf_page_slice(
  pages : ArrayView[@page.PdfPage],
  start : Int,
  stop : Int,
) -> Array[@page.PdfPage] {
  let output : Array[@page.PdfPage] = Array(capacity=stop - start)
  for i in start.. @syntax.PdfObject {
  let kid_refs : Array[@syntax.PdfObject] = Array(capacity=kids.length())
  for number in kids {
    kid_refs.push(PdfIndirect(number))
  }
  let entries : Array[(@core.PdfName, @syntax.PdfObject)] = Array(
    capacity=if parent == 0 { 3 } else { 4 },
  )
  entries.push((pdf_page_type_key(), PdfNameObject(pdf_page_type_pages())))
  entries.push((pdf_page_kids_key(), PdfArray(kid_refs)))
  entries.push((pdf_page_count_key(), PdfInteger(count)))
  if parent != 0 {
    entries.push((pdf_page_parent_key(), PdfIndirect(parent)))
  }
  PdfDictionary(entries)
}

///|
fn PdfDocument::pdf_page_add_pagetree_node(
  self : PdfDocument,
  pages : ArrayView[@page.PdfPage],
  parent : Int,
  node_number : Int,
) -> Unit raise @core.PdfError {
  if pages.length() < 10 {
    let page_numbers : Array[Int] = Array(capacity=pages.length())
    for page in pages {
      page_numbers.push(
        self.add_object(self.pdf_page_object(page, node_number)),
      )
    }
    self.add_object_given_number(
      node_number,
      pdf_page_pages_node_object(page_numbers, pages.length(), parent),
    )
  } else {
    let middle_count = 5
    let left_count = (pages.length() - middle_count) / 2
    let middle_end = left_count + middle_count
    let left_number = self.add_object(PdfNull)
    self.pdf_page_add_pagetree_node(
      pdf_page_slice(pages, 0, left_count),
      node_number,
      left_number,
    )
    let kid_numbers : Array[Int] = Array(capacity=middle_count + 2)
    kid_numbers.push(left_number)
    for i in left_count.. Int raise @core.PdfError {
  let page_root_number = self.add_object(PdfNull)
  self.pdf_page_add_pagetree_node(pages, 0, page_root_number)
  page_root_number
}

///|
/// Copy a document, add a page tree, and return the copy with the tree root.
pub fn pdf_add_pagetree(
  pages : ArrayView[@page.PdfPage],
  document : PdfDocument,
) -> (PdfDocument, Int) raise @core.PdfError {
  let pdf = document.copy()
  let page_root_number = pdf.add_pagetree(pages)
  (pdf, page_root_number)
}