"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.vectorToString = exports.vectorToDirection = exports.vectorEquals = exports.serializeVector = exports.isVector = exports.isSerializedVector = exports.getRandomVector = exports.getClosestVectorTo = exports.doesVectorHaveLength = exports.deserializeVector = exports.copyVector = void 0;
const SerializationBrand_1 = require("../enums/private/SerializationBrand");
const direction_1 = require("./direction");
const isaacAPIClass_1 = require("./isaacAPIClass");
const random_1 = require("./random");
const rng_1 = require("./rng");
const table_1 = require("./table");
const types_1 = require("./types");
const utils_1 = require("./utils");
const OBJECT_NAME = "Vector";
const KEYS = ["X", "Y"];
/** Helper function to copy a `Vector` Isaac API class. */
function copyVector(vector) {
    if (!isVector(vector)) {
        error(`Failed to copy a ${OBJECT_NAME} object since the provided object was not a userdata ${OBJECT_NAME} class.`);
    }
    return Vector(vector.X, vector.Y);
}
exports.copyVector = copyVector;
/**
 * 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 deserializeVector(vector) {
    if (!(0, types_1.isTable)(vector)) {
        error(`Failed to deserialize a ${OBJECT_NAME} object since the provided object was not a Lua table.`);
    }
    const [x, y] = (0, table_1.getNumbersFromTable)(vector, OBJECT_NAME, ...KEYS);
    (0, utils_1.assertDefined)(x, `Failed to deserialize a ${OBJECT_NAME} object since the provided object did not have a value for: X`);
    (0, utils_1.assertDefined)(y, `Failed to deserialize a ${OBJECT_NAME} object since the provided object did not have a value for: Y`);
    return Vector(x, y);
}
exports.deserializeVector = deserializeVector;
/**
 * 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 doesVectorHaveLength(vector, threshold = 0.01) {
    return vector.Length() >= threshold;
}
exports.doesVectorHaveLength = doesVectorHaveLength;
/**
 * 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 getClosestVectorTo(referenceVector, vectors) {
    let closestVector;
    let closestDistance = Number.POSITIVE_INFINITY;
    for (const vector of vectors) {
        const distance = referenceVector.Distance(vector);
        if (distance < closestDistance) {
            closestVector = vector;
            closestDistance = distance;
        }
    }
    return closestVector;
}
exports.getClosestVectorTo = getClosestVectorTo;
/**
 * 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 getRandomVector(seedOrRNG) {
    const rng = (0, rng_1.isRNG)(seedOrRNG) ? seedOrRNG : (0, rng_1.newRNG)(seedOrRNG);
    const x = (0, random_1.getRandomFloat)(-1, 1, rng);
    const y = (0, random_1.getRandomFloat)(-1, 1, rng);
    return Vector(x, y);
}
exports.getRandomVector = getRandomVector;
/**
 * Used to determine is the given table is a serialized `Vector` object created by the `deepCopy`
 * function.
 */
function isSerializedVector(object) {
    if (!(0, types_1.isTable)(object)) {
        return false;
    }
    return (0, table_1.tableHasKeys)(object, ...KEYS) && object.has(SerializationBrand_1.SerializationBrand.VECTOR);
}
exports.isSerializedVector = isSerializedVector;
/** Helper function to check if something is an instantiated `Vector` object. */
function isVector(object) {
    return (0, isaacAPIClass_1.isIsaacAPIClassOfType)(object, OBJECT_NAME);
}
exports.isVector = isVector;
/**
 * 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 serializeVector(vector) {
    if (!isVector(vector)) {
        error(`Failed to serialize a ${OBJECT_NAME} object since the provided object was not a userdata ${OBJECT_NAME} class.`);
    }
    const vectorTable = new LuaMap();
    (0, table_1.copyUserdataValuesToTable)(vector, KEYS, vectorTable);
    vectorTable.set(SerializationBrand_1.SerializationBrand.VECTOR, "");
    return vectorTable;
}
exports.serializeVector = serializeVector;
/**
 * 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 vectorEquals(vector1, vector2) {
    return (0, isaacAPIClass_1.isaacAPIClassEquals)(vector1, vector2, KEYS);
}
exports.vectorEquals = vectorEquals;
/** Helper function for finding out which way a vector is pointing. */
function vectorToDirection(vector) {
    const angleDegrees = vector.GetAngleDegrees();
    return (0, direction_1.angleToDirection)(angleDegrees);
}
exports.vectorToDirection = vectorToDirection;
/**
 * 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 vectorToString(vector, round = false) {
    const x = round ? Math.round(vector.X) : vector.X;
    const y = round ? Math.round(vector.Y) : vector.Y;
    return `(${x}, ${y})`;
}
exports.vectorToString = vectorToString;
