"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.tableHasKeys = exports.iterateTableInOrder = exports.isTableEmpty = exports.getStringsFromTable = exports.getNumbersFromTable = exports.getBooleansFromTable = exports.copyUserdataValuesToTable = exports.clearTable = void 0;
const types_1 = require("./types");
const utils_1 = require("./utils");
/**
 * In a `Map`, you can use the `clear` method to delete every element. However, in a `LuaMap`, the
 * `clear` method does not exist. Use this helper function as a drop-in replacement for this.
 */
function clearTable(luaMap) {
    for (const [key] of luaMap) {
        luaMap.delete(key);
    }
}
exports.clearTable = clearTable;
/** Helper function to copy specific values from a userdata object (e.g. `Vector`) to a table. */
function copyUserdataValuesToTable(object, keys, luaMap) {
    if (!(0, types_1.isUserdata)(object)) {
        error(`Failed to copy an object values to a table, since the object was of type: ${type(object)}`);
    }
    // We can access values on userdata objects similar to a normal table.
    const userdata = object;
    for (const key of keys) {
        const value = userdata.get(key);
        luaMap.set(key, value);
    }
}
exports.copyUserdataValuesToTable = copyUserdataValuesToTable;
/**
 * Helper function to safely get boolean values from a Lua table. Will throw an error if the
 * specific value does not exist on the table.
 *
 * This function is variadic, meaning that you can specify N arguments to get N values.
 */
function getBooleansFromTable(luaMap, objectName, ...keys) {
    const booleans = [];
    for (const key of keys) {
        const value = luaMap.get(key);
        (0, utils_1.assertDefined)(value, `Failed to find a value for "${key}" in a table representing a "${objectName}" object.`);
        if ((0, types_1.isBoolean)(value)) {
            booleans.push(value);
        }
        else {
            error(`Failed to get the boolean for the "${key}" value of a table representing a "${objectName}" object because the type was: ${typeof value}`);
        }
    }
    return booleans;
}
exports.getBooleansFromTable = getBooleansFromTable;
/**
 * Helper function to safely get number values from specific keys on a Lua table. If the values are
 * strings, they will be converted to numbers. Will throw an error if the specific value does not
 * exist on the table or if it cannot be converted to a number.
 *
 * This function is variadic, meaning that you can specify N arguments to get N values.
 */
function getNumbersFromTable(luaMap, objectName, ...keys) {
    const numbers = [];
    for (const key of keys) {
        const value = luaMap.get(key);
        (0, utils_1.assertDefined)(value, `Failed to find a value for "${key}" in a table representing a "${objectName}" object.`);
        if ((0, types_1.isNumber)(value)) {
            numbers.push(value);
        }
        else if ((0, types_1.isString)(value)) {
            const number = tonumber(value);
            (0, utils_1.assertDefined)(number, `Failed to convert the "${key}" value of a table representing a "${objectName}" object to a number: ${value}`);
            numbers.push(number);
        }
        else {
            error(`Failed to get the number for the "${key}" value of a table representing a "${objectName}" object because the type was: ${typeof value}`);
        }
    }
    return numbers;
}
exports.getNumbersFromTable = getNumbersFromTable;
/**
 * Helper function to safely get string values from a Lua table. Will throw an error if the specific
 * value does not exist on the table.
 *
 * This function is variadic, meaning that you can specify N arguments to get N values.
 */
function getStringsFromTable(luaMap, objectName, ...keys) {
    const strings = [];
    for (const key of keys) {
        const value = luaMap.get(key);
        (0, utils_1.assertDefined)(value, `Failed to find a value for "${key}" in a table representing a "${objectName}" object.`);
        if ((0, types_1.isString)(value)) {
            strings.push(value);
        }
        else {
            const string = tostring(value);
            strings.push(string);
        }
    }
    return strings;
}
exports.getStringsFromTable = getStringsFromTable;
/** Helper function to check if a Lua table has 0 keys. */
function isTableEmpty(luaMap) {
    // Using `next` does not seem to work properly, so we use `pairs` instead.
    // eslint-disable-next-line no-unreachable-loop
    for (const [_key, _value] of luaMap) {
        return false;
    }
    return true;
}
exports.isTableEmpty = isTableEmpty;
/**
 * Helper function to iterate over a table deterministically. This is useful because by default, the
 * `pairs` function will return the keys of a Lua table in a random order.
 *
 * This function will sort the table entries based on the value of the key.
 *
 * This function will only work on tables that have number keys or string keys. It will throw a
 * run-time error if it encounters a key of another type.
 *
 * @param luaMap The table to iterate over.
 * @param func The function to run for each iteration.
 * @param inOrder Optional. Whether to iterate in order. True by default. You can dynamically set to
 *                false in situations where iterating randomly would not matter and you need the
 *                extra performance.
 */
function iterateTableInOrder(luaMap, func, inOrder = true) {
    // First, handle the trivial case of a non-deterministic iteration.
    if (!inOrder) {
        for (const [key, value] of luaMap) {
            func(key, value);
        }
        return;
    }
    const keys = Object.keys(luaMap);
    const hasAllNumberKeys = keys.every((key) => (0, types_1.isNumber)(key));
    const hasAllStringKeys = keys.every((key) => (0, types_1.isString)(key));
    if (!hasAllNumberKeys && !hasAllStringKeys) {
        // Since the table has non-homogenous keys, we won't be able to sort it. Revert to
        // non-deterministic iteration in this case.
        for (const [key, value] of luaMap) {
            func(key, value);
        }
        return;
    }
    keys.sort();
    for (const key of keys) {
        const keyIndex = key;
        const value = luaMap.get(keyIndex);
        if (value !== undefined) {
            func(keyIndex, value);
        }
    }
}
exports.iterateTableInOrder = iterateTableInOrder;
/**
 * Helper function to check if a Lua table has all of the provided keys.
 *
 * This function is variadic, meaning that you can specify as many arguments as you want to check
 * for.
 */
function tableHasKeys(luaMap, ...keys) {
    return keys.every((key) => luaMap.has(key));
}
exports.tableHasKeys = tableHasKeys;
