local ____lualib = require("lualib_bundle")
local __TS__New = ____lualib.__TS__New
local Map = ____lualib.Map
local Set = ____lualib.Set
local __TS__Iterator = ____lualib.__TS__Iterator
local __TS__ArraySort = ____lualib.__TS__ArraySort
local __TS__ArraySome = ____lualib.__TS__ArraySome
local ____exports = {}
local deepCopyTable, deepCopyDefaultMap, getNewDefaultMap, deepCopyMap, deepCopySet, deepCopyTSTLClass, deepCopyArray, deepCopyNormalLuaTable, getCopiedEntries, checkMetatable, deepCopyUserdata
local ____DefaultMap = require("classes.DefaultMap")
local DefaultMap = ____DefaultMap.DefaultMap
local ____constants = require("classes.features.other.saveDataManager.constants")
local SAVE_DATA_MANAGER_DEBUG = ____constants.SAVE_DATA_MANAGER_DEBUG
local ____SerializationBrand = require("enums.private.SerializationBrand")
local SerializationBrand = ____SerializationBrand.SerializationBrand
local ____SerializationType = require("enums.SerializationType")
local SerializationType = ____SerializationType.SerializationType
local ____serialization = require("serialization")
local isSerializationBrand = ____serialization.isSerializationBrand
local ____array = require("functions.array")
local isArray = ____array.isArray
local ____isaacAPIClass = require("functions.isaacAPIClass")
local getIsaacAPIClassName = ____isaacAPIClass.getIsaacAPIClassName
local ____log = require("functions.log")
local log = ____log.log
local ____serialization = require("functions.serialization")
local copyIsaacAPIClass = ____serialization.copyIsaacAPIClass
local deserializeIsaacAPIClass = ____serialization.deserializeIsaacAPIClass
local isCopyableIsaacAPIClass = ____serialization.isCopyableIsaacAPIClass
local isSerializedIsaacAPIClass = ____serialization.isSerializedIsaacAPIClass
local serializeIsaacAPIClass = ____serialization.serializeIsaacAPIClass
local ____sort = require("functions.sort")
local sortTwoDimensionalArray = ____sort.sortTwoDimensionalArray
local ____tstlClass = require("functions.tstlClass")
local getTSTLClassName = ____tstlClass.getTSTLClassName
local isDefaultMap = ____tstlClass.isDefaultMap
local isTSTLMap = ____tstlClass.isTSTLMap
local isTSTLSet = ____tstlClass.isTSTLSet
local newTSTLClass = ____tstlClass.newTSTLClass
local ____types = require("functions.types")
local asString = ____types.asString
local isNumber = ____types.isNumber
local isPrimitive = ____types.isPrimitive
local ____utils = require("functions.utils")
local assertDefined = ____utils.assertDefined
local getTraversalDescription = ____utils.getTraversalDescription
--- `deepCopy` is a semi-generic deep cloner. It will recursively copy all of the values so that none
-- of the nested references remain.
-- 
-- `deepCopy` is used by the IsaacScript save data manager to make a backup of your variables, so
-- that it can restore them to the default values at the beginning of a new room, floor, or run.
-- 
-- `deepCopy` supports the following object types:
-- 
-- - Primitives (i.e. strings, numbers, and booleans)
-- - Basic TSTL objects (which are the same thing as Lua tables)
-- - TSTL `Map`
-- - TSTL `Set`
-- - TSTL classes
-- - `DefaultMap`
-- - Isaac `BitSet128` objects
-- - Isaac `Color` objects
-- - Isaac `KColor` objects
-- - Isaac `RNG` objects
-- - Isaac `Vector` objects
-- 
-- It does not support:
-- - objects with values of `null` (since that transpiles to `nil`)
-- - other Isaac API objects such as `EntityPtr` (that have a type of "userdata")
-- 
-- @param value The primitive or object to copy.
-- @param serializationType Optional. Has 3 possible values. Can copy objects as-is, or can
-- serialize objects to Lua tables, or can deserialize Lua tables to
-- objects. Default is `SerializationType.NONE`.
-- @param traversalDescription Optional. Used to track the current key that we are operating on for
-- debugging purposes. Default is an empty string.
-- @param classConstructors Optional. A Lua table that maps the name of a user-defined TSTL class to
-- its corresponding constructor. If the `deepCopy` function finds any
-- user-defined TSTL classes when recursively iterating through the given
-- object, it will use this map to instantiate a new class. Default is an
-- empty Lua table.
-- @param insideMap Optional. Tracks whether the deep copy function is in the process of recursively
-- copying a TSTL Map. Default is false.
function ____exports.deepCopy(self, value, serializationType, traversalDescription, classConstructors, insideMap)
    if serializationType == nil then
        serializationType = SerializationType.NONE
    end
    if traversalDescription == nil then
        traversalDescription = ""
    end
    if classConstructors == nil then
        classConstructors = {}
    end
    if insideMap == nil then
        insideMap = false
    end
    if SAVE_DATA_MANAGER_DEBUG then
        local logString = "deepCopy is operating on: " .. traversalDescription
        if serializationType == SerializationType.SERIALIZE then
            logString = logString .. " (serializing)"
        elseif serializationType == SerializationType.DESERIALIZE then
            logString = logString .. " (deserializing)"
        end
        logString = logString .. ": " .. tostring(value)
        log(logString)
    end
    local valueType = type(value)
    repeat
        local ____switch6 = valueType
        local ____cond6 = ____switch6 == "nil" or ____switch6 == "boolean" or ____switch6 == "number" or ____switch6 == "string"
        if ____cond6 then
            do
                return value
            end
        end
        ____cond6 = ____cond6 or (____switch6 == "function" or ____switch6 == "thread")
        if ____cond6 then
            do
                if serializationType == SerializationType.SERIALIZE then
                    error((("The deep copy function does not support serialization of \"" .. traversalDescription) .. "\", since it is type: ") .. valueType)
                end
                if serializationType == SerializationType.DESERIALIZE then
                    error((("The deep copy function does not support deserialization of \"" .. traversalDescription) .. "\", since it is type: ") .. valueType)
                end
                return value
            end
        end
        ____cond6 = ____cond6 or ____switch6 == "table"
        if ____cond6 then
            do
                local luaMap = value
                return deepCopyTable(
                    nil,
                    luaMap,
                    serializationType,
                    traversalDescription,
                    classConstructors,
                    insideMap
                )
            end
        end
        ____cond6 = ____cond6 or ____switch6 == "userdata"
        if ____cond6 then
            do
                return deepCopyUserdata(nil, value, serializationType, traversalDescription)
            end
        end
    until true
end
function deepCopyTable(self, luaMap, serializationType, traversalDescription, classConstructors, insideMap)
    if isDefaultMap(nil, luaMap) or luaMap[SerializationBrand.DEFAULT_MAP] ~= nil then
        return deepCopyDefaultMap(
            nil,
            luaMap,
            serializationType,
            traversalDescription,
            classConstructors,
            insideMap
        )
    end
    if isTSTLMap(nil, luaMap) or luaMap[SerializationBrand.MAP] ~= nil then
        return deepCopyMap(
            nil,
            luaMap,
            serializationType,
            traversalDescription,
            classConstructors,
            insideMap
        )
    end
    if isTSTLSet(nil, luaMap) or luaMap[SerializationBrand.SET] ~= nil then
        return deepCopySet(
            nil,
            luaMap,
            serializationType,
            traversalDescription,
            classConstructors,
            insideMap
        )
    end
    local className = getTSTLClassName(nil, luaMap)
    if className == "WeakMap" then
        error("The deep copy function does not support copying the \"WeakMap\" class for: " .. traversalDescription)
    end
    if className == "WeakSet" then
        error("The deep copy function does not support copying the \"WeakSet\" class for: " .. traversalDescription)
    end
    if className ~= nil or luaMap[SerializationBrand.TSTL_CLASS] ~= nil then
        return deepCopyTSTLClass(
            nil,
            luaMap,
            serializationType,
            traversalDescription,
            classConstructors,
            insideMap
        )
    end
    checkMetatable(nil, luaMap, traversalDescription)
    if isSerializedIsaacAPIClass(nil, luaMap) and serializationType == SerializationType.DESERIALIZE then
        return deserializeIsaacAPIClass(nil, luaMap)
    end
    if isArray(nil, luaMap) then
        return deepCopyArray(
            nil,
            luaMap,
            serializationType,
            traversalDescription,
            classConstructors,
            insideMap
        )
    end
    return deepCopyNormalLuaTable(
        nil,
        luaMap,
        serializationType,
        traversalDescription,
        classConstructors,
        insideMap
    )
end
function deepCopyDefaultMap(self, defaultMap, serializationType, traversalDescription, classConstructors, insideMap)
    if SAVE_DATA_MANAGER_DEBUG then
        log("deepCopy is copying a DefaultMap.")
    end
    local ____isDefaultMap_result_0
    if isDefaultMap(nil, defaultMap) then
        ____isDefaultMap_result_0 = defaultMap:getConstructorArg()
    else
        ____isDefaultMap_result_0 = nil
    end
    local constructorArg = ____isDefaultMap_result_0
    if serializationType == SerializationType.SERIALIZE and not isPrimitive(nil, constructorArg) then
        if insideMap then
            error("Failed to deep copy a DefaultMap because it was instantiated with a factory function and was also inside of an array, map, or set. For more information, see: https://isaacscript.github.io/main/gotchas#failed-to-deep-copy-a-defaultmap")
        else
            return deepCopyMap(
                nil,
                defaultMap,
                serializationType,
                traversalDescription,
                classConstructors,
                insideMap
            )
        end
    end
    local newDefaultMap = getNewDefaultMap(
        nil,
        defaultMap,
        serializationType,
        traversalDescription,
        constructorArg
    )
    insideMap = true
    local ____getCopiedEntries_result_1 = getCopiedEntries(
        nil,
        defaultMap,
        serializationType,
        traversalDescription,
        classConstructors,
        insideMap
    )
    local entries = ____getCopiedEntries_result_1.entries
    local convertedNumberKeysToStrings = ____getCopiedEntries_result_1.convertedNumberKeysToStrings
    if convertedNumberKeysToStrings then
        if isDefaultMap(nil, newDefaultMap) then
            newDefaultMap:set(SerializationBrand.OBJECT_WITH_NUMBER_KEYS, "")
        else
            newDefaultMap[SerializationBrand.OBJECT_WITH_NUMBER_KEYS] = ""
        end
    end
    for ____, ____value in ipairs(entries) do
        local key = ____value[1]
        local value = ____value[2]
        if isDefaultMap(nil, newDefaultMap) then
            newDefaultMap:set(key, value)
        else
            newDefaultMap[key] = value
        end
    end
    insideMap = false
    return newDefaultMap
end
function getNewDefaultMap(self, defaultMap, serializationType, traversalDescription, constructorArg)
    repeat
        local ____switch35 = serializationType
        local ____cond35 = ____switch35 == SerializationType.NONE
        if ____cond35 then
            do
                return __TS__New(DefaultMap, constructorArg)
            end
        end
        ____cond35 = ____cond35 or ____switch35 == SerializationType.SERIALIZE
        if ____cond35 then
            do
                local newDefaultMap = {}
                newDefaultMap[SerializationBrand.DEFAULT_MAP] = ""
                newDefaultMap[SerializationBrand.DEFAULT_MAP_VALUE] = constructorArg
                return newDefaultMap
            end
        end
        ____cond35 = ____cond35 or ____switch35 == SerializationType.DESERIALIZE
        if ____cond35 then
            do
                if isDefaultMap(nil, defaultMap) then
                    error(("Failed to deserialize a default map of \"" .. traversalDescription) .. "\", since it was not a Lua table.")
                end
                local defaultMapValue = defaultMap[SerializationBrand.DEFAULT_MAP_VALUE]
                assertDefined(nil, defaultMapValue, (("Failed to deserialize a default map of \"" .. traversalDescription) .. "\", since there was no serialization brand of: ") .. SerializationBrand.DEFAULT_MAP_VALUE)
                return __TS__New(DefaultMap, defaultMapValue)
            end
        end
    until true
end
function deepCopyMap(self, map, serializationType, traversalDescription, classConstructors, insideMap)
    if SAVE_DATA_MANAGER_DEBUG then
        log("deepCopy is copying a Map.")
    end
    local newMap
    if serializationType == SerializationType.SERIALIZE then
        newMap = {}
        newMap[SerializationBrand.MAP] = ""
    else
        newMap = __TS__New(Map)
    end
    insideMap = true
    local ____getCopiedEntries_result_2 = getCopiedEntries(
        nil,
        map,
        serializationType,
        traversalDescription,
        classConstructors,
        insideMap
    )
    local entries = ____getCopiedEntries_result_2.entries
    local convertedNumberKeysToStrings = ____getCopiedEntries_result_2.convertedNumberKeysToStrings
    if convertedNumberKeysToStrings then
        if isTSTLMap(nil, newMap) then
            newMap:set(SerializationBrand.OBJECT_WITH_NUMBER_KEYS, "")
        else
            newMap[SerializationBrand.OBJECT_WITH_NUMBER_KEYS] = ""
        end
    end
    for ____, ____value in ipairs(entries) do
        local key = ____value[1]
        local value = ____value[2]
        if isTSTLMap(nil, newMap) then
            newMap:set(key, value)
        else
            newMap[key] = value
        end
    end
    insideMap = false
    return newMap
end
function deepCopySet(self, set, serializationType, traversalDescription, classConstructors, insideMap)
    if SAVE_DATA_MANAGER_DEBUG then
        log("deepCopy is copying a Set.")
    end
    local newSet
    if serializationType == SerializationType.SERIALIZE then
        newSet = {}
        newSet[SerializationBrand.SET] = ""
    else
        newSet = __TS__New(Set)
    end
    local ____getCopiedEntries_result_3 = getCopiedEntries(
        nil,
        set,
        serializationType,
        traversalDescription,
        classConstructors,
        insideMap
    )
    local entries = ____getCopiedEntries_result_3.entries
    local convertedNumberKeysToStrings = ____getCopiedEntries_result_3.convertedNumberKeysToStrings
    if convertedNumberKeysToStrings then
        if isTSTLSet(nil, newSet) then
            error("The deep copy function cannot convert number keys to strings for a Set.")
        else
            newSet[SerializationBrand.OBJECT_WITH_NUMBER_KEYS] = ""
        end
    end
    for ____, ____value in ipairs(entries) do
        local key = ____value[1]
        if isTSTLSet(nil, newSet) then
            newSet:add(key)
        else
            newSet[key] = ""
        end
    end
    return newSet
end
function deepCopyTSTLClass(self, tstlClass, serializationType, traversalDescription, classConstructors, insideMap)
    if SAVE_DATA_MANAGER_DEBUG then
        log("deepCopy is copying a TSTL class.")
    end
    local newClass
    repeat
        local ____switch64 = serializationType
        local ____cond64 = ____switch64 == SerializationType.NONE
        if ____cond64 then
            do
                newClass = newTSTLClass(nil, tstlClass)
                break
            end
        end
        ____cond64 = ____cond64 or ____switch64 == SerializationType.SERIALIZE
        if ____cond64 then
            do
                newClass = {}
                local tstlClassName = getTSTLClassName(nil, tstlClass)
                if tstlClassName ~= nil then
                    newClass[SerializationBrand.TSTL_CLASS] = tstlClassName
                end
                break
            end
        end
        ____cond64 = ____cond64 or ____switch64 == SerializationType.DESERIALIZE
        if ____cond64 then
            do
                local tstlClassName = tstlClass[SerializationBrand.TSTL_CLASS]
                assertDefined(nil, tstlClassName, "Failed to deserialize a TSTL class since the brand did not contain the class name.")
                local classConstructor = classConstructors[tstlClassName]
                assertDefined(nil, classConstructor, ("Failed to deserialize a TSTL class since there was no constructor registered for a class name of \"" .. tstlClassName) .. "\". If this mod is using the save data manager, it must register the class constructor with the \"saveDataManagerRegisterClass\" method.")
                newClass = __TS__New(classConstructor)
                break
            end
        end
    until true
    local ____getCopiedEntries_result_4 = getCopiedEntries(
        nil,
        tstlClass,
        serializationType,
        traversalDescription,
        classConstructors,
        insideMap
    )
    local entries = ____getCopiedEntries_result_4.entries
    local convertedNumberKeysToStrings = ____getCopiedEntries_result_4.convertedNumberKeysToStrings
    if convertedNumberKeysToStrings then
        newClass[SerializationBrand.OBJECT_WITH_NUMBER_KEYS] = ""
    end
    for ____, ____value in ipairs(entries) do
        local key = ____value[1]
        local value = ____value[2]
        newClass[key] = value
    end
    return newClass
end
function deepCopyArray(self, array, serializationType, traversalDescription, classConstructors, insideMap)
    if SAVE_DATA_MANAGER_DEBUG then
        log("deepCopy is copying an array.")
    end
    local newArray = {}
    for ____, value in ipairs(array) do
        local newValue = ____exports.deepCopy(
            nil,
            value,
            serializationType,
            traversalDescription,
            classConstructors,
            insideMap
        )
        newArray[#newArray + 1] = newValue
    end
    return newArray
end
function deepCopyNormalLuaTable(self, luaMap, serializationType, traversalDescription, classConstructors, insideMap)
    if SAVE_DATA_MANAGER_DEBUG then
        log("deepCopy is copying a normal Lua table.")
    end
    local newTable = {}
    local ____getCopiedEntries_result_5 = getCopiedEntries(
        nil,
        luaMap,
        serializationType,
        traversalDescription,
        classConstructors,
        insideMap
    )
    local entries = ____getCopiedEntries_result_5.entries
    local convertedNumberKeysToStrings = ____getCopiedEntries_result_5.convertedNumberKeysToStrings
    if convertedNumberKeysToStrings then
        newTable[SerializationBrand.OBJECT_WITH_NUMBER_KEYS] = ""
    end
    for ____, ____value in ipairs(entries) do
        local key = ____value[1]
        local value = ____value[2]
        newTable[key] = value
    end
    return newTable
end
function getCopiedEntries(self, object, serializationType, traversalDescription, classConstructors, insideMap)
    local entries = {}
    if isTSTLMap(nil, object) or isTSTLSet(nil, object) or isDefaultMap(nil, object) then
        for ____, ____value in __TS__Iterator(object:entries()) do
            local key = ____value[1]
            local value = ____value[2]
            entries[#entries + 1] = {key, value}
        end
    else
        for key, value in pairs(object) do
            entries[#entries + 1] = {key, value}
        end
    end
    if SAVE_DATA_MANAGER_DEBUG then
        __TS__ArraySort(entries, sortTwoDimensionalArray)
    end
    local convertStringKeysToNumbers = serializationType == SerializationType.DESERIALIZE and __TS__ArraySome(
        entries,
        function(____, ____bindingPattern0)
            local key
            key = ____bindingPattern0[1]
            return key == asString(nil, SerializationBrand.OBJECT_WITH_NUMBER_KEYS)
        end
    )
    local hasNumberKeys = __TS__ArraySome(
        entries,
        function(____, ____bindingPattern0)
            local key
            key = ____bindingPattern0[1]
            return isNumber(nil, key)
        end
    )
    local convertNumberKeysToStrings = serializationType == SerializationType.SERIALIZE and hasNumberKeys
    local copiedEntries = {}
    for ____, ____value in ipairs(entries) do
        local key = ____value[1]
        local value = ____value[2]
        do
            if isSerializationBrand(nil, key) then
                goto __continue90
            end
            traversalDescription = getTraversalDescription(nil, key, traversalDescription)
            local newValue = ____exports.deepCopy(
                nil,
                value,
                serializationType,
                traversalDescription,
                classConstructors,
                insideMap
            )
            local keyToUse = key
            if convertStringKeysToNumbers then
                local numberKey = tonumber(key)
                if numberKey ~= nil then
                    keyToUse = numberKey
                end
            end
            if convertNumberKeysToStrings then
                keyToUse = tostring(key)
            end
            copiedEntries[#copiedEntries + 1] = {keyToUse, newValue}
        end
        ::__continue90::
    end
    return {entries = copiedEntries, convertedNumberKeysToStrings = convertNumberKeysToStrings}
end
function checkMetatable(self, luaMap, traversalDescription)
    local metatable = getmetatable(luaMap)
    if metatable == nil then
        return
    end
    local tableDescription = traversalDescription == "" and "the table to copy" or ("\"" .. traversalDescription) .. "\""
    error(("The deepCopy function detected that " .. tableDescription) .. " has a metatable. Copying tables with metatables is not supported, unless they are explicitly handled by the save data manager. (e.g. TypeScriptToLua Maps, TypeScriptToLua Sets, etc.)")
end
function deepCopyUserdata(self, value, serializationType, traversalDescription)
    if not isCopyableIsaacAPIClass(nil, value) then
        local className = getIsaacAPIClassName(nil, value) or "Unknown"
        error((("The deep copy function does not support serializing \"" .. traversalDescription) .. "\", since it is an Isaac API class of type: ") .. className)
    end
    repeat
        local ____switch100 = serializationType
        local ____cond100 = ____switch100 == SerializationType.NONE
        if ____cond100 then
            do
                return copyIsaacAPIClass(nil, value)
            end
        end
        ____cond100 = ____cond100 or ____switch100 == SerializationType.SERIALIZE
        if ____cond100 then
            do
                return serializeIsaacAPIClass(nil, value)
            end
        end
        ____cond100 = ____cond100 or ____switch100 == SerializationType.DESERIALIZE
        if ____cond100 then
            do
                return error(("The deep copy function can not deserialize \"" .. traversalDescription) .. "\", since it is userdata.")
            end
        end
    until true
end
return ____exports
