///|
/// 2D vector math utilities.
///
/// Reference:
/// - Ebiten vector package (path/shape drawing)
/// - General 2D game math (Vec2 operations)

///|
pub struct Vec2 {
  x : Double
  y : Double
} derive(Debug)

///|
pub impl Show for Vec2 with output(self, logger) {
  logger.write_object(to_repr(self))
}

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

///|
pub fn Vec2::zero() -> Vec2 {
  { x: 0.0, y: 0.0 }
}

///|
pub fn Vec2::one() -> Vec2 {
  { x: 1.0, y: 1.0 }
}

///|
pub fn Vec2::unit_x() -> Vec2 {
  { x: 1.0, y: 0.0 }
}

///|
pub fn Vec2::unit_y() -> Vec2 {
  { x: 0.0, y: 1.0 }
}

///|
pub fn Vec2::add(self : Vec2, other : Vec2) -> Vec2 {
  { x: self.x + other.x, y: self.y + other.y }
}

///|
pub fn Vec2::sub(self : Vec2, other : Vec2) -> Vec2 {
  { x: self.x - other.x, y: self.y - other.y }
}

///|
pub fn Vec2::scale(self : Vec2, s : Double) -> Vec2 {
  { x: self.x * s, y: self.y * s }
}

///|
pub fn Vec2::negate(self : Vec2) -> Vec2 {
  { x: -self.x, y: -self.y }
}

///|
pub fn Vec2::dot(self : Vec2, other : Vec2) -> Double {
  self.x * other.x + self.y * other.y
}

///|
/// 2D cross product (returns scalar z-component).
pub fn Vec2::cross(self : Vec2, other : Vec2) -> Double {
  self.x * other.y - self.y * other.x
}

///|
pub fn Vec2::length_squared(self : Vec2) -> Double {
  self.x * self.x + self.y * self.y
}

///|
pub fn Vec2::length(self : Vec2) -> Double {
  self.length_squared().sqrt()
}

///|
pub fn Vec2::normalize(self : Vec2) -> Vec2 {
  let len = self.length()
  if len < 1.0e-12 {
    Vec2::zero()
  } else {
    self.scale(1.0 / len)
  }
}

///|
pub fn Vec2::distance(self : Vec2, other : Vec2) -> Double {
  self.sub(other).length()
}

///|
pub fn Vec2::distance_squared(self : Vec2, other : Vec2) -> Double {
  self.sub(other).length_squared()
}

///|
pub fn Vec2::lerp(self : Vec2, other : Vec2, t : Double) -> Vec2 {
  { x: self.x + (other.x - self.x) * t, y: self.y + (other.y - self.y) * t }
}

///|
pub fn Vec2::perpendicular(self : Vec2) -> Vec2 {
  { x: -self.y, y: self.x }
}

///|
pub fn Vec2::rotate(self : Vec2, angle_rad : Double) -> Vec2 {
  let cos_a = @math.cos(angle_rad)
  let sin_a = @math.sin(angle_rad)
  { x: self.x * cos_a - self.y * sin_a, y: self.x * sin_a + self.y * cos_a }
}

///|
pub fn Vec2::angle(self : Vec2) -> Double {
  @math.atan2(self.y, self.x)
}

///|
pub fn Vec2::angle_between(self : Vec2, other : Vec2) -> Double {
  let cross = self.cross(other)
  let dot = self.dot(other)
  @math.atan2(cross, dot)
}

///|
pub fn Vec2::min(self : Vec2, other : Vec2) -> Vec2 {
  { x: @cmp.minimum(self.x, other.x), y: @cmp.minimum(self.y, other.y) }
}

///|
pub fn Vec2::max(self : Vec2, other : Vec2) -> Vec2 {
  { x: @cmp.maximum(self.x, other.x), y: @cmp.maximum(self.y, other.y) }
}

///|
pub fn Vec2::clamp(self : Vec2, min : Vec2, max : Vec2) -> Vec2 {
  self.max(min).min(max)
}

///|
pub fn Vec2::approx_eq(self : Vec2, other : Vec2, epsilon : Double) -> Bool {
  (self.x - other.x).abs() < epsilon && (self.y - other.y).abs() < epsilon
}

///|
/// Reflect vector around a normal.
pub fn Vec2::reflect(self : Vec2, normal : Vec2) -> Vec2 {
  self.sub(normal.scale(2.0 * self.dot(normal)))
}

///|
pub fn Vec2::mul(self : Vec2, other : Vec2) -> Vec2 {
  { x: self.x * other.x, y: self.y * other.y }
}

///|
pub fn Vec2::div(self : Vec2, other : Vec2) -> Vec2 {
  { x: self.x / other.x, y: self.y / other.y }
}

// ============================================================
// AABB collision detection
// ============================================================

///|
/// Check if two axis-aligned bounding boxes overlap (top-left + size form).
pub fn aabb_overlap(
  ax : Double,
  ay : Double,
  aw : Double,
  ah : Double,
  bx : Double,
  by : Double,
  bw : Double,
  bh : Double,
) -> Bool {
  ax < bx + bw && ax + aw > bx && ay < by + bh && ay + ah > by
}

///|
/// Check if two axis-aligned bounding boxes overlap (center + half-size form).
pub fn aabb_overlap_center(
  ax : Double,
  ay : Double,
  ahw : Double,
  ahh : Double,
  bx : Double,
  by : Double,
  bhw : Double,
  bhh : Double,
) -> Bool {
  (ax - bx).abs() < ahw + bhw && (ay - by).abs() < ahh + bhh
}