// Ported from georust/geo
// https://github.com/georust/geo/blob/main/geo/src/algorithm/covers/

///|
/// `Covers` is like `Contains` but accepts boundary points.
///
/// `covers(a, b)` returns true if every point of `b` lies in `a` (interior
/// **or** boundary).
fn covers_polygon_coord(polygon : @type.Polygon, coord : @type.Coord) -> Bool {
  match coord_position_for_polygon(coord, polygon) {
    Outside => false
    _ => true
  }
}

///|
fn covers_polygon_point(polygon : @type.Polygon, point : @type.Point) -> Bool {
  covers_polygon_coord(polygon, point.coord())
}

///|
fn covers_polygon_line_string(
  polygon : @type.Polygon,
  ls : @type.LineString,
) -> Bool {
  ls.coords().iter().all(fn(c) { covers_polygon_coord(polygon, c) })
}

///|
fn covers_polygon_polygon(outer : @type.Polygon, inner : @type.Polygon) -> Bool {
  inner.exterior().coords().iter().all(fn(c) { covers_polygon_coord(outer, c) })
}

///|
fn covers_rect_coord(rect : @type.Rect, coord : @type.Coord) -> Bool {
  match coord_position_for_rect(coord, rect) {
    Outside => false
    _ => true
  }
}

///|
/// Generic geometry-based covers.
pub fn covers_geometry(a : @type.Geometry, b : @type.Geometry) -> Bool {
  match (a, b) {
    (@type.Geometry::Polygon(p), @type.Geometry::Point(pt)) =>
      covers_polygon_point(p, pt)
    (@type.Geometry::Polygon(p), @type.Geometry::LineString(ls)) =>
      covers_polygon_line_string(p, ls)
    (@type.Geometry::Polygon(o), @type.Geometry::Polygon(i)) =>
      covers_polygon_polygon(o, i)
    (@type.Geometry::Rect(r), @type.Geometry::Point(pt)) =>
      covers_rect_coord(r, pt.coord())
    (@type.Geometry::MultiPolygon(mp), @type.Geometry::Point(pt)) =>
      mp.polygons().iter().any(fn(p) { covers_polygon_point(p, pt) })
    _ => contains_geometry(a, b)
  }
}

// MARK: trait Covers + impls -------------------------------------------------

///|
/// Predicate trait: `self.covers(other)` returns true when every point of
/// `other` lies in `self` (interior **or** boundary).
pub(open) trait Covers {
  fn covers(Self, @type.Geometry) -> Bool
}

///|
pub impl Covers for @type.Geometry with fn covers(self, other) {
  covers_geometry(self, other)
}

///|
pub impl Covers for @type.Polygon with fn covers(self, other) {
  covers_geometry(@type.Geometry::Polygon(self), other)
}

///|
pub impl Covers for @type.MultiPolygon with fn covers(self, other) {
  covers_geometry(@type.Geometry::MultiPolygon(self), other)
}

///|
pub impl Covers for @type.Rect with fn covers(self, other) {
  covers_geometry(@type.Geometry::Rect(self), other)
}

///|
pub impl Covers for @type.Point with fn covers(self, other) {
  covers_geometry(@type.Geometry::Point(self), other)
}

///|
pub impl Covers for @type.Line with fn covers(self, other) {
  covers_geometry(@type.Geometry::Line(self), other)
}

///|
pub impl Covers for @type.LineString with fn covers(self, other) {
  covers_geometry(@type.Geometry::LineString(self), other)
}

///|
pub impl Covers for @type.MultiLineString with fn covers(self, other) {
  covers_geometry(@type.Geometry::MultiLineString(self), other)
}

///|
pub impl Covers for @type.MultiPoint with fn covers(self, other) {
  covers_geometry(@type.Geometry::MultiPoint(self), other)
}

///|
pub impl Covers for @type.Triangle with fn covers(self, other) {
  covers_geometry(@type.Geometry::Triangle(self), other)
}

///|
pub impl Covers for @type.GeometryCollection with fn covers(self, other) {
  covers_geometry(@type.Geometry::GeometryCollection(self), other)
}