///
/// Simple DirectMedia Layer
/// Copyright (C) 1997-2025 Sam Lantinga 
///
/// This software is provided 'as-is', without any express or implied
/// warranty.  In no event will the authors be held liable for any damages
/// arising from the use of this software.
///
/// Permission is granted to anyone to use this software for any purpose,
/// including commercial applications, and to alter it and redistribute it
/// freely, subject to the following restrictions:
///
/// 1. The origin of this software must not be misrepresented; you must not
///    claim that you wrote the original software. If you use this software
///    in a product, an acknowledgment in the product documentation would be
///    appreciated but is not required.
/// 2. Altered source versions must be plainly marked as such, and must not be
///    misrepresented as being the original software.
/// 3. This notice may not be removed or altered from any source distribution.
///

/// # CategoryRect
///
/// Some helper functions for managing rectangles and 2D points, in both
/// integer and floating point versions.

///|
/// The structure that defines a point (using integers).
///
/// ```c
/// typedef struct SDL_Point
/// {
///     int x;
///     int y;
/// } SDL_Point;
/// ```
pub(all) struct SDL_Point {
  x : Int
  y : Int
}

///|
/// The structure that defines a point (using floating point values).
///
/// ```c
/// typedef struct SDL_FPoint
/// {
///     float x;
///     float y;
/// } SDL_FPoint;
/// ```
pub(all) struct SDL_FPoint {
  x : Float
  y : Float
}

///|
/// A rectangle, with the origin at the upper left (using integers).
///
/// ```c
/// typedef struct SDL_Rect
/// {
///     int x, y;
///     int w, h;
/// } SDL_Rect;
/// ```
pub(all) struct SDL_Rect {
  x : Int
  y : Int
  w : Int
  h : Int
}

///| A rectangle, with the origin at the upper left (using floating point

///|
/// values).
///
/// ```c
/// typedef struct SDL_FRect
/// {
///     float x;
///     float y;
///     float w;
///     float h;
/// } SDL_FRect;
/// ```
pub(all) struct SDL_FRect {
  x : Float
  y : Float
  w : Float
  h : Float
}

///|
/// Convert an SDL_Rect to SDL_FRect
///
/// ```c
/// SDL_FORCE_INLINE void SDL_RectToFRect(const SDL_Rect *rect, SDL_FRect *frect)
/// {
///     frect->x = (float)rect->x;
///     frect->y = (float)rect->y;
///     frect->w = (float)rect->w;
///     frect->h = (float)rect->h;
/// }
/// ```
pub fn sdl_RectToFRect(rect : SDL_Rect) -> SDL_FRect {
  {
    x: Float::from_int(rect.x),
    y: Float::from_int(rect.y),
    w: Float::from_int(rect.w),
    h: Float::from_int(rect.h),
  }
}

///|
/// Determine whether a point resides inside a rectangle.
///
/// ```c
/// SDL_FORCE_INLINE bool SDL_PointInRect(const SDL_Point *p, const SDL_Rect *r)
/// {
///     return ( p && r && (p->x >= r->x) && (p->x < (r->x + r->w)) &&
///              (p->y >= r->y) && (p->y < (r->y + r->h)) ) ? true : false;
/// }
/// ```
pub fn sdl_PointInRect(p : SDL_Point, r : SDL_Rect) -> Bool {
  p.x >= r.x && p.x < r.x + r.w && p.y >= r.y && p.y < r.y + r.h
}

///|
/// Determine whether a rectangle has no area.
///
/// ```c
/// SDL_FORCE_INLINE bool SDL_RectEmpty(const SDL_Rect *r)
/// {
///     return ((!r) || (r->w <= 0) || (r->h <= 0)) ? true : false;
/// }
/// ```
pub fn sdl_RectEmpty(r : SDL_Rect) -> Bool {
  r.w <= 0 || r.h <= 0
}

///|
/// Determine whether two rectangles are equal.
///
/// ```c
/// SDL_FORCE_INLINE bool SDL_RectsEqual(const SDL_Rect *a, const SDL_Rect *b)
/// {
///     return (a && b && (a->x == b->x) && (a->y == b->y) &&
///             (a->w == b->w) && (a->h == b->h)) ? true : false;
/// }
/// ```
pub fn sdl_RectsEqual(a : SDL_Rect, b : SDL_Rect) -> Bool {
  a.x == b.x && a.y == b.y && a.w == b.w && a.h == b.h
}

///|
/// Determine whether two rectangles intersect.
///
/// ```c
/// extern SDL_DECLSPEC bool SDLCALL SDL_HasRectIntersection(const SDL_Rect *A, const SDL_Rect *B);
/// ```
#owned(a, b)
pub extern "C" fn sdl_HasRectIntersection(a : SDL_Rect, b : SDL_Rect) -> Bool = "SDL_HasRectIntersection"

///|
/// Calculate the intersection of two rectangles.
///
/// ```c
/// extern SDL_DECLSPEC bool SDLCALL SDL_GetRectIntersection(const SDL_Rect *A, const SDL_Rect *B, SDL_Rect *result);
/// ```
#owned(a, b, result)
extern "C" fn __sdl_GetRectIntersection(
  a : SDL_Rect,
  b : SDL_Rect,
  result : FixedArray[SDL_Rect],
) -> Bool = "SDL_GetRectIntersection"

///|
pub fn sdl_GetRectIntersection(a : SDL_Rect, b : SDL_Rect) -> (Bool, SDL_Rect) {
  let result : FixedArray[SDL_Rect] = FixedArray::make(1, {
    x: 0,
    y: 0,
    w: 0,
    h: 0,
  })
  let success = __sdl_GetRectIntersection(a, b, result)
  (success, result[0])
}

///|
/// Calculate the union of two rectangles.
///
/// ```c
/// extern SDL_DECLSPEC bool SDLCALL SDL_GetRectUnion(const SDL_Rect *A, const SDL_Rect *B, SDL_Rect *result);
/// ```
#owned(a, b, result)
extern "C" fn __sdl_GetRectUnion(
  a : SDL_Rect,
  b : SDL_Rect,
  result : FixedArray[SDL_Rect],
) -> Bool = "SDL_GetRectUnion"

///|
pub fn sdl_GetRectUnion(a : SDL_Rect, b : SDL_Rect) -> (Bool, SDL_Rect) {
  let result : FixedArray[SDL_Rect] = FixedArray::make(1, {
    x: 0,
    y: 0,
    w: 0,
    h: 0,
  })
  let success = __sdl_GetRectUnion(a, b, result)
  (success, result[0])
}

///|
/// Calculate a minimal rectangle enclosing a set of points.
///
/// ```c
/// extern SDL_DECLSPEC bool SDLCALL SDL_GetRectEnclosingPoints(const SDL_Point *points, int count, const SDL_Rect *clip, SDL_Rect *result);
/// ```
#owned(points, clip, result)
extern "C" fn __sdl_GetRectEnclosingPoints(
  points : FixedArray[SDL_Point],
  count : Int,
  clip : SDL_Rect,
  result : FixedArray[SDL_Rect],
) -> Bool = "SDL_GetRectEnclosingPoints"

///|
pub fn sdl_GetRectEnclosingPoints(
  points : Array[SDL_Point],
  clip : SDL_Rect,
) -> (Bool, SDL_Rect) {
  let result : FixedArray[SDL_Rect] = FixedArray::make(1, {
    x: 0,
    y: 0,
    w: 0,
    h: 0,
  })
  let success = __sdl_GetRectEnclosingPoints(
    FixedArray::from_array(points),
    points.length(),
    clip,
    result,
  )
  (success, result[0])
}

///|
/// Calculate the intersection of a rectangle and line segment.
///
/// ```c
/// extern SDL_DECLSPEC bool SDLCALL SDL_GetRectAndLineIntersection(const SDL_Rect *rect, int *X1, int *Y1, int *X2, int *Y2);
/// ```
#owned(rect, x1, y1, x2, y2)
extern "C" fn __sdl_GetRectAndLineIntersection(
  rect : SDL_Rect,
  x1 : FixedArray[Int],
  y1 : FixedArray[Int],
  x2 : FixedArray[Int],
  y2 : FixedArray[Int],
) -> Bool = "SDL_GetRectAndLineIntersection"

///|
pub fn sdl_GetRectAndLineIntersection(
  rect : SDL_Rect,
  x1 : Int,
  y1 : Int,
  x2 : Int,
  y2 : Int,
) -> (Bool, Int, Int, Int, Int) {
  let x1_ptr = FixedArray::make(1, x1)
  let y1_ptr = FixedArray::make(1, y1)
  let x2_ptr = FixedArray::make(1, x2)
  let y2_ptr = FixedArray::make(1, y2)
  let success = __sdl_GetRectAndLineIntersection(
    rect, x1_ptr, y1_ptr, x2_ptr, y2_ptr,
  )
  (success, x1_ptr[0], y1_ptr[0], x2_ptr[0], y2_ptr[0])
}

///|
/// Determine whether a point resides inside a floating point rectangle.
///
/// ```c
/// SDL_FORCE_INLINE bool SDL_PointInRectFloat(const SDL_FPoint *p, const SDL_FRect *r)
/// {
///     return ( p && r && (p->x >= r->x) && (p->x <= (r->x + r->w)) &&
///              (p->y >= r->y) && (p->y <= (r->y + r->h)) ) ? true : false;
/// }
/// ```
pub fn sdl_PointInRectFloat(p : SDL_FPoint, r : SDL_FRect) -> Bool {
  p.x >= r.x && p.x <= r.x + r.w && p.y >= r.y && p.y <= r.y + r.h
}

///|
/// Determine whether a floating point rectangle takes no space.
///
/// ```c
/// SDL_FORCE_INLINE bool SDL_RectEmptyFloat(const SDL_FRect *r)
/// {
///     return ((!r) || (r->w < 0.0f) || (r->h < 0.0f)) ? true : false;
/// }
/// ```
pub fn sdl_RectEmptyFloat(r : SDL_FRect) -> Bool {
  r.w < 0.0 || r.h < 0.0
}

///| Determine whether two floating point rectangles are equal, within some

///|
/// given epsilon.
///
/// ```c
/// SDL_FORCE_INLINE bool SDL_RectsEqualEpsilon(const SDL_FRect *a, const SDL_FRect *b, float epsilon)
/// {
///     return (a && b && ((a == b) ||
///             ((SDL_fabsf(a->x - b->x) <= epsilon) &&
///             (SDL_fabsf(a->y - b->y) <= epsilon) &&
///             (SDL_fabsf(a->w - b->w) <= epsilon) &&
///             (SDL_fabsf(a->h - b->h) <= epsilon))))
///             ? true : false;
/// }
/// ```
pub fn sdl_RectsEqualEpsilon(
  a : SDL_FRect,
  b : SDL_FRect,
  epsilon : Float,
) -> Bool {
  (a.x - b.x).abs() <= epsilon &&
  (a.y - b.y).abs() <= epsilon &&
  (a.w - b.w).abs() <= epsilon &&
  (a.h - b.h).abs() <= epsilon
}

///| Determine whether two floating point rectangles are equal, within a default

///|
/// epsilon.
///
/// ```c
/// SDL_FORCE_INLINE bool SDL_RectsEqualFloat(const SDL_FRect *a, const SDL_FRect *b)
/// {
///     return SDL_RectsEqualEpsilon(a, b, SDL_FLT_EPSILON);
/// }
/// ```
pub fn sdl_RectsEqualFloat(a : SDL_FRect, b : SDL_FRect) -> Bool {
  sdl_RectsEqualEpsilon(a, b, 1.19209290e-07) // SDL_FLT_EPSILON
}

///|
/// Determine whether two rectangles intersect with float precision.
///
/// ```c
/// extern SDL_DECLSPEC bool SDLCALL SDL_HasRectIntersectionFloat(const SDL_FRect *A, const SDL_FRect *B);
/// ```
#owned(a, b)
pub extern "C" fn sdl_HasRectIntersectionFloat(
  a : SDL_FRect,
  b : SDL_FRect,
) -> Bool = "SDL_HasRectIntersectionFloat"

///|
/// Calculate the intersection of two rectangles with float precision.
///
/// ```c
/// extern SDL_DECLSPEC bool SDLCALL SDL_GetRectIntersectionFloat(const SDL_FRect *A, const SDL_FRect *B, SDL_FRect *result);
/// ```
#owned(a, b, result)
extern "C" fn __sdl_GetRectIntersectionFloat(
  a : SDL_FRect,
  b : SDL_FRect,
  result : FixedArray[SDL_FRect],
) -> Bool = "SDL_GetRectIntersectionFloat"

///|
pub fn sdl_GetRectIntersectionFloat(
  a : SDL_FRect,
  b : SDL_FRect,
) -> (Bool, SDL_FRect) {
  let result : FixedArray[SDL_FRect] = FixedArray::make(1, {
    x: 0.0,
    y: 0.0,
    w: 0.0,
    h: 0.0,
  })
  let success = __sdl_GetRectIntersectionFloat(a, b, result)
  (success, result[0])
}

///|
/// Calculate the union of two rectangles with float precision.
///
/// ```c
/// extern SDL_DECLSPEC bool SDLCALL SDL_GetRectUnionFloat(const SDL_FRect *A, const SDL_FRect *B, SDL_FRect *result);
/// ```
#owned(a, b, result)
extern "C" fn __sdl_GetRectUnionFloat(
  a : SDL_FRect,
  b : SDL_FRect,
  result : FixedArray[SDL_FRect],
) -> Bool = "SDL_GetRectUnionFloat"

///|
pub fn sdl_GetRectUnionFloat(a : SDL_FRect, b : SDL_FRect) -> (Bool, SDL_FRect) {
  let result : FixedArray[SDL_FRect] = FixedArray::make(1, {
    x: 0.0,
    y: 0.0,
    w: 0.0,
    h: 0.0,
  })
  let success = __sdl_GetRectUnionFloat(a, b, result)
  (success, result[0])
}

///| Calculate a minimal rectangle enclosing a set of points with float

///|
/// precision.
///
/// ```c
/// extern SDL_DECLSPEC bool SDLCALL SDL_GetRectEnclosingPointsFloat(const SDL_FPoint *points, int count, const SDL_FRect *clip, SDL_FRect *result);
/// ```
#owned(points, clip, result)
extern "C" fn __sdl_GetRectEnclosingPointsFloat(
  points : FixedArray[SDL_FPoint],
  count : Int,
  clip : SDL_FRect,
  result : FixedArray[SDL_FRect],
) -> Bool = "SDL_GetRectEnclosingPointsFloat"

///|
pub fn sdl_GetRectEnclosingPointsFloat(
  points : Array[SDL_FPoint],
  clip : SDL_FRect,
) -> (Bool, SDL_FRect) {
  let result : FixedArray[SDL_FRect] = FixedArray::make(1, {
    x: 0.0,
    y: 0.0,
    w: 0.0,
    h: 0.0,
  })
  let success = __sdl_GetRectEnclosingPointsFloat(
    FixedArray::from_array(points),
    points.length(),
    clip,
    result,
  )
  (success, result[0])
}

///| Calculate the intersection of a rectangle and line segment with float

///|
/// precision.
///
/// ```c
/// extern SDL_DECLSPEC bool SDLCALL SDL_GetRectAndLineIntersectionFloat(const SDL_FRect *rect, float *X1, float *Y1, float *X2, float *Y2);
/// ```
#owned(rect, x1, y1, x2, y2)
extern "C" fn __sdl_GetRectAndLineIntersectionFloat(
  rect : SDL_FRect,
  x1 : FixedArray[Float],
  y1 : FixedArray[Float],
  x2 : FixedArray[Float],
  y2 : FixedArray[Float],
) -> Bool = "SDL_GetRectAndLineIntersectionFloat"

///|
pub fn sdl_GetRectAndLineIntersectionFloat(
  rect : SDL_FRect,
  x1 : Float,
  y1 : Float,
  x2 : Float,
  y2 : Float,
) -> (Bool, Float, Float, Float, Float) {
  let x1_ptr = FixedArray::make(1, x1)
  let y1_ptr = FixedArray::make(1, y1)
  let x2_ptr = FixedArray::make(1, x2)
  let y2_ptr = FixedArray::make(1, y2)
  let success = __sdl_GetRectAndLineIntersectionFloat(
    rect, x1_ptr, y1_ptr, x2_ptr, y2_ptr,
  )
  (success, x1_ptr[0], y1_ptr[0], x2_ptr[0], y2_ptr[0])
}