// Vec2 is a generic 2D integer vector.
//
// It supports different underlying integer types (e.g., int, uint8) via type
// parameters.
//
// For float-based positions, use rl.Vector2 instead.
package core

import (
	"math"

	rl "github.com/gen2brain/raylib-go/raylib"
	"golang.org/x/exp/constraints"
)

type Vec2[T constraints.Integer] struct {
	X, Y T
}

func NewVec2[T constraints.Integer](x, y T) Vec2[T] {
	return Vec2[T]{
		X: x,
		Y: y,
	}
}

func Vec2From[T constraints.Integer, Y constraints.Integer](v Vec2[T]) Vec2[Y] {
	return Vec2[Y]{
		X: Y(v.X),
		Y: Y(v.Y),
	}
}

func Vec2FromRL[T constraints.Integer](v rl.Vector2) Vec2[T] {
	return Vec2[T]{
		X: T(v.X),
		Y: T(v.Y),
	}
}

func (v1 Vec2[T]) Add(v2 Vec2[T]) Vec2[T] {
	return Vec2[T]{
		X: v1.X + v2.X,
		Y: v1.Y + v2.Y,
	}
}

func (v1 Vec2[T]) Subtract(v2 Vec2[T]) Vec2[T] {
	return Vec2[T]{
		X: v1.X - v2.X,
		Y: v1.Y - v2.Y,
	}
}

func AbsDist[T constraints.Integer](a T, b T) T {
	if a > b {
		return a - b
	}
	return b - a
}

func Abs[T constraints.Integer](value T) T {
	if value < T(0) {
		return -value
	}
	return value
}

func (v1 Vec2[T]) Abs() Vec2[T] {
	return Vec2[T]{
		X: Abs(v1.X),
		Y: Abs(v1.Y),
	}
}

func (v1 Vec2[T]) SubtractABS(v2 Vec2[T]) Vec2[T] {
	return Vec2[T]{
		X: AbsDist(v1.X, v2.X),
		Y: AbsDist(v1.Y, v2.Y),
	}
}

func (v Vec2[T]) Scale(value float32) Vec2[T] {
	return Vec2[T]{
		X: T(float32(v.X) * value),
		Y: T(float32(v.Y) * value),
	}
}

func (v1 Vec2[T]) Distance(v2 Vec2[T]) float64 {
	abs := v1.SubtractABS(v2)
	dx := float64(abs.X)
	dy := float64(abs.Y)
	return math.Sqrt((dx * dx) + (dy * dy))
}

// ToRL converts the Vec2 to a rl.Vector2 (float32), which is compatible
// with rendering and physics systems.
func (v Vec2[T]) ToRL() rl.Vector2 {
	return rl.NewVector2(float32(v.X), float32(v.Y))
}

// AddRL adds a float-based rl.Vector2 to the current Vec2,
// returning a new Vec2 after converting the result back to type T.
// Fractional parts are truncated.
func (v1 Vec2[T]) AddRL(v2 rl.Vector2) Vec2[T] {
	v1.X += T(v2.X)
	v1.Y += T(v2.Y)
	return v1
}

// Common Vec2 type aliases for convenience.
type (
	Vec2i   = Vec2[int]
	Vec2i8  = Vec2[int8]
	Vec2i16 = Vec2[int16]
	Vec2i32 = Vec2[int32]
	Vec2i64 = Vec2[int64]

	Vec2u   = Vec2[uint]
	Vec2u8  = Vec2[uint8]
	Vec2u16 = Vec2[uint16]
	Vec2u32 = Vec2[uint32]
	Vec2u64 = Vec2[uint64]
)
