#pragma once

#include <types/base/types.h>
#include <cmath>

namespace extra2d {

struct Vec2 {
    float x, y;

    Vec2() : x(0.0f), y(0.0f) {}
    Vec2(float x, float y) : x(x), y(y) {}
    explicit Vec2(float v) : x(v), y(v) {}

    Vec2 operator+(const Vec2& o) const { return {x + o.x, y + o.y}; }
    Vec2 operator-(const Vec2& o) const { return {x - o.x, y - o.y}; }
    Vec2 operator*(float s) const { return {x * s, y * s}; }
    Vec2 operator/(float s) const { return {x / s, y / s}; }
    Vec2 operator*(const Vec2& o) const { return {x * o.x, y * o.y}; }
    Vec2 operator/(const Vec2& o) const { return {x / o.x, y / o.y}; }

    Vec2& operator+=(const Vec2& o) { x += o.x; y += o.y; return *this; }
    Vec2& operator-=(const Vec2& o) { x -= o.x; y -= o.y; return *this; }
    Vec2& operator*=(float s) { x *= s; y *= s; return *this; }
    Vec2& operator/=(float s) { x /= s; y /= s; return *this; }

    Vec2 operator-() const { return {-x, -y}; }

    bool operator==(const Vec2& o) const { return x == o.x && y == o.y; }
    bool operator!=(const Vec2& o) const { return !(*this == o); }

    float len() const { return std::sqrt(x * x + y * y); }
    float lenSq() const { return x * x + y * y; }

    Vec2 norm() const {
        float l = len();
        return l > 0.0f ? *this / l : Vec2(0.0f);
    }

    void normalize() {
        float l = len();
        if (l > 0.0f) { x /= l; y /= l; }
    }

    float dot(const Vec2& o) const { return x * o.x + y * o.y; }
    float cross(const Vec2& o) const { return x * o.y - y * o.x; }

    static float dist(const Vec2& a, const Vec2& b) { return (a - b).len(); }
    static Vec2 lerp(const Vec2& a, const Vec2& b, float t) {
        return {a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t};
    }

    static const Vec2 Zero;
    static const Vec2 One;
    static const Vec2 UnitX;
    static const Vec2 UnitY;
};

inline Vec2 operator*(float s, const Vec2& v) { return v * s; }

} // namespace extra2d
