///|
extern "C" fn native_shader_color(color_argb : UInt) -> ShaderHandle = "moonbit_skia_shader_color"

///|
extern "C" fn native_color_filter_null() -> ColorFilterHandle = "moonbit_skia_color_filter_null"

///|
#borrow(color_filter)
extern "C" fn native_color_filter_is_null(
  color_filter : ColorFilterHandle,
) -> Bool = "moonbit_skia_color_filter_is_null"

///|
#borrow(values)
extern "C" fn native_color_filter_matrix(
  values : FixedArray[Float],
) -> ColorFilterHandle = "moonbit_skia_color_filter_matrix"

///|
extern "C" fn native_image_filter_null() -> ImageFilterHandle = "moonbit_skia_image_filter_null"

///|
#borrow(image_filter)
extern "C" fn native_image_filter_is_null(
  image_filter : ImageFilterHandle,
) -> Bool = "moonbit_skia_image_filter_is_null"

///|
extern "C" fn native_image_filter_blur(
  sigma_x : Float,
  sigma_y : Float,
) -> ImageFilterHandle = "moonbit_skia_image_filter_blur"

///|
extern "C" fn native_mask_filter_null() -> MaskFilterHandle = "moonbit_skia_mask_filter_null"

///|
#borrow(mask_filter)
extern "C" fn native_mask_filter_is_null(
  mask_filter : MaskFilterHandle,
) -> Bool = "moonbit_skia_mask_filter_is_null"

///|
extern "C" fn native_mask_filter_blur(sigma : Float) -> MaskFilterHandle = "moonbit_skia_mask_filter_blur"

///|
extern "C" fn native_shader_linear_gradient(
  x0 : Float,
  y0 : Float,
  x1 : Float,
  y1 : Float,
  color0_argb : UInt,
  color1_argb : UInt,
  tile_mode : Int,
) -> ShaderHandle = "moonbit_skia_shader_linear_gradient"

///|
extern "C" fn native_shader_radial_gradient(
  center_x : Float,
  center_y : Float,
  radius : Float,
  color0_argb : UInt,
  color1_argb : UInt,
  tile_mode : Int,
) -> ShaderHandle = "moonbit_skia_shader_radial_gradient"

///|
#borrow(shader)
extern "C" fn native_shader_is_null(shader : ShaderHandle) -> Bool = "moonbit_skia_shader_is_null"

///|
fn ColorFilter::from_handle(handle : ColorFilterHandle) -> ColorFilter? {
  if native_color_filter_is_null(handle) {
    None
  } else {
    Some({ handle, })
  }
}

///|
pub fn ColorFilter::matrix(values : Array[Float]) -> ColorFilter? {
  if values.length() != 20 || !skia_float_array_is_finite(values) {
    None
  } else {
    ColorFilter::from_handle(
      native_color_filter_matrix(FixedArray::from_array(values)),
    )
  }
}

///|
pub fn ColorFilter::saturation(amount : Float) -> ColorFilter? {
  if !skia_scalar_is_finite(amount) {
    None
  } else {
    let one : Float = 1.0
    let zero : Float = 0.0
    let inv = one - amount
    let r : Float = 0.2126 * inv
    let g : Float = 0.7152 * inv
    let b : Float = 0.0722 * inv
    ColorFilter::matrix([
      r + amount,
      g,
      b,
      zero,
      zero,
      r,
      g + amount,
      b,
      zero,
      zero,
      r,
      g,
      b + amount,
      zero,
      zero,
      zero,
      zero,
      zero,
      one,
      zero,
    ])
  }
}

///|
pub fn ColorFilter::brightness(amount : Float) -> ColorFilter? {
  if !skia_scalar_is_finite(amount) {
    None
  } else {
    let zero : Float = 0.0
    let one : Float = 1.0
    ColorFilter::matrix([
      amount, zero, zero, zero, zero, zero, amount, zero, zero, zero, zero, zero,
      amount, zero, zero, zero, zero, zero, one, zero,
    ])
  }
}

///|
pub fn ColorFilter::contrast(amount : Float) -> ColorFilter? {
  if !skia_scalar_is_finite(amount) {
    None
  } else {
    let zero : Float = 0.0
    let one : Float = 1.0
    let offset : Float = 0.5 * (one - amount)
    ColorFilter::matrix([
      amount, zero, zero, zero, offset, zero, amount, zero, zero, offset, zero, zero,
      amount, zero, offset, zero, zero, zero, one, zero,
    ])
  }
}

///|
pub fn ColorFilter::is_available(self : ColorFilter) -> Bool {
  !native_color_filter_is_null(self.handle)
}

///|
fn ImageFilter::from_handle(handle : ImageFilterHandle) -> ImageFilter? {
  if native_image_filter_is_null(handle) {
    None
  } else {
    Some({ handle, })
  }
}

///|
pub fn ImageFilter::blur(sigma_x : Float, sigma_y : Float) -> ImageFilter? {
  if !skia_scalar_is_finite(sigma_x) ||
    !skia_scalar_is_finite(sigma_y) ||
    sigma_x <= 0.0 ||
    sigma_y <= 0.0 {
    None
  } else {
    ImageFilter::from_handle(native_image_filter_blur(sigma_x, sigma_y))
  }
}

///|
pub fn ImageFilter::is_available(self : ImageFilter) -> Bool {
  !native_image_filter_is_null(self.handle)
}

///|
fn MaskFilter::from_handle(handle : MaskFilterHandle) -> MaskFilter? {
  if native_mask_filter_is_null(handle) {
    None
  } else {
    Some({ handle, })
  }
}

///|
pub fn MaskFilter::blur(sigma : Float) -> MaskFilter? {
  if !skia_scalar_is_finite(sigma) || sigma <= 0.0 {
    None
  } else {
    MaskFilter::from_handle(native_mask_filter_blur(sigma))
  }
}

///|
pub fn MaskFilter::is_available(self : MaskFilter) -> Bool {
  !native_mask_filter_is_null(self.handle)
}

///|
fn Shader::from_handle(handle : ShaderHandle) -> Shader? {
  if native_shader_is_null(handle) {
    None
  } else {
    Some({ handle, })
  }
}

///|
pub fn Shader::color(color : @skia.Color) -> Shader? {
  Shader::from_handle(native_shader_color(color.argb()))
}

///|
pub fn Shader::linear_gradient(
  start : @skia.Point,
  end : @skia.Point,
  start_color : @skia.Color,
  end_color : @skia.Color,
  tile_mode? : @skia.TileMode = Clamp,
) -> Shader? {
  if !start.is_finite() ||
    !end.is_finite() ||
    start.distance_to_squared(end) <= 0.0 {
    None
  } else {
    Shader::from_handle(
      native_shader_linear_gradient(
        start.x,
        start.y,
        end.x,
        end.y,
        start_color.argb(),
        end_color.argb(),
        tile_mode.to_skia_ordinal(),
      ),
    )
  }
}

///|
pub fn Shader::radial_gradient(
  center : @skia.Point,
  radius : Float,
  inner_color : @skia.Color,
  outer_color : @skia.Color,
  tile_mode? : @skia.TileMode = Clamp,
) -> Shader? {
  if !center.is_finite() || !skia_scalar_is_finite(radius) || radius <= 0.0 {
    None
  } else {
    Shader::from_handle(
      native_shader_radial_gradient(
        center.x,
        center.y,
        radius,
        inner_color.argb(),
        outer_color.argb(),
        tile_mode.to_skia_ordinal(),
      ),
    )
  }
}

///|
pub fn Shader::is_available(self : Shader) -> Bool {
  !native_shader_is_null(self.handle)
}

///|
fn skia_scalar_is_finite(value : Float) -> Bool {
  value == value && value - value == 0.0
}

///|
fn skia_float_array_is_finite(values : Array[Float]) -> Bool {
  let mut finite = true
  for value in values {
    if !skia_scalar_is_finite(value) {
      finite = false
    }
  }
  finite
}