///|
fn PdfDocument::pdf_page_rotate_rewrite(
  self : PdfDocument,
  page_numbers : ArrayView[Int],
  rotate : (@page.PdfPage) -> @page.PdfPageRotation raise @core.PdfError,
  label : String,
) -> PdfDocument raise @core.PdfError {
  pdf_page_box_validate_page_numbers(page_numbers, self.endpage(), label)
  let pages = self.pages_of_pagetree()
  let selected_pages = @core.pdf_number_set(page_numbers)
  let rewritten : Array[@page.PdfPage] = Array(capacity=pages.length())
  for index in 0.. Double {
  rotation * @math.PI / 180.0
}

///|
fn PdfDocument::pdf_page_rotate_contents_matrix(
  self : PdfDocument,
  page : @page.PdfPage,
  rotation : Double,
) -> @geometry.TransformMatrix raise @core.PdfError {
  let bounds = self.parse_rectangle(page.mediabox)
  let origin = @geometry.point2(
    (bounds.min_x + bounds.max_x) / 2.0,
    (bounds.min_y + bounds.max_y) / 2.0,
  )
  @geometry.transform_rotate(
    origin,
    -pdf_page_rotate_degrees_to_radians(rotation),
  )
}

///|
fn PdfDocument::pdf_page_rotate_contents_page(
  self : PdfDocument,
  page : @page.PdfPage,
  matrix : @geometry.TransformMatrix,
  fast : Bool,
) -> @page.PdfPage raise @core.PdfError {
  let page = self.pdf_impose_change_pattern_matrices_page(matrix, page)
  self.transform_annotations(matrix, page.rest)
  self.prepend_operators(page, [Op_cm(matrix)], fast~)
}

///|
/// Set selected page `/Rotate` entries to an absolute rotation in degrees.
pub fn PdfDocument::rotate_pages(
  self : PdfDocument,
  rotation : Int,
  page_numbers : ArrayView[Int],
) -> PdfDocument raise @core.PdfError {
  let normalized = @page.pdf_page_rotation_of_int(rotation)
  self.pdf_page_rotate_rewrite(
    page_numbers,
    fn(_) { normalized },
    "rotate_pages",
  )
}

///|
/// Compatibility method using cpdfpage's `rotate_pdf` name.
pub fn PdfDocument::rotate_pdf(
  self : PdfDocument,
  rotation : Int,
  page_numbers : ArrayView[Int],
) -> PdfDocument raise @core.PdfError {
  self.rotate_pages(rotation, page_numbers)
}

///|
/// Add `rotation` degrees to selected page `/Rotate` entries.
pub fn PdfDocument::rotate_pages_by(
  self : PdfDocument,
  rotation : Int,
  page_numbers : ArrayView[Int],
) -> PdfDocument raise @core.PdfError {
  self.pdf_page_rotate_rewrite(
    page_numbers,
    fn(page) raise @core.PdfError {
      @page.pdf_page_rotation_of_int(
        @page.pdf_page_int_of_rotation(page.rotate) + rotation,
      )
    },
    "rotate_pages_by",
  )
}

///|
/// Compatibility method using cpdfpage's `rotate_pdf_by` name.
pub fn PdfDocument::rotate_pdf_by(
  self : PdfDocument,
  rotation : Int,
  page_numbers : ArrayView[Int],
) -> PdfDocument raise @core.PdfError {
  self.rotate_pages_by(rotation, page_numbers)
}

///|
/// Rotate selected page contents around each page's `/MediaBox` center.
///
/// This ports cpdfpage's `rotate_contents`: page boxes and `/Rotate` entries are
/// left unchanged, while content streams, annotations, pattern matrices, and
/// destination coordinates are transformed.
pub fn PdfDocument::rotate_contents(
  self : PdfDocument,
  rotation : Double,
  page_numbers : ArrayView[Int],
  fast? : Bool = false,
) -> PdfDocument raise @core.PdfError {
  pdf_page_box_validate_page_numbers(
    page_numbers,
    self.endpage(),
    "rotate_contents",
  )
  let pages = self.pages_of_pagetree()
  let selected_pages = @core.pdf_number_set(page_numbers)
  let rewritten : Array[@page.PdfPage] = Array(capacity=pages.length())
  let matrices : Array[(Int, @geometry.TransformMatrix)] = Array(
    capacity=page_numbers.length(),
  )
  for index in 0.. PdfDocument raise @core.PdfError {
  document.rotate_pages(rotation, page_numbers)
}

///|
/// Compatibility wrapper matching cpdfpage's `rotate_pdf` argument order.
pub fn pdf_rotate_pdf(
  rotation : Int,
  document : PdfDocument,
  page_numbers : ArrayView[Int],
) -> PdfDocument raise @core.PdfError {
  document.rotate_pdf(rotation, page_numbers)
}

///|
/// Compatibility wrapper for `PdfDocument::rotate_pages_by`.
pub fn pdf_rotate_pages_by(
  document : PdfDocument,
  rotation : Int,
  page_numbers : ArrayView[Int],
) -> PdfDocument raise @core.PdfError {
  document.rotate_pages_by(rotation, page_numbers)
}

///|
/// Compatibility wrapper matching cpdfpage's `rotate_pdf_by` argument order.
pub fn pdf_rotate_pdf_by(
  rotation : Int,
  document : PdfDocument,
  page_numbers : ArrayView[Int],
) -> PdfDocument raise @core.PdfError {
  document.rotate_pdf_by(rotation, page_numbers)
}

///|
/// Compatibility wrapper for `PdfDocument::rotate_contents`.
pub fn pdf_rotate_contents(
  document : PdfDocument,
  rotation : Double,
  page_numbers : ArrayView[Int],
  fast? : Bool = false,
) -> PdfDocument raise @core.PdfError {
  document.rotate_contents(rotation, page_numbers, fast~)
}

///|
/// Compatibility wrapper preserving cpdfpage's `rotate_contents` argument
/// order without changing the existing document-first `pdf_rotate_contents`.
pub fn pdf_rotate_contents_cpdf_order(
  rotation : Double,
  document : PdfDocument,
  page_numbers : ArrayView[Int],
  fast? : Bool = false,
) -> PdfDocument raise @core.PdfError {
  document.rotate_contents(rotation, page_numbers, fast~)
}