///|
/// An owned Cairo drawing source used by `Context::set_source` and masks.
///
/// A value owns one native pattern reference through its private raw handle;
/// MoonBit finalization releases that reference. Solid, surface, gradient,
/// mesh, and raster-source subtypes share this wrapper. Equality and hashing
/// use native pointer identity, not rendered-value equality.
struct Pattern(@pattern_impl.RawPattern)

///|
fn Pattern::from_raw(raw : @pattern_impl.RawPattern) -> Pattern {
  Pattern(raw)
}

///|
fn Pattern::to_raw(self : Pattern) -> @pattern_impl.RawPattern {
  self.0
}

///|
fn pattern_status_from_raw(raw : Int) -> Status {
  status_from_raw(raw) catch {
    _ => InvalidStatus
  }
}

///|
fn check_pattern_status_raw(raw : Int) -> Unit raise CairoError {
  check_status(status_from_raw(raw))
}

///|
/// Create an opaque solid-color pattern.
///
/// `red`, `green`, and `blue` are clamped to `[0.0, 1.0]`. The returned value
/// owns its native pattern and raises the checked creation status, including
/// allocation failure.
pub fn Pattern::solid_rgb(
  red : Double,
  green : Double,
  blue : Double,
) -> Pattern raise CairoError {
  let raw = @pattern_impl.create_rgb_raw(red, green, blue)
  check_pattern_status_raw(@pattern_impl.status_raw(raw))
  Pattern::from_raw(raw)
}

///|
/// Create a solid-color pattern with explicit alpha.
///
/// All four components are clamped to `[0.0, 1.0]`. The returned value owns
/// its native pattern and raises the checked creation status.
pub fn Pattern::solid_rgba(
  red : Double,
  green : Double,
  blue : Double,
  alpha : Double,
) -> Pattern raise CairoError {
  let raw = @pattern_impl.create_rgba_raw(red, green, blue, alpha)
  check_pattern_status_raw(@pattern_impl.status_raw(raw))
  Pattern::from_raw(raw)
}

///|
/// Create a pattern that samples `surface`.
///
/// The new pattern retains the source surface, including MoonBit-managed image
/// data backing it, so the pattern remains usable after `surface` leaves scope.
/// Raises either the source or newly created pattern status.
pub fn Pattern::for_surface(surface : Surface) -> Pattern raise CairoError {
  let raw = @pattern_impl.create_for_surface_raw(surface.to_raw())
  check_pattern_status_raw(@pattern_impl.status_raw(raw))
  Pattern::from_raw(raw)
}

///|
/// Return the surface sampled by a surface pattern.
///
/// The result owns an independent Cairo surface reference and remains valid
/// after this pattern leaves scope. Raises `PatternTypeMismatch` for any other
/// pattern subtype and propagates an existing sticky pattern or surface error.
pub fn Pattern::get_surface(self : Pattern) -> Surface raise CairoError {
  let status = Ref(0)
  let raw = @pattern_impl.get_surface_raw(self.to_raw(), status)
  check_pattern_status_raw(status.val)
  Surface::from_raw(raw)
}

///|
/// Return this pattern's sticky Cairo status without raising.
///
/// Once Cairo records an error, later checked operations keep reporting it.
/// An unrecognized native status is conservatively returned as `InvalidStatus`.
pub fn Pattern::status(self : Pattern) -> Status {
  pattern_status_from_raw(@pattern_impl.status_raw(self.to_raw()))
}

///|
/// Test whether two wrappers refer to the same native Cairo pattern.
///
/// This is pointer identity; independently created patterns with equal colors
/// or geometry compare unequal.
pub fn Pattern::equal(self : Pattern, other : Pattern) -> Bool {
  @pattern_impl.equal_raw(self.to_raw(), other.to_raw())
}

///|
/// Return a stable hash of this native pattern's pointer identity.
///
/// It is consistent with `equal()` for the lifetime of the pattern and does
/// not describe its mutable drawing state.
pub fn Pattern::hash(self : Pattern) -> UInt64 {
  @pattern_impl.hash_raw(self.to_raw())
}

///|
pub impl Eq for Pattern with fn equal(self, other) {
  self.equal(other)
}

///|
pub impl Hash for Pattern with fn hash(self) {
  self.hash().hash()
}

///|
pub impl Hash for Pattern with fn hash_combine(self, hasher) {
  hasher.combine_uint64(self.hash())
}

///|
fn extend_from_raw(raw : Int) -> Extend raise CairoError {
  match raw {
    0 => ExtendNone
    1 => Repeat
    2 => Reflect
    3 => Pad
    _ =>
      raise CairoInvalidArgument(InvalidStatus, "unknown cairo extend: \{raw}")
  }
}

///|
fn extend_to_raw(extend_mode : Extend) -> Int {
  match extend_mode {
    ExtendNone => 0
    Repeat => 1
    Reflect => 2
    Pad => 3
  }
}

///|
fn filter_from_raw(raw : Int) -> Filter raise CairoError {
  match raw {
    0 => Fast
    1 => Good
    2 => Best
    3 => Nearest
    4 => Bilinear
    5 => Gaussian
    _ =>
      raise CairoInvalidArgument(InvalidStatus, "unknown cairo filter: \{raw}")
  }
}

///|
fn filter_to_raw(filter : Filter) -> Int {
  match filter {
    Fast => 0
    Good => 1
    Best => 2
    Nearest => 3
    Bilinear => 4
    Gaussian => 5
  }
}

///|
fn dither_from_raw(raw : Int) -> Dither raise CairoError {
  match raw {
    0 => DitherNone
    1 => DitherDefault
    2 => DitherFast
    3 => DitherGood
    4 => DitherBest
    _ =>
      raise CairoInvalidArgument(InvalidStatus, "unknown cairo dither: \{raw}")
  }
}

///|
fn dither_to_raw(dither : Dither) -> Int {
  match dither {
    DitherNone => 0
    DitherDefault => 1
    DitherFast => 2
    DitherGood => 3
    DitherBest => 4
  }
}

///|
/// Return how sampling behaves outside this pattern's intrinsic bounds.
///
/// Surface patterns default to `ExtendNone`; gradient patterns default to
/// `Pad`. Raises a sticky pattern error or `InvalidStatus` if a prior raw setter
/// stored a value unknown to the typed `Extend` enum.
pub fn Pattern::get_extend(self : Pattern) -> Extend raise CairoError {
  extend_from_raw(self.get_extend_raw())
}

///|
/// Set how sampling behaves outside this pattern's intrinsic bounds.
///
/// The mode affects later uses of this mutable pattern. Raises the checked
/// sticky pattern status.
pub fn Pattern::set_extend(
  self : Pattern,
  extend_mode : Extend,
) -> Unit raise CairoError {
  check_pattern_status_raw(
    @pattern_impl.set_extend_raw(self.to_raw(), extend_to_raw(extend_mode)),
  )
}

///|
/// Return Cairo's integer extend value without typed-enum conversion.
///
/// Known values are `0` through `3`; an arbitrary value previously supplied to
/// `set_extend_raw()` is returned unchanged. Raises an existing pattern error.
pub fn Pattern::get_extend_raw(self : Pattern) -> Int raise CairoError {
  check_pattern_status_raw(@pattern_impl.status_raw(self.to_raw()))
  @pattern_impl.get_extend_raw(self.to_raw())
}

///|
/// Pass an integer extend value directly to Cairo for pycairo compatibility.
///
/// The value is intentionally not range-checked. Unknown values can be read by
/// `get_extend_raw()`, while `get_extend()` raises `InvalidStatus` for them.
/// Raises the checked sticky pattern status.
pub fn Pattern::set_extend_raw(
  self : Pattern,
  extend_mode : Int,
) -> Unit raise CairoError {
  check_pattern_status_raw(
    @pattern_impl.set_extend_raw(self.to_raw(), extend_mode),
  )
}

///|
/// Return the sampling filter hint used when this pattern is rescaled.
///
/// New patterns normally use `Good`. Raises a sticky pattern error or
/// `InvalidStatus` when a prior raw setter stored an unknown integer.
pub fn Pattern::get_filter(self : Pattern) -> Filter raise CairoError {
  filter_from_raw(self.get_filter_raw())
}

///|
/// Set the quality/performance hint used to filter this pattern.
///
/// Cairo backends may choose an implementation compatible with the hint.
/// Raises the checked sticky pattern status.
pub fn Pattern::set_filter(
  self : Pattern,
  filter : Filter,
) -> Unit raise CairoError {
  check_pattern_status_raw(
    @pattern_impl.set_filter_raw(self.to_raw(), filter_to_raw(filter)),
  )
}

///|
/// Return Cairo's integer filter hint without typed-enum conversion.
///
/// Known values are `0` through `5`; arbitrary values set through the raw API
/// are preserved. Raises an existing sticky pattern error.
pub fn Pattern::get_filter_raw(self : Pattern) -> Int raise CairoError {
  check_pattern_status_raw(@pattern_impl.status_raw(self.to_raw()))
  @pattern_impl.get_filter_raw(self.to_raw())
}

///|
/// Pass an integer filter hint directly to Cairo for pycairo compatibility.
///
/// The value is intentionally not range-checked. The typed getter later raises
/// `InvalidStatus` if Cairo returns a value outside the `Filter` enum.
pub fn Pattern::set_filter_raw(
  self : Pattern,
  filter : Int,
) -> Unit raise CairoError {
  check_pattern_status_raw(@pattern_impl.set_filter_raw(self.to_raw(), filter))
}

///|
/// Return this pattern's dithering hint.
///
/// Dithering is available with Cairo 1.18 or newer and is currently honored by
/// pixman-backed rendering. Older runtimes raise `InvalidStatus`; an unknown
/// raw value also raises `InvalidStatus` during typed conversion.
pub fn Pattern::get_dither(self : Pattern) -> Dither raise CairoError {
  dither_from_raw(self.get_dither_raw())
}

///|
/// Set this pattern's dithering hint for later rendering.
///
/// Requires Cairo 1.18 or newer; older runtimes raise `InvalidStatus`. The hint
/// does not guarantee that every backend performs dithering.
pub fn Pattern::set_dither(
  self : Pattern,
  dither : Dither,
) -> Unit raise CairoError {
  check_pattern_status_raw(
    @pattern_impl.set_dither_raw(self.to_raw(), dither_to_raw(dither)),
  )
}

///|
/// Return Cairo's raw integer dithering hint.
///
/// Requires Cairo 1.18 or newer and otherwise raises `InvalidStatus`. Values
/// outside the typed enum are returned unchanged.
pub fn Pattern::get_dither_raw(self : Pattern) -> Int raise CairoError {
  let status = Ref(0)
  let dither = @pattern_impl.get_dither_raw(self.to_raw(), status)
  check_pattern_status_raw(status.val)
  dither
}

///|
/// Pass a dithering integer directly to Cairo for pycairo compatibility.
///
/// No enum range check is performed. Requires Cairo 1.18 or newer; older
/// runtimes raise `InvalidStatus`, and the typed getter rejects unknown values.
pub fn Pattern::set_dither_raw(
  self : Pattern,
  dither : Int,
) -> Unit raise CairoError {
  check_pattern_status_raw(@pattern_impl.set_dither_raw(self.to_raw(), dither))
}

///|
/// Return the affine transform from user space to pattern space.
///
/// New patterns start with identity. Because this direction is the inverse of
/// the apparent pattern-to-user transform, scaling the matrix down makes the
/// rendered pattern appear larger. Raises an existing sticky pattern error.
pub fn Pattern::get_matrix(self : Pattern) -> Matrix raise CairoError {
  let xx = Ref(0.0)
  let yx = Ref(0.0)
  let xy = Ref(0.0)
  let yy = Ref(0.0)
  let x0 = Ref(0.0)
  let y0 = Ref(0.0)
  check_pattern_status_raw(
    @pattern_impl.get_matrix_raw(self.to_raw(), xx, yx, xy, yy, x0, y0),
  )
  Matrix::new(xx=xx.val, yx=yx.val, xy=xy.val, yy=yy.val, x0=x0.val, y0=y0.val)
}

///|
/// Set the affine transform from user space to pattern space.
///
/// `matrix` is copied; later changes to the value do not affect the pattern.
/// A non-invertible matrix raises `CairoInvalidArgument(InvalidMatrix, _)` and
/// leaves that error sticky on the pattern.
pub fn Pattern::set_matrix(
  self : Pattern,
  matrix : Matrix,
) -> Unit raise CairoError {
  check_pattern_status_raw(
    @pattern_impl.set_matrix_raw(
      self.to_raw(),
      matrix.xx,
      matrix.yx,
      matrix.xy,
      matrix.yy,
      matrix.x0,
      matrix.y0,
    ),
  )
}

///|
/// Return a solid pattern's unpremultiplied RGBA components.
///
/// Components are in `[0.0, 1.0]`; an RGB pattern reports alpha `1.0`. Raises
/// `PatternTypeMismatch` for surface, gradient, mesh, or raster-source patterns
/// and propagates any existing sticky pattern error.
pub fn Pattern::get_rgba(
  self : Pattern,
) -> (Double, Double, Double, Double) raise CairoError {
  let red = Ref(0.0)
  let green = Ref(0.0)
  let blue = Ref(0.0)
  let alpha = Ref(0.0)
  check_pattern_status_raw(
    @pattern_impl.get_rgba_raw(self.to_raw(), red, green, blue, alpha),
  )
  (red.val, green.val, blue.val, alpha.val)
}