///|
/// Working color spaces used by internal rendering surfaces.
#warnings("-unused_constructor-unused_value")
priv enum WorkingColorSpace {
  SRGB
  LinearRGB
} derive(Eq)

///|
/// A premultiplied RGBA color with channels in the range 0...65535.
#warnings("-unused_value")
priv struct PremulColor16 {
  r : Int
  g : Int
  b : Int
  a : Int
} derive(Eq)

///|
fn transparent_premul16() -> PremulColor16 {
  { r: 0, g: 0, b: 0, a: 0 }
}

///|
fn clamp_channel16(value : Int) -> Int {
  if value < 0 {
    0
  } else if value > 65535 {
    65535
  } else {
    value
  }
}

///|
fn round_div_nonnegative(value : Int64, divisor : Int64) -> Int {
  ((value + divisor / 2L) / divisor).to_int()
}

///|
fn multiply_channel16(left : Int, right : Int) -> Int {
  round_div_nonnegative(left.to_int64() * right.to_int64(), 65535L)
}

///|
fn unit_to_channel16(value : Double) -> Int {
  let clamped = if value < 0.0 {
    0.0
  } else if value > 1.0 {
    1.0
  } else {
    value
  }
  (clamped * 65535.0 + 0.5).floor().to_int()
}

///|
fn channel16_to_byte(value : Int) -> Int {
  round_div_nonnegative(clamp_channel16(value).to_int64(), 257L)
}

///|
#warnings("-unused_value")
fn PremulColor16::from_color(color : Color) -> PremulColor16 {
  let alpha = clamp_channel16(color.a * 257)
  {
    r: multiply_channel16(clamp_channel16(color.r * 257), alpha),
    g: multiply_channel16(clamp_channel16(color.g * 257), alpha),
    b: multiply_channel16(clamp_channel16(color.b * 257), alpha),
    a: alpha,
  }
}

///|
#warnings("-unused_value")
fn PremulColor16::to_color(self : PremulColor16) -> Color {
  if self.a <= 0 {
    return Color::transparent()
  }
  let alpha = clamp_channel16(self.a)
  let unpremultiply = fn(channel : Int) {
    let straight = round_div_nonnegative(
      clamp_channel16(channel).to_int64() * 65535L,
      alpha.to_int64(),
    )
    channel16_to_byte(straight)
  }
  Color::rgba(
    unpremultiply(self.r),
    unpremultiply(self.g),
    unpremultiply(self.b),
    channel16_to_byte(alpha),
  )
}

///|
#warnings("-unused_value")
fn PremulColor16::scaled(self : PremulColor16, factor : Int) -> PremulColor16 {
  let factor = clamp_channel16(factor)
  {
    r: multiply_channel16(self.r, factor),
    g: multiply_channel16(self.g, factor),
    b: multiply_channel16(self.b, factor),
    a: multiply_channel16(self.a, factor),
  }
}

///|
fn PremulColor16::source_over(
  self : PremulColor16,
  backdrop : PremulColor16,
) -> PremulColor16 {
  let inverse_alpha = 65535 - clamp_channel16(self.a)
  {
    r: clamp_channel16(self.r + multiply_channel16(backdrop.r, inverse_alpha)),
    g: clamp_channel16(self.g + multiply_channel16(backdrop.g, inverse_alpha)),
    b: clamp_channel16(self.b + multiply_channel16(backdrop.b, inverse_alpha)),
    a: clamp_channel16(self.a + multiply_channel16(backdrop.a, inverse_alpha)),
  }
}

///|
fn srgb_to_linear_unit(value : Double) -> Double {
  if value <= 0.04045 {
    value / 12.92
  } else {
    @math.pow((value + 0.055) / 1.055, 2.4)
  }
}

///|
fn linear_to_srgb_unit(value : Double) -> Double {
  if value <= 0.0031308 {
    value * 12.92
  } else {
    1.055 * @math.pow(value, 1.0 / 2.4) - 0.055
  }
}

///|
fn PremulColor16::convert_space(
  self : PremulColor16,
  from : WorkingColorSpace,
  to : WorkingColorSpace,
) -> PremulColor16 {
  if from == to || self.a <= 0 {
    return self
  }
  let alpha = clamp_channel16(self.a)
  let convert = fn(channel : Int) {
    let straight = clamp_channel16(channel).to_double() / alpha.to_double()
    let converted = match (from, to) {
      (SRGB, LinearRGB) => srgb_to_linear_unit(straight)
      (LinearRGB, SRGB) => linear_to_srgb_unit(straight)
      _ => straight
    }
    multiply_channel16(unit_to_channel16(converted), alpha)
  }
  { r: convert(self.r), g: convert(self.g), b: convert(self.b), a: alpha }
}

///|
/// A tight rectangular rendering surface addressed in device coordinates.
priv struct RenderSurface {
  mut origin_x : Int
  mut origin_y : Int
  width : Int
  height : Int
  color_space : WorkingColorSpace
  pixels : Array[PremulColor16]
}

///|
fn RenderSurface::new(
  origin_x : Int,
  origin_y : Int,
  width : Int,
  height : Int,
  color_space : WorkingColorSpace,
) -> RenderSurface {
  let width = if width < 0 { 0 } else { width }
  let height = if height < 0 { 0 } else { height }
  {
    origin_x,
    origin_y,
    width,
    height,
    color_space,
    pixels: Array::make(width * height, transparent_premul16()),
  }
}

///|
fn RenderSurface::contains_device(
  self : RenderSurface,
  x : Int,
  y : Int,
) -> Bool {
  x >= self.origin_x &&
  x < self.origin_x + self.width &&
  y >= self.origin_y &&
  y < self.origin_y + self.height
}

///|
fn RenderSurface::index_device(self : RenderSurface, x : Int, y : Int) -> Int {
  (y - self.origin_y) * self.width + (x - self.origin_x)
}

///|
#warnings("-unused_value")
fn RenderSurface::get_device(
  self : RenderSurface,
  x : Int,
  y : Int,
) -> PremulColor16 {
  if self.contains_device(x, y) {
    self.pixels[self.index_device(x, y)]
  } else {
    transparent_premul16()
  }
}

///|
#warnings("-unused_value")
fn RenderSurface::set_device(
  self : RenderSurface,
  x : Int,
  y : Int,
  color : PremulColor16,
) -> Unit {
  if self.contains_device(x, y) {
    self.pixels[self.index_device(x, y)] = color
  }
}

///|
#warnings("-unused_value")
fn RenderSurface::composite_device(
  self : RenderSurface,
  x : Int,
  y : Int,
  source : PremulColor16,
) -> Unit {
  if self.contains_device(x, y) {
    let index = self.index_device(x, y)
    self.pixels[index] = source.source_over(self.pixels[index])
  }
}

///|
fn RenderSurface::clear(self : RenderSurface) -> Unit {
  let transparent = transparent_premul16()
  for index in 0.. Image {
  let image = Image::new(self.width, self.height)
  for local_y in 0.. Unit {
  self.clear()
  let width = min_int(self.width, image.width)
  let height = min_int(self.height, image.height)
  for local_y in 0.. Unit {
  let factor = unit_to_channel16(opacity)
  for index in 0.. RenderSurface {
  let result = RenderSurface::new(
    self.origin_x,
    self.origin_y,
    self.width,
    self.height,
    color_space,
  )
  for index in 0.. RenderSurfacePool {
  {
    available: [],
    max_surfaces: if max_surfaces < 0 {
      0
    } else {
      max_surfaces
    },
  }
}

///|
#warnings("-unused_value")
fn RenderSurfacePool::acquire(
  self : RenderSurfacePool,
  origin_x : Int,
  origin_y : Int,
  width : Int,
  height : Int,
  color_space : WorkingColorSpace,
) -> RenderSurface {
  for index in 0.. Unit {
  if self.available.length() < self.max_surfaces {
    surface.clear()
    self.available.push(surface)
  }
}