local ____lualib = require("lualib_bundle")
local __TS__TypeOf = ____lualib.__TS__TypeOf
local ____exports = {}
local RECOMMENDED_SHIFT_IDX, OBJECT_NAME
local ____cachedClasses = require("core.cachedClasses")
local game = ____cachedClasses.game
local ____SerializationBrand = require("enums.private.SerializationBrand")
local SerializationBrand = ____SerializationBrand.SerializationBrand
local ____isaacAPIClass = require("functions.isaacAPIClass")
local isaacAPIClassEquals = ____isaacAPIClass.isaacAPIClassEquals
local isIsaacAPIClassOfType = ____isaacAPIClass.isIsaacAPIClassOfType
local ____log = require("functions.log")
local logError = ____log.logError
local ____table = require("functions.table")
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 get a random `Seed` value to be used in spawning entities and so on. Use this
-- instead of calling the `Random` function directly since that can return a value of 0 and crash
-- the game.
function ____exports.getRandomSeed(self)
    local randomNumber = Random()
    local safeRandomNumber = randomNumber == 0 and 1 or randomNumber
    return safeRandomNumber
end
--- Helper function to check if something is an instantiated `RNG` object.
function ____exports.isRNG(self, object)
    return isIsaacAPIClassOfType(nil, object, OBJECT_NAME)
end
--- Helper function to initialize a new RNG object using Blade's recommended shift index.
-- 
-- @param seed Optional. The seed to initialize it with. Default is a random seed.
function ____exports.newRNG(self, seed)
    if seed == nil then
        seed = ____exports.getRandomSeed(nil)
    end
    local rng = RNG()
    ____exports.setSeed(nil, rng, seed)
    return rng
end
--- Helper function to set a seed to an RNG object using Blade's recommended shift index.
function ____exports.setSeed(self, rng, seed)
    if seed == 0 then
        seed = ____exports.getRandomSeed(nil)
        logError("Failed to set a RNG object to a seed of 0. Using a random value instead.")
    end
    rng:SetSeed(seed, RECOMMENDED_SHIFT_IDX)
end
RECOMMENDED_SHIFT_IDX = 35
OBJECT_NAME = "RNG"
local KEYS = {"seed"}
--- Helper function to copy an `RNG` Isaac API class.
function ____exports.copyRNG(self, rng)
    if not ____exports.isRNG(nil, rng) then
        error(((("Failed to copy a " .. OBJECT_NAME) .. " object since the provided object was not a userdata ") .. OBJECT_NAME) .. " class.")
    end
    local seed = rng:GetSeed()
    return ____exports.newRNG(nil, seed)
end
--- Helper function to convert a `SerializedRNG` object to a normal `RNG` object. (This is used by
-- the save data manager when reading data from the "save#.dat" file.)
function ____exports.deserializeRNG(self, rng)
    if not isTable(nil, rng) then
        error(("Failed to deserialize a " .. OBJECT_NAME) .. " object since the provided object was not a Lua table.")
    end
    local seed = table.unpack(getNumbersFromTable(
        nil,
        rng,
        OBJECT_NAME,
        table.unpack(KEYS)
    ))
    assertDefined(nil, seed, ("Failed to deserialize a " .. OBJECT_NAME) .. " object since the provided object did not have a value for: seed")
    return ____exports.newRNG(nil, seed)
end
--- Used to determine is the given table is a serialized `RNG` object created by the `deepCopy`
-- function.
function ____exports.isSerializedRNG(self, object)
    if not isTable(nil, object) then
        return false
    end
    return tableHasKeys(
        nil,
        object,
        table.unpack(KEYS)
    ) and object[SerializationBrand.RNG] ~= nil
end
function ____exports.rngEquals(self, rng1, rng2)
    return isaacAPIClassEquals(nil, rng1, rng2, KEYS)
end
--- Helper function to convert a `RNG` object to a `SerializedRNG` object. (This is used by the save
-- data manager when writing data from the "save#.dat" file.)
function ____exports.serializeRNG(self, rng)
    if not ____exports.isRNG(nil, rng) then
        error(((("Failed to serialize a " .. OBJECT_NAME) .. " object since the provided object was not a userdata ") .. OBJECT_NAME) .. " class.")
    end
    local seed = rng:GetSeed()
    local rngTable = {}
    rngTable.seed = seed
    rngTable[SerializationBrand.RNG] = ""
    return rngTable
end
--- Helper function to iterate over the provided object and set the seed for all of the values that
-- are RNG objects equal to a particular seed.
function ____exports.setAllRNGToSeed(self, object, seed)
    if not isTable(nil, object) then
        error("Failed to iterate over the object containing RNG objects since the type of the provided object was: " .. __TS__TypeOf(object))
    end
    local setAtLeastOneSeed = false
    for _key, value in pairs(object) do
        if ____exports.isRNG(nil, value) then
            ____exports.setSeed(nil, value, seed)
            setAtLeastOneSeed = true
        end
    end
    if not setAtLeastOneSeed then
        error(("Failed to set all RNG objects to seed " .. tostring(seed)) .. " because the parent object did not contain any RNG objects.")
    end
end
--- Helper function to iterate over the provided object and set the seed for all of the values that
-- are RNG objects equal to the start seed for the current run.
function ____exports.setAllRNGToStartSeed(self, object)
    local seeds = game:GetSeeds()
    local startSeed = seeds:GetStartSeed()
    ____exports.setAllRNGToSeed(nil, object, startSeed)
end
return ____exports
