local ____exports = {}
local OBJECT_NAME
local ____SerializationBrand = require("enums.private.SerializationBrand")
local SerializationBrand = ____SerializationBrand.SerializationBrand
local ____direction = require("functions.direction")
local angleToDirection = ____direction.angleToDirection
local ____isaacAPIClass = require("functions.isaacAPIClass")
local isIsaacAPIClassOfType = ____isaacAPIClass.isIsaacAPIClassOfType
local isaacAPIClassEquals = ____isaacAPIClass.isaacAPIClassEquals
local ____random = require("functions.random")
local getRandomFloat = ____random.getRandomFloat
local ____rng = require("functions.rng")
local isRNG = ____rng.isRNG
local newRNG = ____rng.newRNG
local ____table = require("functions.table")
local copyUserdataValuesToTable = ____table.copyUserdataValuesToTable
local getNumbersFromTable = ____table.getNumbersFromTable
local tableHasKeys = ____table.tableHasKeys
local ____types = require("functions.types")
local isTable = ____types.isTable
local ____utils = require("functions.utils")
local assertDefined = ____utils.assertDefined
--- Helper function to check if something is an instantiated `Vector` object.
function ____exports.isVector(self, object)
    return isIsaacAPIClassOfType(nil, object, OBJECT_NAME)
end
OBJECT_NAME = "Vector"
local KEYS = {"X", "Y"}
--- Helper function to copy a `Vector` Isaac API class.
function ____exports.copyVector(self, vector)
    if not ____exports.isVector(nil, vector) then
        error(((("Failed to copy a " .. OBJECT_NAME) .. " object since the provided object was not a userdata ") .. OBJECT_NAME) .. " class.")
    end
    return Vector(vector.X, vector.Y)
end
--- Helper function to convert a `SerializedVector` object to a normal `RNG` object. (This is used by
-- the save data manager when reading data from the "save#.dat" file.)
function ____exports.deserializeVector(self, vector)
    if not isTable(nil, vector) then
        error(("Failed to deserialize a " .. OBJECT_NAME) .. " object since the provided object was not a Lua table.")
    end
    local x, y = table.unpack(
        getNumbersFromTable(
            nil,
            vector,
            OBJECT_NAME,
            table.unpack(KEYS)
        ),
        1,
        2
    )
    assertDefined(nil, x, ("Failed to deserialize a " .. OBJECT_NAME) .. " object since the provided object did not have a value for: X")
    assertDefined(nil, y, ("Failed to deserialize a " .. OBJECT_NAME) .. " object since the provided object did not have a value for: Y")
    return Vector(x, y)
end
--- Helper function to measure a vector to see if it has a non-zero length using a threshold to
-- ignore extremely small values.
-- 
-- Use this function instead of explicitly checking if the length is 0 because vectors in the game
-- are unlikely to ever be exactly set to 0. Instead, they will always have some miniscule length.
-- 
-- @param vector The vector to measure.
-- @param threshold Optional. The threshold from 0 to consider to be a non-zero vector. Default is
-- 0.01.
function ____exports.doesVectorHaveLength(self, vector, threshold)
    if threshold == nil then
        threshold = 0.01
    end
    return vector:Length() >= threshold
end
--- Given an array of vectors, this helper function returns the closest one to a provided reference
-- vector.
-- 
-- @param referenceVector The vector to compare against.
-- @param vectors The array of vectors to look through.
function ____exports.getClosestVectorTo(self, referenceVector, vectors)
    local closestVector
    local closestDistance = math.huge
    for ____, vector in ipairs(vectors) do
        local distance = referenceVector:Distance(vector)
        if distance < closestDistance then
            closestVector = vector
            closestDistance = distance
        end
    end
    return closestVector
end
--- Helper function to get a random vector between (-1, -1) and (1, 1).
-- 
-- To get random vectors with a bigger length, multiply this with a number.
-- 
-- Use this over the `RandomVector` function when you need the vector to be seeded.
-- 
-- If you want to generate an unseeded vector, you must explicitly pass `undefined` to the
-- `seedOrRNG` parameter.
-- 
-- @param seedOrRNG The `Seed` or `RNG` object to use. If an `RNG` object is provided, the
-- `RNG.Next` method will be called. If `undefined` is provided, it will default to
-- a random seed.
function ____exports.getRandomVector(self, seedOrRNG)
    local rng = isRNG(nil, seedOrRNG) and seedOrRNG or newRNG(nil, seedOrRNG)
    local x = getRandomFloat(nil, -1, 1, rng)
    local y = getRandomFloat(nil, -1, 1, rng)
    return Vector(x, y)
end
--- Used to determine is the given table is a serialized `Vector` object created by the `deepCopy`
-- function.
function ____exports.isSerializedVector(self, object)
    if not isTable(nil, object) then
        return false
    end
    return tableHasKeys(
        nil,
        object,
        table.unpack(KEYS)
    ) and object[SerializationBrand.VECTOR] ~= nil
end
--- Helper function to convert a `Vector` object to a `SerializedVector` object. (This is used by the
-- save data manager when writing data from the "save#.dat" file.)
function ____exports.serializeVector(self, vector)
    if not ____exports.isVector(nil, vector) then
        error(((("Failed to serialize a " .. OBJECT_NAME) .. " object since the provided object was not a userdata ") .. OBJECT_NAME) .. " class.")
    end
    local vectorTable = {}
    copyUserdataValuesToTable(nil, vector, KEYS, vectorTable)
    vectorTable[SerializationBrand.VECTOR] = ""
    return vectorTable
end
--- Helper function to compare two vectors for equality.
-- 
-- This function is useful because vectors are not directly comparable. In other words, `Vector(1.2)
-- === Vector(1.2)` will be equal to false.
function ____exports.vectorEquals(self, vector1, vector2)
    return isaacAPIClassEquals(nil, vector1, vector2, KEYS)
end
--- Helper function for finding out which way a vector is pointing.
function ____exports.vectorToDirection(self, vector)
    local angleDegrees = vector:GetAngleDegrees()
    return angleToDirection(nil, angleDegrees)
end
--- Helper function to convert a vector to a string.
-- 
-- @param vector The vector to convert.
-- @param round Optional. If true, will round the vector values to the nearest integer. Default is
-- false.
function ____exports.vectorToString(self, vector, round)
    if round == nil then
        round = false
    end
    local x = round and math.floor(vector.X + 0.5) or vector.X
    local y = round and math.floor(vector.Y + 0.5) or vector.Y
    return ((("(" .. tostring(x)) .. ", ") .. tostring(y)) .. ")"
end
return ____exports
