import 'dart:math' as math;

/// A lightweight, immutable 2D Vector/Coordinate system.
///
/// Used for entity positioning, raycasting calculations, and direction vectors.
class Coordinate2D implements Comparable<Coordinate2D> {
  final double x;
  final double y;

  const Coordinate2D(this.x, this.y);

  /// Returns the angle in radians between this coordinate and [other].
  ///
  /// Useful for "Look At" logic or determining steering direction for AI.
  /// Result is between -pi and pi.
  double angleTo(Coordinate2D other) {
    return math.atan2(other.y - y, other.x - x);
  }

  /// Rotates the coordinate around the origin (0,0) by [radians].
  ///
  /// This uses a standard rotation matrix:
  /// x' = x*cos(θ) - y*sin(θ)
  /// y' = x*sin(θ) + y*cos(θ)
  Coordinate2D rotate(double radians) {
    final cos = math.cos(radians);
    final sin = math.sin(radians);
    return Coordinate2D(
      (x * cos) - (y * sin),
      (x * sin) + (y * cos),
    );
  }

  /// Linear Interpolation (LERP): Slides between this and [target] by [t].
  ///
  /// [t] should be a value between 0.0 and 1.0.
  /// Perfect for smooth camera follows or "lerping" an object to a new spot.
  Coordinate2D lerp(Coordinate2D target, double t) {
    return Coordinate2D(
      x + (target.x - x) * t,
      y + (target.y - y) * t,
    );
  }

  /// Returns the length of the vector.
  double get magnitude => math.sqrt(x * x + y * y);

  /// Returns a vector with the same direction but a magnitude of 1.0.
  Coordinate2D get normalized {
    final m = magnitude;
    if (m == 0) return const Coordinate2D(0, 0);
    return Coordinate2D(x / m, y / m);
  }

  /// Calculates the Dot Product between this and [other].
  double dot(Coordinate2D other) => (x * other.x) + (y * other.y);

  /// Calculates the Euclidean distance between two points.
  double distanceTo(Coordinate2D other) => (this - other).magnitude;

  // --- Operator Overloads for Vector Math ---

  Coordinate2D operator +(Coordinate2D other) =>
      Coordinate2D(x + other.x, y + other.y);
  Coordinate2D operator -(Coordinate2D other) =>
      Coordinate2D(x - other.x, y - other.y);
  Coordinate2D operator *(double n) => Coordinate2D(x * n, y * n);
  Coordinate2D operator /(double n) => Coordinate2D(x / n, y / n);

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is Coordinate2D && x == other.x && y == other.y;

  @override
  int get hashCode => Object.hash(x, y);

  @override
  int compareTo(Coordinate2D other) =>
      x != other.x ? x.compareTo(other.x) : y.compareTo(other.y);

  @override
  String toString() => 'Coordinate2D($x, $y)';
}
