///|
pub struct PixelBuffer {
  data : Bytes
  width : Int
  height : Int
  row_bytes : Int
}

///|
pub fn PixelBuffer::PixelBuffer(
  width~ : Int,
  height~ : Int,
) -> PixelBuffer raise SkiaError {
  if width <= 0 || height <= 0 {
    raise InvalidPixelBufferSize(width~, height~)
  }
  if width > 0x7fff_ffff / 4 {
    raise InvalidPixelBufferSize(width~, height~)
  }
  let row_bytes = width * 4
  if height > 0x7fff_ffff / row_bytes {
    raise InvalidPixelBufferSize(width~, height~)
  }
  { data: Bytes::new(row_bytes * height), width, height, row_bytes }
}

///|
pub struct Surface {
  priv raw : @sys.Surface
  priv width : Int
  priv height : Int
}

///|
pub fn Surface::Surface(width~ : Int, height~ : Int) -> Surface raise SkiaError {
  if width <= 0 || height <= 0 || width > 0x7fff_ffff / 4 {
    raise InvalidSurfaceSize(width~, height~)
  }
  let row_bytes = width * 4
  let info = @sys.imageinfo_bgra8888_premul(width, height)
  let props = @sys.surfaceprops_new(0, 0)
  let raw = @sys.surface_new_raster(info, row_bytes.to_uint64(), props)
  @sys.surfaceprops_delete(props)
  if raw.is_null() {
    raise SurfaceCreateFailed(width~, height~)
  }
  { raw, width, height }
}

///|
fn Surface::to_sys(self : Self) -> @sys.Surface {
  self.raw
}

///|
pub fn Surface::canvas(self : Self) -> Canvas {
  Canvas::from_sys(@sys.surface_get_canvas(self.to_sys()))
}

///|
pub fn Surface::width(self : Self) -> Int {
  self.width
}

///|
pub fn Surface::height(self : Self) -> Int {
  self.height
}

///|
pub fn Surface::row_bytes(self : Self) -> Int {
  self.width * 4
}

///|
pub fn Surface::read_pixels(self : Self) -> PixelBuffer raise SkiaError {
  let buffer = PixelBuffer(width=self.width, height=self.height)
  self.read_pixels_into(buffer)
  buffer
}

///|
pub fn Surface::read_pixels_into(
  self : Self,
  buffer : PixelBuffer,
) -> Unit raise SkiaError {
  if buffer.width != self.width ||
    buffer.height != self.height ||
    buffer.row_bytes < self.row_bytes() ||
    buffer.data.length() / buffer.row_bytes < buffer.height {
    raise InvalidPixelBufferSize(width=buffer.width, height=buffer.height)
  }
  let info = @sys.imageinfo_bgra8888_premul(self.width, self.height)
  if !@sys.surface_read_pixels(
      self.raw,
      info,
      buffer.data,
      buffer.row_bytes.to_uint64(),
      0,
      0,
    ) {
    raise SurfaceReadPixelsFailed
  }
}

///|
pub fn Surface::write_png(self : Self, path : Bytes) -> Unit raise SkiaError {
  let status = @sys.skia_write_surface_png(self.to_sys(), path)
  if status != 0 {
    raise WritePngFailed(status~)
  }
}

///|
pub fn Surface::dispose(self : Self) -> Unit {
  @sys.surface_unref(self.raw)
}