///|
fn pdf_page_label_continuation(
  labels : ArrayView[@label.PdfPageLabel],
  start_page : Int,
) -> @label.PdfPageLabel? {
  if labels.length() == 0 {
    None
  } else {
    let label = labels[labels.length() - 1]
    Some({
      ..label,
      start_page,
      start_value: start_page + (label.start_value - label.start_page),
    })
  }
}

///|
/// Insert or replace a label range over a page interval.
///
/// Existing ranges after `range_end_page` are preserved, and a continuation
/// range is inserted when needed so later labels keep their previous numbering.
pub fn pdf_page_label_add_label(
  end_page : Int,
  labels : ArrayView[@label.PdfPageLabel],
  label : @label.PdfPageLabel,
  range_end_page : Int,
) -> Array[@label.PdfPageLabel] {
  let before_or_during_or_equal : Array[@label.PdfPageLabel] = Array(
    capacity=labels.length(),
  )
  let after : Array[@label.PdfPageLabel] = Array(capacity=labels.length())
  for existing in labels {
    if existing.start_page <= range_end_page {
      before_or_during_or_equal.push(existing)
    } else {
      after.push(existing)
    }
  }
  let before_or_during : Array[@label.PdfPageLabel] = Array(
    capacity=before_or_during_or_equal.length(),
  )
  for existing in before_or_during_or_equal {
    if existing.start_page != label.start_page {
      before_or_during.push(existing)
    }
  }
  let before : Array[@label.PdfPageLabel] = Array(
    capacity=before_or_during.length() + 1,
  )
  for existing in before_or_during {
    if !(existing.start_page > label.start_page &&
      existing.start_page <= range_end_page) {
      before.push(existing)
    }
  }
  let output = before
  output.push(label)
  if range_end_page != end_page &&
    !(after.length() > 0 && after[0].start_page == range_end_page + 1) {
    match
      pdf_page_label_continuation(before_or_during_or_equal, range_end_page + 1) {
      Some(replica) => output.push(replica)
      None => ()
    }
  }
  for existing in after {
    output.push(existing)
  }
  output
}

///|
fn pdf_page_label_range_extents(
  page_numbers : ArrayView[Int],
) -> Array[(Int, Int)] {
  let ranges : Array[(Int, Int)] = Array(capacity=page_numbers.length())
  if page_numbers.length() > 0 {
    let mut start = page_numbers[0]
    let mut previous = page_numbers[0]
    for index in 1.. Bool {
  for label in labels {
    if label.start_page == 1 {
      break true
    }
  } nobreak {
    false
  }
}

///|
fn pdf_page_label_start_additions(ranges : ArrayView[(Int, Int)]) -> Array[Int] {
  let additions : Array[Int] = Array(capacity=ranges.length())
  let mut so_far = 0
  for range in ranges {
    let (start_page, end_page) = range
    additions.push(so_far)
    so_far += end_page - start_page + 1
  }
  additions
}

///|
fn pdf_page_label_with_basic_if_needed(
  labels : ArrayView[@label.PdfPageLabel],
) -> Array[@label.PdfPageLabel] {
  let output : Array[@label.PdfPageLabel] = Array(capacity=labels.length() + 1)
  if !pdf_page_label_has_page_one(labels) {
    output.push(@label.pdf_page_label_basic)
  }
  for label in labels {
    output.push(label)
  }
  output
}

///|
/// Add page-label ranges to the selected one-based page numbers.
///
/// `page_numbers` is interpreted in its existing order; contiguous runs are
/// labelled as ranges. When `progress` is true, each later range starts after
/// the number of pages already labelled by earlier ranges.
pub fn PdfDocument::add_page_labels(
  self : PdfDocument,
  progress : Bool,
  style : @label.PdfPageLabelStyle,
  prefix : @core.PdfBytes?,
  start_value : Int,
  page_numbers : ArrayView[Int],
) -> PdfDocument raise @core.PdfError {
  let ranges = pdf_page_label_range_extents(page_numbers)
  if ranges.length() == 0 {
    raise BadPageSpecification("empty page label range")
  }
  let additions = pdf_page_label_start_additions(ranges)
  let end_page = self.endpage()
  let mut labels = pdf_page_label_with_basic_if_needed(self.read_page_labels())
  for index in 0.. PdfDocument raise @core.PdfError {
  document.add_page_labels(progress, style, prefix, start_value, page_numbers)
}