--- @class Vec3
--- @field x number
--- @field y number
--- @field z number
local __Vec3 = {
    x = 0,
    y = 0,
    z = 0,
}

local function __tostring(self)
    return "Vec3{" .. self.x .. ", " .. self.y .. ", " .. self.z .. "}"
end



--- @param other Vec3
--- @return Vec3
function __Vec3:add(other)
    return Vec3 { self.x + other.x, self.y + other.y, self.z + other.z }
end

--- @param factor number
function __Vec3:scale(factor)
    return Vec3 { self.x * factor, self.y * factor, self.z * factor }
end

function __Vec3:length()
    return math.sqrt(self.x ^ 2 + self.y ^ 2 + self.z ^ 2)
end

function __Vec3:normalize()
    local length = self:length()
    if length == 0 then return nil end
    return Vec3 {
        self.x / length,
        self.y / length,
        self.z / length
    }
end

function __Vec3:direction()
    return math.atan2(self.y, self.x)
end

--- @param other Vec3
function __Vec3:dot(other)
    return self.x * other.x + self.y * other.y + self.z * other.z
end

--- @param other Vec3
function __Vec3:angle_to(other)
    return (other - self):direction()
end

function __Vec3:floor()
    return Vec3 { math.floor(self.x), math.floor(self.y), math.floor(self.z) }
end

function __Vec3:invert()
    return self:scale(-1)
end

--- @param other Vec3
function __Vec3:subtract(other)
    return self:add(other:invert())
end

function __Vec3:copy()
    return Vec3 { self.x, self.y, self.z }
end

--- @param other Vec3
function __Vec3:equalsTo(other)
    return
        self.x == other.x
        and self.y == other.y
        and self.z == other.z
end

---Vec3 constructor
---@param vec number[]
function Vec3(vec)
    return setmetatable({
        x = vec[1] or 0,
        y = vec[2] or 0,
        z = vec[3] or 0,

    }, {
        __index = __Vec3,
        __tostring = __tostring,
        __add = __Vec3.add,
        __mul = __Vec3.scale,
        __unm = __Vec3.invert,
        __sub = __Vec3.subtract,
        __eq = __Vec3.equalsTo
    })
end

return Vec3
