///|
/// A 2D point coordinate representing a polygon vertex.
pub struct Point2D {
  x : Double
  y : Double
} derive(Eq, Debug, Default)

///|
/// Creates a new 2D point coordinate.
pub fn Point2D::new(x : Double, y : Double) -> Point2D {
  Point2D::{ x, y, }
}

///|
/// Computes Euclidean distance to another 2D point.
pub fn Point2D::distance_to(self : Point2D, other : Point2D) -> Double {
  let dx = self.x - other.x
  let dy = self.y - other.y
  (dx * dx + dy * dy).sqrt()
}

///|
/// A 2D polygonal planar curve or ring defined by sequential vertices.
pub struct Polygon2D {
  vertices : Array[Point2D]
} derive(Eq, Debug, Default)

///|
/// Creates a new Polygon2D from an ordered array of 2D vertices.
pub fn Polygon2D::new(vertices : Array[Point2D]) -> Polygon2D {
  Polygon2D::{ vertices, }
}

///|
fn Polygon2D::to_tree(self : Polygon2D) -> @polygon.Polygon2D {
  let vs : Array[@polygon.Point2D] = []
  for i = 0; i < self.vertices.length(); i = i + 1 {
    let p = self.vertices[i]
    vs.push(@polygon.Point2D::new(p.x, p.y))
  }
  @polygon.Polygon2D::new(vs)
}

///|
fn Polygon2D::from_tree(poly : @polygon.Polygon2D) -> Polygon2D {
  let vs : Array[Point2D] = []
  for i = 0; i < poly.vertices.length(); i = i + 1 {
    let p = poly.vertices[i]
    vs.push(Point2D::new(p.x, p.y))
  }
  Polygon2D::new(vs)
}

///|
/// Computes the planar signed/unsigned area using Green's theorem / Shoelace formula.
pub fn Polygon2D::area(self : Polygon2D) -> Double {
  self.to_tree().area()
}

///|
/// Computes total boundary perimeter length of the closed polygon.
pub fn Polygon2D::perimeter(self : Polygon2D) -> Double {
  self.to_tree().perimeter()
}

///|
/// Returns the minimum axis-aligned bounding box (AABB) enveloping all vertices.
pub fn Polygon2D::envelope(self : Polygon2D) -> Rect {
  let r = self.to_tree().envelope()
  Rect::new(r.min_x, r.min_y, r.max_x, r.max_y)
}

///|
/// Tests whether a point (px, py) is contained within the polygon boundary.
pub fn Polygon2D::contains_point(
  self : Polygon2D,
  px : Double,
  py : Double,
) -> Bool {
  self.to_tree().contains_point(px, py)
}

///|
/// Computes the planar geometric centroid of the polygon.
pub fn Polygon2D::centroid(self : Polygon2D) -> Point2D {
  let c = self.to_tree().centroid()
  Point2D::new(c.x, c.y)
}

///|
/// Sutherland-Hodgman polygon clipping against an axis-aligned rectangular bounding box.
pub fn Polygon2D::clip_rect(self : Polygon2D, clip_box : Rect) -> Polygon2D {
  let res = self.to_tree().clip_rect(clip_box)
  Polygon2D::from_tree(res)
}

///|
/// Sutherland-Hodgman polygon clipping against an arbitrary convex polygon.
pub fn Polygon2D::clip_convex(
  self : Polygon2D,
  clip_poly : Polygon2D,
) -> Polygon2D {
  let res = self.to_tree().clip_convex(clip_poly.to_tree())
  Polygon2D::from_tree(res)
}

///|
/// A packed spatial index managing collections of 2D polygonal planar shapes.
pub struct StrPolygonIndex {
  priv inner : @polygon.StrPolygonIndex
} derive(Eq, Debug, Default)

///|
/// Builds a packed StrPolygonIndex from an array of 2D polygons and optional node capacity.
pub fn StrPolygonIndex::build(
  polygons : Array[Polygon2D],
  node_capacity? : Int = 16,
) -> StrPolygonIndex {
  let t_polys : Array[@polygon.Polygon2D] = []
  for i = 0; i < polygons.length(); i = i + 1 {
    t_polys.push(polygons[i].to_tree())
  }
  let inner = @polygon.StrPolygonIndex::build(t_polys, node_capacity~)
  StrPolygonIndex::{ inner, }
}

///|
/// Returns total count of indexed polygons.
pub fn StrPolygonIndex::total_polygons(self : StrPolygonIndex) -> Int {
  self.inner.total_polygons()
}

///|
/// Returns the global bounding envelope encompassing all indexed polygons.
pub fn StrPolygonIndex::envelope(self : StrPolygonIndex) -> Rect {
  let r = self.inner.envelope()
  Rect::new(r.min_x, r.min_y, r.max_x, r.max_y)
}

///|
/// Queries polygon indices whose bounding envelopes intersect the query window.
pub fn StrPolygonIndex::query_window(
  self : StrPolygonIndex,
  window : Rect,
) -> Array[Int] {
  self.inner.query_window(window)
}

///|
/// Returns the count of candidate polygons intersecting the query window without allocating a result array.
pub fn StrPolygonIndex::query_count(
  self : StrPolygonIndex,
  window : Rect,
) -> Int {
  self.inner.query_count(window)
}

///|
/// Retrieves all full Polygon2D instances intersecting the query window.
pub fn StrPolygonIndex::query_polygons(
  self : StrPolygonIndex,
  window : Rect,
) -> Array[Polygon2D] {
  let polys = self.inner.query_polygons(window)
  let res : Array[Polygon2D] = []
  for i = 0; i < polys.length(); i = i + 1 {
    res.push(Polygon2D::from_tree(polys[i]))
  }
  res
}

///|
/// Hierarchically queries polygons intersecting the window and clips each with Sutherland-Hodgman,
/// returning only valid planar fragments strictly contained inside the window.
pub fn StrPolygonIndex::query_clipped_polygons(
  self : StrPolygonIndex,
  window : Rect,
) -> Array[Polygon2D] {
  let polys = self.inner.query_clipped_polygons(window)
  let res : Array[Polygon2D] = []
  for i = 0; i < polys.length(); i = i + 1 {
    res.push(Polygon2D::from_tree(polys[i]))
  }
  res
}