export class Vector2 {
    public x: number;
    public y: number;

    constructor(x: number, y: number) {
        this.x = x;
        this.y = y;
    }

    public static lerp(a: Vector2, b: Vector2, t: number) {
        return a.multiplyScalar(1 - t).add(b.multiplyScalar(t));
    }

    public add(vector: Vector2): Vector2 {
        return new Vector2(this.x + vector.x, this.y + vector.y);
    }

    public subtract(vector: Vector2): Vector2 {
        return new Vector2(this.x - vector.x, this.y - vector.y);
    }

    public multiplyScalar(scalar: number): Vector2 {
        return new Vector2(this.x * scalar, this.y * scalar);
    }

    public dot(vector: Vector2): number {
        return this.x * vector.x + this.y * vector.y;
    }

    public cross(vector: Vector2): number {
        return this.x * vector.y - this.y * vector.x;
    }

    public magnitude(): number {
        return Math.sqrt(this.x * this.x + this.y * this.y);
    }

    public normalized(): Vector2 {
        const magnitude = this.magnitude();
        if (magnitude === 0) {
            throw new Error("Cannot normalize a vector with magnitude 0");
        }
        return this.multiplyScalar(1 / magnitude);
    }

    public distance(vector: Vector2): number {
        return Math.sqrt((this.x - vector.x) ** 2 + (this.y - vector.y) ** 2);
    }

    public distanceSquared(vector: Vector2): number {
        return (this.x - vector.x) ** 2 + (this.y - vector.y) ** 2;
    }

    public limit(max: number): Vector2 {
        const magnitude = this.magnitude();
        if (magnitude > max) {
            return this.normalized().multiplyScalar(max);
        }
        return this;
    }

    public angle(): number {
        return Math.atan2(this.y, this.x);
    }

    public angleBetween(vector: Vector2): number {
        const dotProd = this.dot(vector);
        const magnitudes = this.magnitude() * vector.magnitude();
        if (magnitudes === 0) {
            throw new Error("Cannot calculate angle with a zero-magnitude vector");
        }
        return Math.acos(dotProd / magnitudes);
    }

    public clone(): Vector2 {
        return new Vector2(this.x, this.y);
    }

    public equals(vector: Vector2): boolean {
        return this.x === vector.x && this.y === vector.y;
    }

    public rotate(angle: number): Vector2 {
        const cos = Math.cos(angle);
        const sin = Math.sin(angle);
        return new Vector2(
            this.x * cos - this.y * sin,
            this.x * sin + this.y * cos
        );
    }
}
