///|
/// Vec2 represents a 2D vector.
pub(all) struct Vec2 {
  mut x : Double
  mut y : Double
} derive(Show, Eq, FromJson, ToJson)

///|
/// `DEFAULT_TOLERANCE` is the default tolerance used for floating point comparisons.
pub const DEFAULT_TOLERANCE = 0.001

///|
/// Vec2::new returns a new Vec2.
pub fn Vec2::new(x? : Double = 0, y? : Double = 0) -> Vec2 {
  { x, y }
}

///|
/// Vec::infinity returns a new Vec2 with infinite x and y.
pub fn Vec2::infinity() -> Vec2 {
  { x: @double.infinity, y: @double.infinity }
}

///|
/// Vec::neg_infinity returns a new Vec2 with negative infinite x and y.
pub fn Vec2::neg_infinity() -> Vec2 {
  { x: @double.neg_infinity, y: @double.neg_infinity }
}

///|
/// vec2 returns a new Vec2.
pub fn vec2(x : Double, y : Double) -> Vec2 {
  { x, y }
}

///|
/// Vec2::from_angle returns a new unit vec2 from an angle in degrees.
pub fn Vec2::from_angle(angle : Double) -> Vec2 {
  let rad = angle * @math.PI / 180
  Vec2::from_angle_radians(rad)
}

///|
/// Vec2::from_angle_radians returns a new unit vec2 from an angle in radians.
pub fn Vec2::from_angle_radians(rad : Double) -> Vec2 {
  let x = @math.cos(rad)
  let y = @math.sin(rad)
  { x, y }
}

///|
/// clone returns a new copy of this Vec2.
pub fn Vec2::clone(self : Vec2) -> Vec2 {
  { x: self.x, y: self.y }
}

///|
/// set sets the x and y components of this Vec2.
pub fn Vec2::set(self : Vec2, x : Double, y : Double) -> Unit {
  self.x = x
  self.y = y
}

///|
/// copy copies `v` into this Vec2.
pub fn Vec2::copy(self : Vec2, v : Vec2) -> Unit {
  self.x = v.x
  self.y = v.y
}

///|
/// is_valid returns whether or not this Vec2 is valid.
pub fn Vec2::is_valid(self : Vec2) -> Bool {
  not(self.x.is_inf() || self.x.is_nan() || self.y.is_inf() || self.y.is_nan())
}

///|
/// affine_transform transforms this Vec2 by the `affine_matrix`.
/// This is used when transforming a point or position.
pub fn Vec2::affine_transform(
  self : Vec2,
  affine_matrix : AffineMatrix,
) -> Vec2 {
  let { x: x0, y: y0 } = self
  let { a, b, c, d, tx, ty } = affine_matrix
  let x = a * x0 + c * y0 + tx
  let y = b * x0 + d * y0 + ty
  { x, y }
}

///|
/// affine_transform_without_translation transforms this Vec2 by the `affine_matrix`
/// but without performing translation.
/// This is used when transforming a normal or tangent.
pub fn Vec2::affine_transform_without_translation(
  self : Vec2,
  affine_matrix : AffineMatrix,
) -> Vec2 {
  let { x: x0, y: y0 } = self
  let { a, b, c, d, .. } = affine_matrix
  let x = a * x0 + c * y0
  let y = b * x0 + d * y0
  { x, y }
}

///|
/// transform provides a convenient API for a common task.
pub fn Vec2::transform(
  self : Vec2,
  position? : Vec2 = vec2(0, 0),
  rotation? : Double = 0,
  scale? : Vec2 = vec2(1, 1),
  skew? : Double = 0,
  origin? : Vec2 = vec2(0, 0),
) -> Vec2 {
  let affine_matrix = AffineMatrix::from_transform(
    Transform::new(position~, rotation~, scale~, skew~, origin~),
  )
  self.affine_transform(affine_matrix)
}

///|
/// self_add adds the vector `v` to this vector.
pub fn Vec2::self_add(self : Vec2, v : Vec2) -> Unit {
  self.x += v.x
  self.y += v.y
}

///|
/// add adds vector `other` to this vector without modifying either one
/// and returns the result.
pub impl Add for Vec2 with add(self, other) {
  let result = self.clone()
  result.self_add(other)
  result
}

///|
/// add_scalar adds scalar `s` to self and returns a new vector.
pub fn Vec2::add_scalar(self : Vec2, s : Double) -> Vec2 {
  vec2(self.x + s, self.y + s)
}

///|
/// self_add_scalar adds scalar `s` to this vector.
pub fn Vec2::self_add_scalar(self : Vec2, s : Double) -> Unit {
  self.x += s
  self.y += s
}

///|
/// self_sub subtracts the vector `v` from this vector.
pub fn Vec2::self_sub(self : Vec2, v : Vec2) -> Unit {
  self.x -= v.x
  self.y -= v.y
}

///|
/// sub subtracts vector `other` from this vector without modifying either one
/// and returns the result.
pub impl Sub for Vec2 with sub(self, other) {
  let result = self.clone()
  result.self_sub(other)
  result
}

///|
/// self_sub_scalar subtracts scalar `s` from this vector.
pub fn Vec2::self_sub_scalar(self : Vec2, s : Double) -> Unit {
  self.x -= s
  self.y -= s
}

///|
/// self_mul multiplies the vector `v` to this vector.
pub fn Vec2::self_mul(self : Vec2, v : Vec2) -> Unit {
  self.x *= v.x
  self.y *= v.y
}

///|
/// mul multiplies vector `other` to this vector without modifying either one
/// and returns the result.
pub impl Mul for Vec2 with mul(self, other) {
  let result = self.clone()
  result.self_mul(other)
  result
}

///|
/// mul_scalar multiplies scalar `s` to self and returns a new vector.
pub fn Vec2::mul_scalar(self : Vec2, s : Double) -> Vec2 {
  vec2(self.x * s, self.y * s)
}

///|
/// self_mul_scalar multiplies scalar `s` to this vector.
pub fn Vec2::self_mul_scalar(self : Vec2, s : Double) -> Unit {
  self.x *= s
  self.y *= s
}

///|
/// self_div divides this vector by vector `v`.
pub fn Vec2::self_div(self : Vec2, v : Vec2) -> Unit {
  self.x /= v.x
  self.y /= v.y
}

///|
/// div divides this vector by vector `other` without modifying either one
/// and returns the result.
pub impl Div for Vec2 with div(self, other) {
  let result = self.clone()
  result.self_div(other)
  result
}

///|
/// self_div_scalar divides this vector by scalar `s`.
pub fn Vec2::self_div_scalar(self : Vec2, s : Double) -> Unit {
  self.x /= s
  self.y /= s
}

///|
/// negate multiplies both components (in-place) by -1.
pub fn Vec2::negate(self : Vec2) -> Unit {
  self.x *= -1
  self.y *= -1
}

///|
/// neg returns a negated copy of Vec2.
pub impl Neg for Vec2 with neg(self) {
  { x: -self.x, y: -self.y }
}

///|
fn almost_equal(v1 : Double, v2 : Double, tolerance : Double) -> Bool {
  let delta = (v2 - v1).abs()
  if delta <= tolerance {
    return true
  }
  let v1 = v1.abs()
  let v2 = v2.abs()
  delta <= @cmp.maximum(v1, v2) * tolerance
}

///|
/// almost_equals returns true if the vectors are equal within the provided tolerance.
pub fn Vec2::almost_equals(
  self : Vec2,
  other : Vec2,
  tolerance? : Double = DEFAULT_TOLERANCE,
) -> Bool {
  almost_equal(self.x, other.x, tolerance) &&
  almost_equal(self.y, other.y, tolerance)
}

///|
/// apply applies the provided `func` to both components of this vector.
pub fn Vec2::apply(self : Vec2, func : (Double) -> Double) -> Vec2 {
  self.x = func(self.x)
  self.y = func(self.y)
  self
}

///|
/// floor rounds the components of this vector to the next-lower integer.
pub fn Vec2::floor(self : Vec2) -> Vec2 {
  self.apply(@math.floor)
}

///|
/// ceil rounds the components of this vector to the next-higher integer.
pub fn Vec2::ceil(self : Vec2) -> Vec2 {
  self.apply(@math.ceil)
}

///|
/// round rounds the components of this vector to the next-higher integer.
pub fn Vec2::round(self : Vec2) -> Vec2 {
  self.apply(@math.round)
}

///|
/// round_to_fixed rounds the components of this vector to the provided
/// number of digits.
pub fn Vec2::round_to_fixed(self : Vec2, digits : Int) -> Vec2 {
  self.x = round_to_fixed(self.x, digits)
  self.y = round_to_fixed(self.y, digits)
  self
}

///|
/// round_to_multiple rounds the components of this vector to the closes
/// multiple of `v`.
pub fn Vec2::round_to_multiple(self : Vec2, v : Double) -> Vec2 {
  if 0.0 == v {
    return self
  }
  let t = 1.0 / v
  self.x = @math.round(self.x * t) * v
  self.y = @math.round(self.y * t) * v
  self
}

///|
/// min compares the components of this vector and `v` and sets this vector's
/// components to the minimum of the two.
pub fn Vec2::min(self : Vec2, v : Vec2) -> Unit {
  self.x = @cmp.minimum(self.x, v.x)
  self.y = @cmp.minimum(self.y, v.y)
}

///|
/// max compares the components of this vector and `v` and sets this vector's
/// components to the maximum of the two.
pub fn Vec2::max(self : Vec2, v : Vec2) -> Unit {
  self.x = @cmp.maximum(self.x, v.x)
  self.y = @cmp.maximum(self.y, v.y)
}

///|
/// mix linearly interpolates this vector to the vector `v` by the mixing
/// factor `t` (0..1).
pub fn Vec2::mix(self : Vec2, v : Vec2, t : Double) -> Vec2 {
  self.x += (v.x - self.x) * t
  self.y += (v.y - self.y) * t
  self
}

///|
/// dot returns the dot product between this vector and the vector `v`.
pub fn Vec2::dot(self : Vec2, v : Vec2) -> Double {
  self.x * v.x + self.y * v.y
}

///|
/// cross returns the cross product between this vector and the vector `v`.
pub fn Vec2::cross(self : Vec2, v : Vec2) -> Double {
  self.x * v.y - self.y * v.x
}

///|
/// normalize scales this vector so that its length is 1.
/// Note that this vector must already have a non-zero length.
pub fn Vec2::normalize(self : Vec2) -> Unit {
  let v = self.length_squared()
  if v > 0 {
    self.self_mul_scalar(1.0 / v.sqrt())
  }
}

///|
/// rotate rotates this vector clockwise by `angle` in degrees.
pub fn Vec2::rotate(self : Vec2, angle : Double) -> Vec2 {
  let rad = angle * @math.PI / 180
  self.rotate_radians(rad)
}

///|
/// rotate_radians rotates this vector clockwise by `rad` in radians.
pub fn Vec2::rotate_radians(self : Vec2, rad : Double) -> Vec2 {
  let rx = @math.cos(rad)
  let ry = @math.sin(rad)
  let { x: x0, y: y0 } = self
  self.x = x0 * rx - y0 * ry
  self.y = x0 * ry + y0 * rx
  self
}

///|
/// rotate90 rotates this vector clockwise by 90°.
pub fn Vec2::rotate90(self : Vec2) -> Vec2 {
  let { x: x0, y: y0 } = self
  self.x = -y0
  self.y = x0
  self
}

///|
/// rotate_neg90 rotates this vector counter-clockwise by 90°.
pub fn Vec2::rotate_neg90(self : Vec2) -> Vec2 {
  let { x: x0, y: y0 } = self
  self.x = y0
  self.y = -x0
  self
}

///|
/// project_onto projects this vector onto a non-zero vector `v`.
pub fn Vec2::project_onto(self : Vec2, v : Vec2) -> Vec2 {
  let t = v.length_squared()
  if t > 0 {
    let n = self.dot(v) / t
    self.x = v.x * n
    self.y = v.y * n
  }
  self
}

///|
/// angle returns the angle of this vector in degrees.
pub fn Vec2::angle(self : Vec2) -> Double {
  self.angle_radians() * 180 / @math.PI
}

///|
/// angle_radians returns the angle of this vector in radians.
pub fn Vec2::angle_radians(self : Vec2) -> Double {
  @math.atan2(self.y, self.x)
}

///|
/// is_clockwise_from returns true if this vector lies in the 180° region
/// clockwise from `v`.
pub fn Vec2::is_clockwise_from(self : Vec2, v : Vec2) -> Bool {
  self.cross(v) > 0
}

///|
/// length returns the length of this vector.
pub fn Vec2::length(self : Vec2) -> Double {
  self.length_squared().sqrt()
}

///|
/// length_squared returns the squared length of this vector.
pub fn Vec2::length_squared(self : Vec2) -> Double {
  self.x * self.x + self.y * self.y
}

///|
/// distance returns the distance from this vector to `v`.
pub fn Vec2::distance(self : Vec2, v : Vec2) -> Double {
  self.distance_squared(v).sqrt()
}

///|
/// distance_squared returns the squared distance from this vector to `v`.
pub fn Vec2::distance_squared(self : Vec2, v : Vec2) -> Double {
  let x = self.x - v.x
  let y = self.y - v.y
  x * x + y * y
}

///|
/// is_zero returns true if both components of this vector are 0.
pub fn Vec2::is_zero(self : Vec2) -> Bool {
  self.x == 0 && self.y == 0
}

///|
/// is_nan returns true if either component is NaN (not a number).
pub fn Vec2::is_nan(self : Vec2) -> Bool {
  self.x.is_nan() || self.y.is_nan()
}

///|
/// is_inf returns true if either component is infinite.
pub fn Vec2::is_inf(self : Vec2) -> Bool {
  self.x.is_inf() || self.y.is_inf()
}

///|
/// to_tuple returns this Vec2 as a (Double, Double) tuple.
pub fn Vec2::to_tuple(self : Vec2) -> (Double, Double) {
  (self.x, self.y)
}

///|
/// from_tuple creates a Vec2 from a tuple.
pub fn Vec2::from_tuple(t : (Double, Double)) -> Vec2 {
  Vec2::new(x=t.0, y=t.1)
}

///|
/// distance_squared_tuple returns the squared distance between two tuples.
pub fn Vec2::distance_squared_tuple(
  a : (Double, Double),
  b : (Double, Double),
) -> Double {
  Vec2::distance_squared(Vec2::from_tuple(a), Vec2::from_tuple(b))
}

///|
pub fn Vec2::is_in(self : Vec2, box : BoundingBox) -> Bool {
  box.contains_point(self)
}