package maths

import (
	"math"

	"github.com/mdiluz/rove/proto/roveapi"
)

// Vector desribes a 3D vector
type Vector struct {
	X int
	Y int
}

// Add adds one vector to another
func (v *Vector) Add(v2 Vector) {
	v.X += v2.X
	v.Y += v2.Y
}

// Added calculates a new vector
func (v Vector) Added(v2 Vector) Vector {
	v.Add(v2)
	return v
}

// Negated returns a negated vector
func (v Vector) Negated() Vector {
	return Vector{-v.X, -v.Y}
}

// Length returns the length of the vector
func (v Vector) Length() float64 {
	return math.Sqrt(float64(v.X*v.X + v.Y*v.Y))
}

// Distance returns the distance between two vectors
func (v Vector) Distance(v2 Vector) float64 {
	// Negate the two vectors and calciate the length
	return v.Added(v2.Negated()).Length()
}

// Multiplied returns the vector multiplied by an int
func (v Vector) Multiplied(val int) Vector {
	return Vector{v.X * val, v.Y * val}
}

// Divided returns the vector divided by an int
func (v Vector) Divided(val int) Vector {
	return Vector{v.X / val, v.Y / val}
}

// DividedFloor returns the vector divided but floors the value regardless
func (v Vector) DividedFloor(val int) Vector {
	x := float64(v.X) / float64(val)

	if x < 0 {
		x = math.Floor(x)
	} else {
		x = math.Floor(x)
	}
	y := float64(v.Y) / float64(val)
	if y < 0 {
		y = math.Floor(y)
	} else {
		y = math.Floor(y)
	}

	return Vector{X: int(x), Y: int(y)}
}

// Abs returns an absolute version of the vector
func (v Vector) Abs() Vector {
	return Vector{Abs(v.X), Abs(v.Y)}
}

// Min2 returns the minimum values in both vectors
func Min2(v1 Vector, v2 Vector) Vector {
	return Vector{Min(v1.X, v2.X), Min(v1.Y, v2.Y)}
}

// Max2 returns the max values in both vectors
func Max2(v1 Vector, v2 Vector) Vector {
	return Vector{Max(v1.X, v2.X), Max(v1.Y, v2.Y)}
}

// BearingToVector converts a bearing to a vector
func BearingToVector(b roveapi.Bearing) Vector {
	switch b {
	case roveapi.Bearing_North:
		return Vector{Y: 1}
	case roveapi.Bearing_NorthEast:
		return Vector{X: 1, Y: 1}
	case roveapi.Bearing_East:
		return Vector{X: 1}
	case roveapi.Bearing_SouthEast:
		return Vector{X: 1, Y: -1}
	case roveapi.Bearing_South:
		return Vector{Y: -1}
	case roveapi.Bearing_SouthWest:
		return Vector{X: -1, Y: -1}
	case roveapi.Bearing_West:
		return Vector{X: -1}
	case roveapi.Bearing_NorthWest:
		return Vector{X: -1, Y: 1}
	}

	return Vector{}
}

// Dot returns the dot product of two vectors
func Dot(a Vector, b Vector) int {
	return a.X*b.X + a.Y*b.Y
}

// AngleCos returns the cosine of the angle between two vectors
func AngleCos(a Vector, b Vector) float64 {
	return float64(Dot(a, b)) / a.Length() * b.Length()
}
