///|
/// Finds the nearest point in a set, returning its index and distance.
pub fn nearest_point(points : Array[Vec2], target : Vec2) -> (Int, Double)? {
  if points.length() == 0 {
    None
  } else {
    let mut index = 0
    let mut distance = points[0].distance(target)
    for i in 1.. (Int, Double)? {
  if points.length() == 0 {
    None
  } else {
    let mut index = 0
    let mut distance = points[0].distance(target)
    for i in 1.. distance {
        distance = candidate
        index = i
      }
    }
    Some((index, distance))
  }
}

///|
/// Rotates a point cloud around its centroid.
pub fn rotate_around_centroid(
  points : Array[Vec2],
  angle : Double,
) -> Array[Vec2] {
  let box_ = bounds_of_points(points)
  let center = box_.center()
  let result = []
  for point in points {
    result.push(point.rotate(angle, center~))
  }
  result
}

///|
/// Reflects points across a line defined by an origin and a unit direction.
pub fn reflect_points(
  points : Array[Vec2],
  origin : Vec2,
  direction : Vec2,
) -> Array[Vec2] {
  let axis = direction.normalized()
  let result = []
  for point in points {
    let relative = point.sub(origin)
    let projection = axis.scale(relative.dot(axis))
    result.push(origin.add(projection.scale(2.0).sub(relative)))
  }
  result
}

///|
pub fn convex_hull(points : Array[Vec2]) -> Array[Vec2] {
  if points.length() <= 1 {
    points
  } else {
    let sorted = points.copy()
    for i in 0..= 2 &&
            lower[lower.length() - 1]
            .sub(lower[lower.length() - 2])
            .cross(point.sub(lower[lower.length() - 1])) <=
            0.0 {
        let _ = lower.pop()
      }
      lower.push(point)
    }
    let upper : Array[Vec2] = []
    for offset in 1..<=sorted.length() {
      let point = sorted[sorted.length() - offset]
      while upper.length() >= 2 &&
            upper[upper.length() - 1]
            .sub(upper[upper.length() - 2])
            .cross(point.sub(upper[upper.length() - 1])) <=
            0.0 {
        let _ = upper.pop()
      }
      upper.push(point)
    }
    let _ = lower.pop()
    let _ = upper.pop()
    for point in upper {
      lower.push(point)
    }
    lower
  }
}

///|
/// Calculates the signed angle between two vectors.
pub fn signed_angle(left : Vec2, right : Vec2) -> Double {
  @math.atan2(left.cross(right), left.dot(right))
}

///|
pub fn is_convex_polygon(points : Array[Vec2]) -> Bool {
  if points.length() < 3 {
    false
  } else {
    let mut sign = 0
    for i in 0.. 0.000000001 {
        if sign < 0 {
          return false
        }
        sign = 1
      }
      if cross < -0.000000001 {
        if sign > 0 {
          return false
        }
        sign = -1
      }
    }
    true
  }
}