///|
/// Error returned when presenting RGBA pixels to a surface fails.
pub(all) enum PresentError {
  InvalidDimensions(Int, Int)
  SizeMismatch(Int, Int, Int, Int)
  RectOutOfBounds(Int, Int, Int, Int, Int, Int)
  InvalidRowBytes(Int, Int)
  PixelBufferTooSmall(Int, Int)
} derive(Eq, Debug)

///|
pub extend PresentError with Debug::{to_repr}

///|
pub extend PresentError with Eq::{not_equal, equal}

///|
/// Validated geometry for a rectangle present call. Backends can use this to
/// share row-stride and buffer-size checks with the reference MemorySurface.
pub(all) struct PresentRectInfo {
  required_row_bytes : Int
  required_len : Int
} derive(Eq, Debug)

///|
pub extend PresentRectInfo with Debug::{to_repr}

///|
pub extend PresentRectInfo with Eq::{not_equal, equal}

///|
/// Packed source pixels for one rectangle present operation.
pub struct PresentRectPayload {
  rect : PixelRect
  row_bytes : Int
  pixels : FixedArray[Byte]
} derive(Debug)

///|
pub extend PresentRectPayload with Debug::{to_repr}

///|
/// A materialized batch of packed rectangle present payloads.
pub struct PresentBatch {
  payloads : Array[PresentRectPayload]
  present_pixel_count : Int
  present_byte_count : Int
} derive(Debug)

///|
pub extend PresentBatch with Debug::{to_repr}

///|
/// Validated dry-run plan for submitting a materialized present batch to a
/// concrete surface.
pub struct PresentBatchSubmitPlan {
  batch : PresentBatch
  surface_width : Int
  surface_height : Int
} derive(Debug)

///|
pub extend PresentBatchSubmitPlan with Debug::{to_repr}

///|
/// Result of submitting a materialized present batch to a concrete surface.
pub struct PresentBatchSubmitResult {
  submit_plan : PresentBatchSubmitPlan
  presented_rect_count : Int
} derive(Debug)

///|
pub extend PresentBatchSubmitResult with Debug::{to_repr}

///|
/// One validated present operation recorded by MemorySurface.
pub struct MemoryPresentRecord {
  rect : PixelRect
  row_bytes : Int
} derive(Eq, Debug)

///|
pub extend MemoryPresentRecord with Debug::{to_repr}

///|
pub extend MemoryPresentRecord with Eq::{not_equal, equal}

///|
pub fn PresentRectInfo::required_row_bytes(self : PresentRectInfo) -> Int {
  self.required_row_bytes
}

///|
pub fn PresentRectInfo::required_len(self : PresentRectInfo) -> Int {
  self.required_len
}

///|
/// Build a validated rectangle present payload from caller-provided RGBA rows.
pub fn PresentRectPayload::new(
  rect : PixelRect,
  row_bytes : Int,
  pixels : FixedArray[Byte],
) -> Result[PresentRectPayload, PresentError] {
  match
    validate_present_rect(
      rect.x + rect.width,
      rect.y + rect.height,
      rect.x,
      rect.y,
      rect.width,
      rect.height,
      row_bytes,
      pixels,
    ) {
    Ok(_) => Ok({ rect, row_bytes, pixels: copy_byte_pixels(pixels), })
    Err(error) => Err(error)
  }
}

///|
pub fn PresentRectPayload::get_rect(self : PresentRectPayload) -> PixelRect {
  self.rect
}

///|
pub fn PresentRectPayload::get_row_bytes(self : PresentRectPayload) -> Int {
  self.row_bytes
}

///|
pub fn PresentRectPayload::get_pixels(
  self : PresentRectPayload,
) -> FixedArray[Byte] {
  copy_byte_pixels(self.pixels)
}

///|
pub fn PresentRectPayload::get_present_pixel_count(
  self : PresentRectPayload,
) -> Int {
  self.rect.area()
}

///|
pub fn PresentRectPayload::get_present_byte_count(
  self : PresentRectPayload,
) -> Int {
  self.get_present_pixel_count() * 4
}

///|
pub fn PresentRectPayload::get_source_byte_count(
  self : PresentRectPayload,
) -> Int {
  self.row_bytes * self.rect.height
}

///|
pub fn PresentRectPayload::get_source_padding_byte_count(
  self : PresentRectPayload,
) -> Int {
  (self.get_source_byte_count() - self.get_present_byte_count()).max(0)
}

///|
pub fn[S : Surface] PresentRectPayload::submit_to(
  self : PresentRectPayload,
  surface : S,
) -> Result[Unit, PresentError] {
  surface.present_pixels_rect(
    self.rect.x,
    self.rect.y,
    self.rect.width,
    self.rect.height,
    self.row_bytes,
    copy_byte_pixels(self.pixels),
  )
}

///|
pub fn PresentBatch::get_payloads(
  self : PresentBatch,
) -> Array[PresentRectPayload] {
  copy_present_payloads(self.payloads)
}

///|
pub fn PresentBatch::get_rect_count(self : PresentBatch) -> Int {
  self.payloads.length()
}

///|
pub fn PresentBatch::get_present_bounds(self : PresentBatch) -> PixelRect? {
  let rects = []
  for payload in self.payloads {
    rects.push(payload.get_rect())
  }
  pixel_rect_array_bounds(rects)
}

///|
pub fn PresentBatch::is_empty(self : PresentBatch) -> Bool {
  self.payloads.length() == 0
}

///|
pub fn PresentBatch::get_present_pixel_count(self : PresentBatch) -> Int {
  self.present_pixel_count
}

///|
pub fn PresentBatch::get_present_byte_count(self : PresentBatch) -> Int {
  self.present_byte_count
}

///|
pub fn PresentBatch::saves_full_present_pixels(
  self : PresentBatch,
  width : Int,
  height : Int,
) -> Bool {
  self.get_full_present_pixel_savings(width, height) > 0
}

///|
/// Return how many pixels this batch avoids compared with presenting the full
/// target surface. Empty batches report no savings because they represent skip,
/// not a partial present.
pub fn PresentBatch::get_full_present_pixel_savings(
  self : PresentBatch,
  width : Int,
  height : Int,
) -> Int {
  guard !self.is_empty() else { return 0 }
  let full_pixels = PixelRect::new(x=0, y=0, width~, height~).area()
  guard full_pixels > 0 else { return 0 }
  (full_pixels - self.present_pixel_count).max(0)
}

///|
/// Return how many packed RGBA bytes this batch avoids compared with presenting
/// the full target surface.
pub fn PresentBatch::get_full_present_byte_savings(
  self : PresentBatch,
  width : Int,
  height : Int,
) -> Int {
  self.get_full_present_pixel_savings(width, height) * 4
}

///|
pub fn PresentBatch::get_present_strategy(
  self : PresentBatch,
  width : Int,
  height : Int,
) -> DirtyPresentStrategy {
  guard !self.is_empty() else { return Skip }
  if self.saves_full_present_pixels(width, height) {
    Partial
  } else {
    Full
  }
}

///|
pub fn PresentBatch::get_source_byte_count(self : PresentBatch) -> Int {
  let mut bytes = 0
  for payload in self.payloads {
    bytes = bytes + payload.get_source_byte_count()
  }
  bytes
}

///|
pub fn PresentBatch::get_source_padding_byte_count(self : PresentBatch) -> Int {
  let mut bytes = 0
  for payload in self.payloads {
    bytes = bytes + payload.get_source_padding_byte_count()
  }
  bytes
}

///|
pub fn PresentBatchSubmitPlan::get_batch(
  self : PresentBatchSubmitPlan,
) -> PresentBatch {
  copy_present_batch(self.batch)
}

///|
pub fn PresentBatchSubmitPlan::get_surface_width(
  self : PresentBatchSubmitPlan,
) -> Int {
  self.surface_width
}

///|
pub fn PresentBatchSubmitPlan::get_surface_height(
  self : PresentBatchSubmitPlan,
) -> Int {
  self.surface_height
}

///|
pub fn PresentBatchSubmitPlan::get_present_strategy(
  self : PresentBatchSubmitPlan,
) -> DirtyPresentStrategy {
  self.batch.get_present_strategy(self.surface_width, self.surface_height)
}

///|
pub fn PresentBatchSubmitPlan::get_rect_count(
  self : PresentBatchSubmitPlan,
) -> Int {
  self.batch.get_rect_count()
}

///|
pub fn PresentBatchSubmitPlan::needs_present(
  self : PresentBatchSubmitPlan,
) -> Bool {
  !self.batch.is_empty()
}

///|
pub fn PresentBatchSubmitPlan::get_present_bounds(
  self : PresentBatchSubmitPlan,
) -> PixelRect? {
  self.batch.get_present_bounds()
}

///|
pub fn PresentBatchSubmitPlan::get_present_pixel_count(
  self : PresentBatchSubmitPlan,
) -> Int {
  self.batch.get_present_pixel_count()
}

///|
pub fn PresentBatchSubmitPlan::get_present_byte_count(
  self : PresentBatchSubmitPlan,
) -> Int {
  self.batch.get_present_byte_count()
}

///|
pub fn PresentBatchSubmitPlan::get_source_byte_count(
  self : PresentBatchSubmitPlan,
) -> Int {
  self.batch.get_source_byte_count()
}

///|
pub fn PresentBatchSubmitPlan::get_source_padding_byte_count(
  self : PresentBatchSubmitPlan,
) -> Int {
  self.batch.get_source_padding_byte_count()
}

///|
pub fn PresentBatchSubmitPlan::saves_full_present_pixels(
  self : PresentBatchSubmitPlan,
) -> Bool {
  self.batch.saves_full_present_pixels(self.surface_width, self.surface_height)
}

///|
pub fn PresentBatchSubmitPlan::get_full_present_pixel_savings(
  self : PresentBatchSubmitPlan,
) -> Int {
  self.batch.get_full_present_pixel_savings(
    self.surface_width,
    self.surface_height,
  )
}

///|
pub fn PresentBatchSubmitPlan::get_full_present_byte_savings(
  self : PresentBatchSubmitPlan,
) -> Int {
  self.batch.get_full_present_byte_savings(
    self.surface_width,
    self.surface_height,
  )
}

///|
pub fn PresentBatchSubmitResult::get_submit_plan(
  self : PresentBatchSubmitResult,
) -> PresentBatchSubmitPlan {
  copy_present_batch_submit_plan(self.submit_plan)
}

///|
pub fn PresentBatchSubmitResult::get_batch(
  self : PresentBatchSubmitResult,
) -> PresentBatch {
  self.submit_plan.get_batch()
}

///|
fn copy_byte_pixels(src : FixedArray[Byte]) -> FixedArray[Byte] {
  let dst : FixedArray[Byte] = FixedArray::make(src.length(), 0)
  for i in 0.. PresentRectPayload {
  {
    rect: payload.rect,
    row_bytes: payload.row_bytes,
    pixels: copy_byte_pixels(payload.pixels),
  }
}

///|
fn copy_present_payloads(
  payloads : Array[PresentRectPayload],
) -> Array[PresentRectPayload] {
  let copied = []
  for payload in payloads {
    copied.push(copy_present_payload(payload))
  }
  copied
}

///|
fn copy_present_batch(batch : PresentBatch) -> PresentBatch {
  {
    payloads: copy_present_payloads(batch.payloads),
    present_pixel_count: batch.present_pixel_count,
    present_byte_count: batch.present_byte_count,
  }
}

///|
fn copy_present_batch_submit_plan(
  plan : PresentBatchSubmitPlan,
) -> PresentBatchSubmitPlan {
  {
    batch: copy_present_batch(plan.batch),
    surface_width: plan.surface_width,
    surface_height: plan.surface_height,
  }
}

///|
pub fn PresentBatchSubmitResult::get_surface_width(
  self : PresentBatchSubmitResult,
) -> Int {
  self.submit_plan.get_surface_width()
}

///|
pub fn PresentBatchSubmitResult::get_surface_height(
  self : PresentBatchSubmitResult,
) -> Int {
  self.submit_plan.get_surface_height()
}

///|
pub fn PresentBatchSubmitResult::get_present_strategy(
  self : PresentBatchSubmitResult,
) -> DirtyPresentStrategy {
  self.submit_plan.get_present_strategy()
}

///|
pub fn PresentBatchSubmitResult::get_rect_count(
  self : PresentBatchSubmitResult,
) -> Int {
  self.submit_plan.get_rect_count()
}

///|
pub fn PresentBatchSubmitResult::get_presented_rect_count(
  self : PresentBatchSubmitResult,
) -> Int {
  self.presented_rect_count
}

///|
pub fn PresentBatchSubmitResult::did_present(
  self : PresentBatchSubmitResult,
) -> Bool {
  self.presented_rect_count > 0
}

///|
pub fn PresentBatchSubmitResult::needs_present(
  self : PresentBatchSubmitResult,
) -> Bool {
  self.submit_plan.needs_present()
}

///|
pub fn PresentBatchSubmitResult::get_present_bounds(
  self : PresentBatchSubmitResult,
) -> PixelRect? {
  self.submit_plan.get_present_bounds()
}

///|
pub fn PresentBatchSubmitResult::get_present_pixel_count(
  self : PresentBatchSubmitResult,
) -> Int {
  self.submit_plan.get_present_pixel_count()
}

///|
pub fn PresentBatchSubmitResult::get_present_byte_count(
  self : PresentBatchSubmitResult,
) -> Int {
  self.submit_plan.get_present_byte_count()
}

///|
pub fn PresentBatchSubmitResult::get_source_byte_count(
  self : PresentBatchSubmitResult,
) -> Int {
  self.submit_plan.get_source_byte_count()
}

///|
pub fn PresentBatchSubmitResult::get_source_padding_byte_count(
  self : PresentBatchSubmitResult,
) -> Int {
  self.submit_plan.get_source_padding_byte_count()
}

///|
pub fn PresentBatchSubmitResult::saves_full_present_pixels(
  self : PresentBatchSubmitResult,
) -> Bool {
  self.submit_plan.saves_full_present_pixels()
}

///|
pub fn PresentBatchSubmitResult::get_full_present_pixel_savings(
  self : PresentBatchSubmitResult,
) -> Int {
  self.submit_plan.get_full_present_pixel_savings()
}

///|
pub fn PresentBatchSubmitResult::get_full_present_byte_savings(
  self : PresentBatchSubmitResult,
) -> Int {
  self.submit_plan.get_full_present_byte_savings()
}

///|
pub fn MemoryPresentRecord::get_rect(self : MemoryPresentRecord) -> PixelRect {
  self.rect
}

///|
pub fn MemoryPresentRecord::get_row_bytes(self : MemoryPresentRecord) -> Int {
  self.row_bytes
}

///|
pub fn MemoryPresentRecord::get_present_pixel_count(
  self : MemoryPresentRecord,
) -> Int {
  self.rect.area()
}

///|
pub fn MemoryPresentRecord::get_present_byte_count(
  self : MemoryPresentRecord,
) -> Int {
  self.get_present_pixel_count() * 4
}

///|
pub fn MemoryPresentRecord::get_source_byte_count(
  self : MemoryPresentRecord,
) -> Int {
  self.row_bytes * self.rect.height
}

///|
pub fn MemoryPresentRecord::get_source_padding_byte_count(
  self : MemoryPresentRecord,
) -> Int {
  (self.get_source_byte_count() - self.get_present_byte_count()).max(0)
}

///|
/// Validate every payload in this batch against a target surface before any
/// present call mutates backend state.
pub fn[S : Surface] PresentBatch::validate_for(
  self : PresentBatch,
  surface : S,
) -> Result[Unit, PresentError] {
  for payload in self.payloads {
    match
      validate_present_rect(
        surface.get_width(),
        surface.get_height(),
        payload.rect.x,
        payload.rect.y,
        payload.rect.width,
        payload.rect.height,
        payload.row_bytes,
        payload.pixels,
      ) {
      Ok(_) => ()
      Err(error) => return Err(error)
    }
  }
  Ok(())
}

///|
/// Build a validated dry-run submit plan for this packed batch without
/// presenting any payloads to the surface.
pub fn[S : Surface] PresentBatch::submit_plan_for(
  self : PresentBatch,
  surface : S,
) -> Result[PresentBatchSubmitPlan, PresentError] {
  let surface_width = surface.get_width()
  let surface_height = surface.get_height()
  match self.validate_for(surface) {
    Ok(_) => Ok({ batch: self, surface_width, surface_height, })
    Err(error) => Err(error)
  }
}

///|
pub fn[S : Surface] PresentBatch::submit_to(
  self : PresentBatch,
  surface : S,
) -> Result[Int, PresentError] {
  match self.submit_to_result(surface) {
    Ok(result) => Ok(result.get_presented_rect_count())
    Err(error) => Err(error)
  }
}

///|
pub fn[S : Surface] PresentBatch::submit_to_result(
  self : PresentBatch,
  surface : S,
) -> Result[PresentBatchSubmitResult, PresentError] {
  let submit_plan = match self.submit_plan_for(surface) {
    Ok(plan) => plan
    Err(error) => return Err(error)
  }
  submit_plan.submit_to_result(surface)
}

///|
/// Submit this prevalidated plan to a surface with the same dimensions.
pub fn[S : Surface] PresentBatchSubmitPlan::submit_to(
  self : PresentBatchSubmitPlan,
  surface : S,
) -> Result[Int, PresentError] {
  match self.submit_to_result(surface) {
    Ok(result) => Ok(result.get_presented_rect_count())
    Err(error) => Err(error)
  }
}

///|
/// Submit this prevalidated plan and return lifecycle telemetry.
///
/// The target surface must still match the dimensions captured by the dry-run
/// plan, so stale resize-era plans fail before any backend present call.
pub fn[S : Surface] PresentBatchSubmitPlan::submit_to_result(
  self : PresentBatchSubmitPlan,
  surface : S,
) -> Result[PresentBatchSubmitResult, PresentError] {
  let surface_width = surface.get_width()
  let surface_height = surface.get_height()
  guard surface_width == self.surface_width &&
    surface_height == self.surface_height else {
    return Err(
      SizeMismatch(
        self.surface_width,
        self.surface_height,
        surface_width,
        surface_height,
      ),
    )
  }
  match self.batch.validate_for(surface) {
    Ok(_) => ()
    Err(error) => return Err(error)
  }
  let mut presented = 0
  for payload in self.batch.payloads {
    match payload.submit_to(surface) {
      Ok(_) => presented = presented + 1
      Err(error) => return Err(error)
    }
  }
  Ok({ submit_plan: self, presented_rect_count: presented, })
}

///|
/// Destination for CPU-rendered RGBA pixels.
pub(open) trait Surface {
  fn get_width(Self) -> Int
  fn get_height(Self) -> Int
  fn present_pixels(
    Self,
    width : Int,
    height : Int,
    row_bytes : Int,
    pixels : FixedArray[Byte],
  ) -> Result[Unit, PresentError]
  fn present_pixels_rect(
    Self,
    x : Int,
    y : Int,
    width : Int,
    height : Int,
    row_bytes : Int,
    pixels : FixedArray[Byte],
  ) -> Result[Unit, PresentError]
}

///|
/// In-memory surface used as the reference backend for tests and headless tools.
pub struct MemorySurface {
  width : Int
  height : Int
  pixels : FixedArray[Byte]
  mut present_count : Int
  present_records : Array[MemoryPresentRecord]
} derive(Debug)

///|
pub extend MemorySurface with Debug::{to_repr}

///|
pub fn MemorySurface::new(width : Int, height : Int) -> MemorySurface {
  let safe_width = width.max(0)
  let safe_height = height.max(0)
  {
    width: safe_width,
    height: safe_height,
    pixels: FixedArray::make(safe_width * safe_height * 4, 0),
    present_count: 0,
    present_records: [],
  }
}

///|
pub fn MemorySurface::get_width(self : MemorySurface) -> Int {
  self.width
}

///|
pub fn MemorySurface::get_height(self : MemorySurface) -> Int {
  self.height
}

///|
/// Return a copied snapshot of the surface's RGBA pixels.
pub fn MemorySurface::get_pixels(self : MemorySurface) -> FixedArray[Byte] {
  copy_byte_pixels(self.pixels)
}

///|
pub fn MemorySurface::get_present_count(self : MemorySurface) -> Int {
  self.present_count
}

///|
pub fn MemorySurface::get_present_pixel_count(self : MemorySurface) -> Int {
  let mut pixels = 0
  for record in self.present_records {
    pixels = pixels + record.get_present_pixel_count()
  }
  pixels
}

///|
pub fn MemorySurface::get_present_byte_count(self : MemorySurface) -> Int {
  let mut bytes = 0
  for record in self.present_records {
    bytes = bytes + record.get_present_byte_count()
  }
  bytes
}

///|
pub fn MemorySurface::get_present_source_byte_count(
  self : MemorySurface,
) -> Int {
  let mut bytes = 0
  for record in self.present_records {
    bytes = bytes + record.get_source_byte_count()
  }
  bytes
}

///|
pub fn MemorySurface::get_present_source_padding_byte_count(
  self : MemorySurface,
) -> Int {
  let mut bytes = 0
  for record in self.present_records {
    bytes = bytes + record.get_source_padding_byte_count()
  }
  bytes
}

///|
pub fn MemorySurface::get_present_records(
  self : MemorySurface,
) -> Array[MemoryPresentRecord] {
  copy_memory_present_records(self.present_records)
}

///|
pub fn MemorySurface::get_present_bounds(self : MemorySurface) -> PixelRect? {
  let rects = []
  for record in self.present_records {
    rects.push(record.get_rect())
  }
  pixel_rect_array_bounds(rects)
}

///|
pub fn MemorySurface::get_last_present_record(
  self : MemorySurface,
) -> MemoryPresentRecord? {
  guard self.present_records.length() > 0 else { return None }
  Some(self.present_records[self.present_records.length() - 1])
}

///|
fn copy_memory_present_records(
  records : Array[MemoryPresentRecord],
) -> Array[MemoryPresentRecord] {
  let copied = []
  for record in records {
    copied.push(record)
  }
  copied
}

///|
/// Clear accumulated present telemetry while preserving surface pixels.
pub fn MemorySurface::reset_present_telemetry(self : MemorySurface) -> Unit {
  self.present_count = 0
  while self.present_records.length() > 0 {
    ignore(self.present_records.remove(self.present_records.length() - 1))
  }
}

///|
pub fn MemorySurface::get_pixel(
  self : MemorySurface,
  x : Int,
  y : Int,
) -> Color {
  guard x >= 0 && x < self.width && y >= 0 && y < self.height else {
    return Color::transparent()
  }
  let idx = (y * self.width + x) * 4
  Color::new(
    r=self.pixels[idx],
    g=self.pixels[idx + 1],
    b=self.pixels[idx + 2],
    a=self.pixels[idx + 3],
  )
}

///|
/// Snapshot this in-memory surface into an isolated pixmap.
pub fn MemorySurface::to_pixmap(self : MemorySurface) -> Pixmap {
  let pixmap = Pixmap::new(self.width, self.height)
  let dst = pixmap.get_pixels()
  for i in 0.. Bytes {
  self.to_pixmap().to_ppm_bytes()
}

///|
/// Encode the current surface pixels as deterministic 32-bit BMP(BGRA) bytes.
pub fn MemorySurface::to_bmp_bytes(self : MemorySurface) -> Bytes {
  self.to_pixmap().to_bmp_bytes()
}

///|
/// Encode the current surface pixels as deterministic 8-bit RGBA PNG bytes.
pub fn MemorySurface::to_png_bytes(
  self : MemorySurface,
) -> Result[Bytes, PixmapEncodeError] {
  self.to_pixmap().to_png_bytes()
}

///|
/// Return an isolated copy of the current surface RGBA pixel bytes.
pub fn MemorySurface::to_rgba_bytes(self : MemorySurface) -> Bytes {
  rgba_bytes_from_pixels(self.pixels)
}

///|
/// Return a deterministic lightweight checksum of the current surface pixels.
pub fn MemorySurface::checksum(self : MemorySurface) -> Int {
  checksum_rgba_bytes(self.pixels)
}

///|
/// Snapshot a rectangle of this in-memory surface into an isolated pixmap.
pub fn MemorySurface::to_pixmap_rect(
  self : MemorySurface,
  rect : PixelRect,
) -> Result[Pixmap, PresentError] {
  match validate_pixel_rect_bounds(self.width, self.height, rect) {
    Err(error) => Err(error)
    Ok(_) =>
      Ok(
        pixmap_from_packed_rgba(
          rect.width,
          rect.height,
          copy_rgba_rect(self.pixels, self.width, rect),
        ),
      )
  }
}

///|
/// Encode a rectangle of this surface as deterministic binary PPM(P6) bytes.
pub fn MemorySurface::to_ppm_rect_bytes(
  self : MemorySurface,
  rect : PixelRect,
) -> Result[Bytes, PresentError] {
  match self.to_pixmap_rect(rect) {
    Ok(pixmap) => Ok(pixmap.to_ppm_bytes())
    Err(error) => Err(error)
  }
}

///|
/// Encode a rectangle of this surface as deterministic 32-bit BMP(BGRA) bytes.
pub fn MemorySurface::to_bmp_rect_bytes(
  self : MemorySurface,
  rect : PixelRect,
) -> Result[Bytes, PresentError] {
  match self.to_pixmap_rect(rect) {
    Ok(pixmap) => Ok(pixmap.to_bmp_bytes())
    Err(error) => Err(error)
  }
}

///|
/// Encode a rectangle of this surface as deterministic 8-bit RGBA PNG bytes.
pub fn MemorySurface::to_png_rect_bytes(
  self : MemorySurface,
  rect : PixelRect,
) -> Result[Bytes, PresentError] {
  match self.to_pixmap_rect(rect) {
    Ok(pixmap) =>
      match pixmap.to_png_bytes() {
        Ok(bytes) => Ok(bytes)
        Err(_) => Err(InvalidDimensions(rect.width, rect.height))
      }
    Err(error) => Err(error)
  }
}

///|
/// Return packed RGBA bytes for a surface rectangle in row-major order.
pub fn MemorySurface::to_rgba_rect_bytes(
  self : MemorySurface,
  rect : PixelRect,
) -> Result[Bytes, PresentError] {
  match validate_pixel_rect_bounds(self.width, self.height, rect) {
    Err(error) => Err(error)
    Ok(_) =>
      Ok(rgba_bytes_from_pixels(copy_rgba_rect(self.pixels, self.width, rect)))
  }
}

///|
/// Return a deterministic lightweight checksum of a surface rectangle.
pub fn MemorySurface::checksum_rect(
  self : MemorySurface,
  rect : PixelRect,
) -> Result[Int, PresentError] {
  match validate_pixel_rect_bounds(self.width, self.height, rect) {
    Err(error) => Err(error)
    Ok(_) =>
      Ok(checksum_rgba_bytes(copy_rgba_rect(self.pixels, self.width, rect)))
  }
}

///|
pub fn MemorySurface::present_pixels(
  self : MemorySurface,
  width : Int,
  height : Int,
  row_bytes : Int,
  pixels : FixedArray[Byte],
) -> Result[Unit, PresentError] {
  guard width > 0 && height > 0 else {
    return Err(InvalidDimensions(width, height))
  }
  guard width == self.width && height == self.height else {
    return Err(SizeMismatch(self.width, self.height, width, height))
  }
  self.present_pixels_rect(0, 0, width, height, row_bytes, pixels)
}

///|
pub fn MemorySurface::present_pixels_rect(
  self : MemorySurface,
  x : Int,
  y : Int,
  width : Int,
  height : Int,
  row_bytes : Int,
  pixels : FixedArray[Byte],
) -> Result[Unit, PresentError] {
  match
    validate_present_rect(
      self.width,
      self.height,
      x,
      y,
      width,
      height,
      row_bytes,
      pixels,
    ) {
    Err(error) => return Err(error)
    Ok(info) => {
      for row in 0.. Result[PresentRectInfo, PresentError] {
  guard width > 0 && height > 0 else {
    return Err(InvalidDimensions(width, height))
  }
  guard x >= 0 &&
    y >= 0 &&
    width <= surface_width - x &&
    height <= surface_height - y else {
    return Err(
      RectOutOfBounds(x, y, width, height, surface_width, surface_height),
    )
  }
  let required_row_bytes = width * 4
  guard row_bytes >= required_row_bytes else {
    return Err(InvalidRowBytes(row_bytes, required_row_bytes))
  }
  let required_len = row_bytes * (height - 1) + required_row_bytes
  guard pixels.length() >= required_len else {
    return Err(PixelBufferTooSmall(pixels.length(), required_len))
  }
  Ok({ required_row_bytes, required_len, })
}

///|
pub impl Surface for MemorySurface with fn get_width(self) {
  self.get_width()
}

///|
pub impl Surface for MemorySurface with fn get_height(self) {
  self.get_height()
}

///|
pub impl Surface for MemorySurface with fn present_pixels(
  self,
  width,
  height,
  row_bytes,
  pixels,
) {
  self.present_pixels(width, height, row_bytes, pixels)
}

///|
pub impl Surface for MemorySurface with fn present_pixels_rect(
  self,
  x,
  y,
  width,
  height,
  row_bytes,
  pixels,
) {
  self.present_pixels_rect(x, y, width, height, row_bytes, pixels)
}

///|
pub fn[S : Surface] present_pixels(
  surface : S,
  width : Int,
  height : Int,
  row_bytes : Int,
  pixels : FixedArray[Byte],
) -> Result[Unit, PresentError] {
  surface.present_pixels(width, height, row_bytes, pixels)
}

///|
pub fn[S : Surface] present_pixels_rect(
  surface : S,
  x : Int,
  y : Int,
  width : Int,
  height : Int,
  row_bytes : Int,
  pixels : FixedArray[Byte],
) -> Result[Unit, PresentError] {
  surface.present_pixels_rect(x, y, width, height, row_bytes, pixels)
}

///|
pub fn[S : Surface] Pixmap::present_to(
  self : Pixmap,
  surface : S,
) -> Result[Unit, PresentError] {
  surface.present_pixels(
    self.get_width(),
    self.get_height(),
    self.get_width() * 4,
    self.get_pixels(),
  )
}

///|
pub fn[S : Surface] Pixmap::present_rect_to(
  self : Pixmap,
  surface : S,
  rect : PixelRect,
) -> Result[Unit, PresentError] {
  match self.present_rect_payload(rect) {
    Ok(payload) => payload.submit_to(surface)
    Err(error) => Err(error)
  }
}

///|
pub fn Pixmap::present_rect_payload(
  self : Pixmap,
  rect : PixelRect,
) -> Result[PresentRectPayload, PresentError] {
  match validate_pixmap_present_rect(self, rect) {
    Err(error) => Err(error)
    Ok(_) => {
      let row_bytes = rect.width * 4
      Ok({ rect, row_bytes, pixels: copy_pixmap_rect(self, rect), })
    }
  }
}

///|
/// Snapshot a rectangle of this pixmap into an isolated pixmap.
pub fn Pixmap::copy_rect(
  self : Pixmap,
  rect : PixelRect,
) -> Result[Pixmap, PresentError] {
  match validate_pixmap_present_rect(self, rect) {
    Err(error) => Err(error)
    Ok(_) =>
      Ok(
        pixmap_from_packed_rgba(
          rect.width,
          rect.height,
          copy_pixmap_rect(self, rect),
        ),
      )
  }
}

///|
/// Return a deterministic lightweight checksum of a pixmap rectangle.
pub fn Pixmap::checksum_rect(
  self : Pixmap,
  rect : PixelRect,
) -> Result[Int, PresentError] {
  match validate_pixmap_present_rect(self, rect) {
    Err(error) => Err(error)
    Ok(_) => Ok(checksum_rgba_bytes(copy_pixmap_rect(self, rect)))
  }
}

///|
/// Return packed RGBA bytes for a pixmap rectangle in row-major order.
pub fn Pixmap::to_rgba_rect_bytes(
  self : Pixmap,
  rect : PixelRect,
) -> Result[Bytes, PresentError] {
  match validate_pixmap_present_rect(self, rect) {
    Err(error) => Err(error)
    Ok(_) => Ok(rgba_bytes_from_pixels(copy_pixmap_rect(self, rect)))
  }
}

///|
/// Encode a rectangle of this pixmap as deterministic binary PPM(P6) bytes.
pub fn Pixmap::to_ppm_rect_bytes(
  self : Pixmap,
  rect : PixelRect,
) -> Result[Bytes, PresentError] {
  match self.copy_rect(rect) {
    Ok(pixmap) => Ok(pixmap.to_ppm_bytes())
    Err(error) => Err(error)
  }
}

///|
/// Encode a rectangle of this pixmap as deterministic 32-bit BMP(BGRA) bytes.
pub fn Pixmap::to_bmp_rect_bytes(
  self : Pixmap,
  rect : PixelRect,
) -> Result[Bytes, PresentError] {
  match self.copy_rect(rect) {
    Ok(pixmap) => Ok(pixmap.to_bmp_bytes())
    Err(error) => Err(error)
  }
}

///|
/// Encode a rectangle of this pixmap as deterministic 8-bit RGBA PNG bytes.
pub fn Pixmap::to_png_rect_bytes(
  self : Pixmap,
  rect : PixelRect,
) -> Result[Bytes, PresentError] {
  match self.copy_rect(rect) {
    Ok(pixmap) =>
      match pixmap.to_png_bytes() {
        Ok(bytes) => Ok(bytes)
        Err(_) => Err(InvalidDimensions(rect.width, rect.height))
      }
    Err(error) => Err(error)
  }
}

///|
pub fn Pixmap::present_batch(
  self : Pixmap,
  plan : DirtyPresentPlan,
) -> Result[PresentBatch, PresentError] {
  let payloads = []
  let mut present_pixel_count = 0
  let mut present_byte_count = 0
  for rect in plan.get_rects() {
    match self.present_rect_payload(rect) {
      Ok(payload) => {
        present_pixel_count = present_pixel_count +
          payload.get_present_pixel_count()
        present_byte_count = present_byte_count +
          payload.get_present_byte_count()
        payloads.push(payload)
      }
      Err(error) => return Err(error)
    }
  }
  Ok({ payloads, present_pixel_count, present_byte_count, })
}

///|
pub fn[S : Surface] Canvas::present_to(
  self : Canvas,
  surface : S,
) -> Result[Unit, PresentError] {
  self.get_pixmap().present_to(surface)
}

///|
pub fn[S : Surface] Canvas::present_dirty_to(
  self : Canvas,
  surface : S,
) -> Result[Int, PresentError] {
  self.present_dirty_to_with_limit(surface, default_dirty_present_rect_limit)
}

///|
pub fn[S : Surface] Canvas::dirty_present_plan(
  self : Canvas,
  surface : S,
  max_rects : Int,
) -> DirtyPresentPlan {
  let dirty_region = self.get_dirty_region()
  guard !dirty_region.is_empty() else { return { mode: Empty, rects: [], } }
  let surface_bounds = PixelRect::new(
    x=0,
    y=0,
    width=surface.get_width(),
    height=surface.get_height(),
  )
  let pixmap_bounds = PixelRect::new(
    x=0,
    y=0,
    width=self.get_pixmap().get_width(),
    height=self.get_pixmap().get_height(),
  )
  match surface_bounds.intersection(pixmap_bounds) {
    Some(present_bounds) => dirty_region.present_plan(present_bounds, max_rects)
    None => { mode: Empty, rects: [], }
  }
}

///|
pub fn[S : Surface] Canvas::dirty_present_batch(
  self : Canvas,
  surface : S,
  max_rects : Int,
) -> Result[PresentBatch, PresentError] {
  self.get_pixmap().present_batch(self.dirty_present_plan(surface, max_rects))
}

///|
pub fn[S : Surface] Canvas::present_dirty_to_with_limit(
  self : Canvas,
  surface : S,
  max_rects : Int,
) -> Result[Int, PresentError] {
  let dirty_region = self.get_dirty_region()
  guard !dirty_region.is_empty() else { return Ok(0) }
  let plan = self.dirty_present_plan(surface, max_rects)
  guard !plan.is_empty() else {
    self.clear_dirty_region()
    return Ok(0)
  }
  match self.present_dirty_plan_to(surface, plan) {
    Ok(presented) => {
      self.clear_dirty_region()
      Ok(presented)
    }
    Err(error) => Err(error)
  }
}

///|
fn[S : Surface] Canvas::present_dirty_plan_to(
  self : Canvas,
  surface : S,
  plan : DirtyPresentPlan,
) -> Result[Int, PresentError] {
  guard !plan.is_empty() else { return Ok(0) }
  match self.get_pixmap().present_batch(plan) {
    Ok(batch) => batch.submit_to(surface)
    Err(error) => Err(error)
  }
}

///|
let default_dirty_present_rect_limit = 32

///|
fn validate_pixmap_present_rect(
  pixmap : Pixmap,
  rect : PixelRect,
) -> Result[Unit, PresentError] {
  validate_pixel_rect_bounds(pixmap.get_width(), pixmap.get_height(), rect)
}

///|
fn validate_pixel_rect_bounds(
  width : Int,
  height : Int,
  rect : PixelRect,
) -> Result[Unit, PresentError] {
  guard !rect.is_empty() else {
    return Err(InvalidDimensions(rect.width, rect.height))
  }
  guard rect.x >= 0 &&
    rect.y >= 0 &&
    rect.width <= width - rect.x &&
    rect.height <= height - rect.y else {
    return Err(
      RectOutOfBounds(rect.x, rect.y, rect.width, rect.height, width, height),
    )
  }
  Ok(())
}

///|
fn copy_pixmap_rect(pixmap : Pixmap, rect : PixelRect) -> FixedArray[Byte] {
  copy_rgba_rect(pixmap.get_pixels(), pixmap.get_width(), rect)
}

///|
fn copy_rgba_rect(
  source : FixedArray[Byte],
  source_width : Int,
  rect : PixelRect,
) -> FixedArray[Byte] {
  let row_bytes = rect.width * 4
  let packed : FixedArray[Byte] = FixedArray::make(row_bytes * rect.height, 0)
  let source_row_bytes = source_width * 4
  for row in 0.. Pixmap {
  let pixmap = Pixmap::new(width, height)
  let dst = pixmap.get_pixels()
  for i in 0..