local ____lualib = require("lualib_bundle")
local __TS__Spread = ____lualib.__TS__Spread
local __TS__ArraySort = ____lualib.__TS__ArraySort
local __TS__ArrayEntries = ____lualib.__TS__ArrayEntries
local __TS__Iterator = ____lualib.__TS__Iterator
local __TS__TypeOf = ____lualib.__TS__TypeOf
local __TS__ObjectKeys = ____lualib.__TS__ObjectKeys
local __TS__New = ____lualib.__TS__New
local Set = ____lualib.Set
local ____exports = {}
local ____isaac_2Dtypescript_2Ddefinitions = require("isaac-typescript-definitions")
local BossID = ____isaac_2Dtypescript_2Ddefinitions.BossID
local DamageFlag = ____isaac_2Dtypescript_2Ddefinitions.DamageFlag
local DisplayFlag = ____isaac_2Dtypescript_2Ddefinitions.DisplayFlag
local EntityFlag = ____isaac_2Dtypescript_2Ddefinitions.EntityFlag
local GameStateFlag = ____isaac_2Dtypescript_2Ddefinitions.GameStateFlag
local GridRoom = ____isaac_2Dtypescript_2Ddefinitions.GridRoom
local HeartSubType = ____isaac_2Dtypescript_2Ddefinitions.HeartSubType
local LevelStateFlag = ____isaac_2Dtypescript_2Ddefinitions.LevelStateFlag
local Music = ____isaac_2Dtypescript_2Ddefinitions.Music
local ProjectileFlag = ____isaac_2Dtypescript_2Ddefinitions.ProjectileFlag
local RoomType = ____isaac_2Dtypescript_2Ddefinitions.RoomType
local SeedEffect = ____isaac_2Dtypescript_2Ddefinitions.SeedEffect
local SoundEffect = ____isaac_2Dtypescript_2Ddefinitions.SoundEffect
local StageID = ____isaac_2Dtypescript_2Ddefinitions.StageID
local TearFlag = ____isaac_2Dtypescript_2Ddefinitions.TearFlag
local UseFlag = ____isaac_2Dtypescript_2Ddefinitions.UseFlag
local ____cachedClasses = require("core.cachedClasses")
local game = ____cachedClasses.game
local musicManager = ____cachedClasses.musicManager
local sfxManager = ____cachedClasses.sfxManager
local ____ReadonlySet = require("types.ReadonlySet")
local ReadonlySet = ____ReadonlySet.ReadonlySet
local ____array = require("functions.array")
local arrayToString = ____array.arrayToString
local isArray = ____array.isArray
local ____bosses = require("functions.bosses")
local getBossID = ____bosses.getBossID
local ____collectibles = require("functions.collectibles")
local getCollectibleName = ____collectibles.getCollectibleName
local ____entities = require("functions.entities")
local getEntityID = ____entities.getEntityID
local ____enums = require("functions.enums")
local getEnumEntries = ____enums.getEnumEntries
local ____flag = require("functions.flag")
local hasFlag = ____flag.hasFlag
local ____isaacAPIClass = require("functions.isaacAPIClass")
local getIsaacAPIClassName = ____isaacAPIClass.getIsaacAPIClassName
local ____itemPool = require("functions.itemPool")
local getItemPoolName = ____itemPool.getItemPoolName
local ____log = require("functions.log")
local log = ____log.log
local ____playerEffects = require("functions.playerEffects")
local getEffectsList = ____playerEffects.getEffectsList
local ____playerHealth = require("functions.playerHealth")
local getPlayerHealth = ____playerHealth.getPlayerHealth
local ____players = require("functions.players")
local getPlayerName = ____players.getPlayerName
local ____roomData = require("functions.roomData")
local getRoomData = ____roomData.getRoomData
local getRoomGridIndex = ____roomData.getRoomGridIndex
local getRoomListIndex = ____roomData.getRoomListIndex
local ____set = require("functions.set")
local combineSets = ____set.combineSets
local getSortedSetValues = ____set.getSortedSetValues
local ____sort = require("functions.sort")
local sortNormal = ____sort.sortNormal
local ____table = require("functions.table")
local iterateTableInOrder = ____table.iterateTableInOrder
local ____trinkets = require("functions.trinkets")
local getTrinketName = ____trinkets.getTrinketName
local ____tstlClass = require("functions.tstlClass")
local isDefaultMap = ____tstlClass.isDefaultMap
local isTSTLMap = ____tstlClass.isTSTLMap
local isTSTLSet = ____tstlClass.isTSTLSet
local ____types = require("functions.types")
local isTable = ____types.isTable
local isUserdata = ____types.isUserdata
local ____vector = require("functions.vector")
local vectorToString = ____vector.vectorToString
--- Helper function for logging every flag that is turned on. Useful when debugging.
function ____exports.logFlags(flags, flagEnum, description)
    if description == nil then
        description = ""
    end
    if description ~= "" then
        description = "flag"
    end
    log((("Logging " .. description) .. " values for: ") .. tostring(flags))
    local hasNoFlags = true
    local entries = getEnumEntries(nil, flagEnum)
    for ____, ____value in ipairs(entries) do
        local key = ____value[1]
        local value = ____value[2]
        if hasFlag(nil, flags, value) then
            log(((("  Has flag: " .. key) .. " (") .. tostring(value)) .. ")")
            hasNoFlags = false
        end
    end
    if hasNoFlags then
        log("  n/a (no flags)")
    end
end
--- Helper function to log all of the values in an array.
-- 
-- @param array The array to log.
-- @param name Optional. The name of the array, which will be logged before the elements.
function ____exports.logArray(array, name)
    if not isArray(nil, array, false) then
        log("Tried to log an array, but the given object was not an array.")
        return
    end
    local arrayString = arrayToString(nil, array)
    if name == nil then
        name = "array"
    end
    log((("Logging " .. name) .. ": ") .. arrayString)
end
--- Helper function to log the names of a collectible type array.
-- 
-- @param collectibleTypes The collectible types to log.
-- @param name Optional. The name of the array, which will be logged before the elements.
function ____exports.logCollectibleTypes(collectibleTypes, name)
    if name == nil then
        name = "collectibles"
    end
    log(("Logging " .. name) .. ":")
    local i = 1
    for ____, collectibleType in ipairs(collectibleTypes) do
        local collectibleName = getCollectibleName(nil, collectibleType)
        log(((((tostring(i) .. ") ") .. collectibleName) .. " (") .. tostring(collectibleType)) .. ")")
        i = i + 1
    end
end
--- Helper function to log a `Color` object.
-- 
-- @param color The `Color` object to log.
-- @param name Optional. The name of the object, which will be logged before the properties.
function ____exports.logColor(color, name)
    if name == nil then
        name = "color"
    end
    log((((((((((((((("Logging " .. name) .. ": R") .. tostring(color.R)) .. ", G") .. tostring(color.G)) .. ", B") .. tostring(color.B)) .. ", A") .. tostring(color.A)) .. ", RO") .. tostring(color.RO)) .. ", BO") .. tostring(color.BO)) .. ", GO") .. tostring(color.GO))
end
--- Helper function to log every damage flag that is turned on. Useful when debugging.
function ____exports.logDamageFlags(damageFlags)
    ____exports.logFlags(damageFlags, DamageFlag, "damage")
end
--- Helper function to log every display flag that is turned on. Useful when debugging.
function ____exports.logDisplayFlags(displayFlags)
    ____exports.logFlags(displayFlags, DisplayFlag, "display")
end
--- Helper function to log every entity flag that is turned on. Useful when debugging.
function ____exports.logEntityFlags(entityFlags)
    ____exports.logFlags(entityFlags, EntityFlag, "entity")
end
function ____exports.logEntityID(entity)
    local entityID = getEntityID(nil, entity)
    log("Logging entity: " .. entityID)
end
--- Helper function for logging every game state flag that is turned on. Useful when debugging.
function ____exports.logGameStateFlags()
    log("Logging game state flags:")
    local gameStateFlagEntries = getEnumEntries(nil, GameStateFlag)
    local hasNoFlags = true
    for ____, ____value in ipairs(gameStateFlagEntries) do
        local key = ____value[1]
        local gameStateFlag = ____value[2]
        local flagValue = game:GetStateFlag(gameStateFlag)
        if flagValue then
            log(((("  Has flag: " .. key) .. " (") .. tostring(gameStateFlag)) .. ")")
            hasNoFlags = false
        end
    end
    if hasNoFlags then
        log("  n/a (no flags)")
    end
end
--- Helper function to log the names of a item pool type array.
-- 
-- @param itemPoolTypes The item pool types to log.
-- @param name Optional. The name of the array, which will be logged before the elements.
function ____exports.logItemPoolTypes(itemPoolTypes, name)
    if name == nil then
        name = "item pool types"
    end
    log(("Logging " .. name) .. ":")
    local i = 1
    for ____, itemPoolType in ipairs(itemPoolTypes) do
        local itemPoolName = getItemPoolName(nil, itemPoolType)
        log(((((tostring(i) .. ") ") .. itemPoolName) .. " (") .. tostring(itemPoolType)) .. ")")
        i = i + 1
    end
end
--- Helper function to log a `KColor` object.
-- 
-- @param kColor The `KColor` object to log.
-- @param name Optional. The name of the object, which will be logged before the properties.
function ____exports.logKColor(kColor, name)
    if name == nil then
        name = "KColor"
    end
    log((((((((("Logging " .. name) .. ": R") .. tostring(kColor.Red)) .. ", G") .. tostring(kColor.Green)) .. ", B") .. tostring(kColor.Blue)) .. ", A") .. tostring(kColor.Alpha))
end
--- Helper function for logging every level state flag that is turned on. Useful when debugging.
function ____exports.logLevelStateFlags()
    local level = game:GetLevel()
    local levelStateFlagEntries = getEnumEntries(nil, LevelStateFlag)
    log("Logging level state flags:")
    local hasNoFlags = true
    for ____, ____value in ipairs(levelStateFlagEntries) do
        local key = ____value[1]
        local levelStateFlag = ____value[2]
        local flagValue = level:GetStateFlag(levelStateFlag)
        if flagValue then
            log(((("  Has flag: " .. key) .. " (") .. tostring(levelStateFlag)) .. ")")
            hasNoFlags = false
        end
    end
    if hasNoFlags then
        log("  n/a (no flags)")
    end
end
--- Helper function to log a TSTL `Map`.
-- 
-- @param map The TSTL `Map` to log.
-- @param name Optional. The name of the map, which will be logged before the elements.
function ____exports.logMap(map, name)
    if not isTSTLMap(nil, map) and not isDefaultMap(nil, map) then
        log("Tried to log a TSTL map, but the given object was not a TSTL map.")
        return
    end
    local suffix = name == nil and "" or (" \"" .. name) .. "\""
    log(("Logging a TSTL map" .. suffix) .. ":")
    local mapKeys = {__TS__Spread(map:keys())}
    __TS__ArraySort(mapKeys, sortNormal)
    for ____, key in ipairs(mapKeys) do
        local value = map:get(key)
        log((("  " .. tostring(key)) .. " --> ") .. tostring(value))
    end
    log("  The size of the map was: " .. tostring(map.size))
end
function ____exports.logMusic()
    local currentMusic = musicManager:GetCurrentMusicID()
    log(((("Currently playing music track: " .. Music[currentMusic]) .. " (") .. tostring(currentMusic)) .. ")")
end
function ____exports.logPlayerEffects(player)
    local effects = getEffectsList(nil, player)
    log("Logging player effects:")
    if #effects == 0 then
        log("  n/a (no effects)")
        return
    end
    for ____, ____value in __TS__Iterator(__TS__ArrayEntries(effects)) do
        local i = ____value[1]
        local effect = ____value[2]
        local effectDescription
        if effect.Item:IsCollectible() then
            local collectibleName = getCollectibleName(nil, effect.Item.ID)
            effectDescription = "Collectible: " .. collectibleName
        elseif effect.Item:IsTrinket() then
            local trinketName = getTrinketName(nil, effect.Item.ID)
            effectDescription = "Trinket: " .. trinketName
        elseif effect.Item:IsNull() then
            effectDescription = "Null item: " .. tostring(effect.Item.ID)
        else
            effectDescription = "Unknown type of effect: " .. tostring(effect.Item.ID)
        end
        log(((((("  " .. tostring(i + 1)) .. ") ") .. effectDescription) .. " (x") .. tostring(effect.Count)) .. ")")
    end
end
function ____exports.logPlayerHealth(player)
    local playerName = getPlayerName(nil, player)
    local playerHealth = getPlayerHealth(nil, player)
    log(("Player health for " .. playerName) .. ":")
    log("  Max hearts: " .. tostring(playerHealth.maxHearts))
    log("  Hearts: " .. tostring(playerHealth.hearts))
    log("  Eternal hearts: " .. tostring(playerHealth.eternalHearts))
    log("  Soul hearts: " .. tostring(playerHealth.soulHearts))
    log("  Bone hearts: " .. tostring(playerHealth.boneHearts))
    log("  Golden hearts: " .. tostring(playerHealth.goldenHearts))
    log("  Rotten hearts: " .. tostring(playerHealth.rottenHearts))
    log("  Broken hearts: " .. tostring(playerHealth.brokenHearts))
    log("  Soul charges: " .. tostring(playerHealth.soulCharges))
    log("  Blood charges: " .. tostring(playerHealth.bloodCharges))
    log("  Soul heart types: [")
    for ____, soulHeartType in ipairs(playerHealth.soulHeartTypes) do
        log("    HeartSubType." .. HeartSubType[soulHeartType])
    end
    log("  ]")
end
--- Helper function for logging every projectile flag that is turned on. Useful when debugging.
function ____exports.logProjectileFlags(projectileFlags)
    ____exports.logFlags(projectileFlags, ProjectileFlag, "projectile")
end
--- Helper function for logging information about the current room.
function ____exports.logRoom()
    local bossID = getBossID(nil)
    local roomGridIndex = getRoomGridIndex(nil)
    local roomListIndex = getRoomListIndex(nil)
    local roomData = getRoomData(nil)
    log("Logging room information:")
    log(("- Room stage ID: " .. StageID[roomData.StageID]) .. " (roomData.StageID)")
    log(((("- Room type: " .. RoomType[roomData.Type]) .. " (") .. tostring(roomData.Type)) .. ")")
    log("- Variant: " .. tostring(roomData.Variant))
    log("- Sub-type: " .. tostring(roomData.Subtype))
    log("- Name: " .. roomData.Name)
    local roomGridIndexName = GridRoom[roomGridIndex]
    if roomGridIndexName == nil then
        log("- Grid index: " .. tostring(roomGridIndex))
    else
        log(((("- Grid index: " .. roomGridIndexName) .. " (") .. tostring(roomGridIndex)) .. ")")
    end
    log("- List index: " .. tostring(roomListIndex))
    if bossID == nil then
        log("- Boss ID: undefined")
    else
        log(((("- Boss ID: " .. BossID[bossID]) .. " (") .. tostring(bossID)) .. ")")
    end
end
--- Helper function for logging every seed effect (i.e. Easter Egg) that is turned on for the
-- particular run.
function ____exports.logSeedEffects()
    local seeds = game:GetSeeds()
    local seedEffectEntries = getEnumEntries(nil, SeedEffect)
    log("Logging seed effects:")
    local hasNoSeedEffects = true
    for ____, ____value in ipairs(seedEffectEntries) do
        local key = ____value[1]
        local seedEffect = ____value[2]
        if seeds:HasSeedEffect(seedEffect) then
            log(((("  " .. key) .. " (") .. tostring(seedEffect)) .. ")")
            hasNoSeedEffects = false
        end
    end
    if hasNoSeedEffects then
        log("  n/a (no seed effects)")
    end
end
--- Helper function to log a TSTL `Set`.
-- 
-- @param set The TSTL `Set` to log.
-- @param name Optional. The name of the set, which will be logged before the elements.
function ____exports.logSet(set, name)
    if not isTSTLSet(nil, set) then
        log("Tried to log a TSTL set, but the given object was not a TSTL set.")
        return
    end
    local suffix = name == nil and "" or (" \"" .. name) .. "\""
    log(("Logging a TSTL set" .. suffix) .. ":")
    local setValues = getSortedSetValues(nil, set)
    for ____, value in ipairs(setValues) do
        log("  Value: " .. tostring(value))
    end
    log("  The size of the set was: " .. tostring(set.size))
end
--- Helper function for logging every sound effect that is currently playing.
function ____exports.logSounds()
    local soundEffects = getEnumEntries(nil, SoundEffect)
    for ____, ____value in ipairs(soundEffects) do
        local key = ____value[1]
        local soundEffect = ____value[2]
        if sfxManager:IsPlaying(soundEffect) then
            log(((("Currently playing sound effect: " .. key) .. " (") .. tostring(soundEffect)) .. ")")
        end
    end
end
--- Helper function for logging every key and value of a Lua table. This is a deep log; the function
-- will recursively call itself if it encounters a table within a table.
-- 
-- This function will only work on tables that have string keys (because it logs the keys in order,
-- instead of randomly). It will throw a run-time error if it encounters a non-string key.
-- 
-- In order to prevent infinite recursion, this function will not log a sub-table when there are 10
-- or more parent tables.
function ____exports.logTable(luaTable, parentTables)
    if parentTables == nil then
        parentTables = 0
    end
    if parentTables == 0 then
        log("Logging a Lua table:", false)
    elseif parentTables > 10 then
        return
    end
    local numSpaces = (parentTables + 1) * 2
    local indentation = string.rep(
        " ",
        math.floor(numSpaces)
    )
    if not isTable(nil, luaTable) then
        log(
            ((indentation .. "n/a (encountered a variable of type \"") .. __TS__TypeOf(luaTable)) .. "\" instead of a table)",
            false
        )
        return
    end
    local numElements = 0
    iterateTableInOrder(
        nil,
        luaTable,
        function(____, key, value)
            log(
                ((indentation .. tostring(key)) .. " --> ") .. tostring(value),
                false
            )
            if isTable(nil, value) then
                if key == "__class" then
                    log(indentation .. "  (skipping enumerating this key to avoid infinite recursion)", false)
                else
                    ____exports.logTable(value, parentTables + 1)
                end
            end
            numElements = numElements + 1
        end
    )
    log(
        (indentation .. "The size of the table was: ") .. tostring(numElements),
        false
    )
end
--- Helper function to log the differences between the entries of two tables. Note that this will
-- only do a shallow comparison.
function ____exports.logTableDifferences(table1, table2)
    log("Comparing two Lua tables:")
    local table1Keys = __TS__ObjectKeys(table1)
    local table1KeysSet = __TS__New(ReadonlySet, table1Keys)
    local table2Keys = __TS__ObjectKeys(table2)
    local table2KeysSet = __TS__New(ReadonlySet, table2Keys)
    local keysSet = combineSets(nil, table1KeysSet, table2KeysSet)
    local keys = {__TS__Spread(keysSet:values())}
    __TS__ArraySort(keys)
    for ____, key in ipairs(keys) do
        local value1 = table1[key]
        local value2 = table2[key]
        if value1 == nil then
            log("  Table 1 is missing key: " .. tostring(key))
        end
        if value2 == nil then
            log("  Table 2 is missing key: " .. tostring(key))
        end
        if value1 ~= value2 then
            log(((((("  " .. tostring(key)) .. " --> \"") .. tostring(value1)) .. "\" versus \"") .. tostring(value2)) .. "\"")
        end
    end
end
--- Helper function to log the keys of a Lua table. This is not a deep log; only the keys of the
-- top-most table will be logged.
-- 
-- This function is useful for tables that have recursive references.
function ____exports.logTableKeys(luaTable)
    log("Logging the keys of a Lua table:")
    if not isTable(nil, luaTable) then
        log(("  n/a (encountered a variable of type \"" .. __TS__TypeOf(luaTable)) .. "\" instead of a table)")
        return
    end
    local numElements = 0
    iterateTableInOrder(
        nil,
        luaTable,
        function(____, key)
            log(tostring(key))
            numElements = numElements + 1
        end
    )
    log("  The size of the table was: " .. tostring(numElements))
end
--- Helper function to log every table key and value. This is a shallow log; the function will not
-- recursively traverse sub-tables.
-- 
-- This function will only work on tables that have string keys (because it logs the keys in order,
-- instead of randomly). It will throw a run-time error if it encounters a non-string key.
function ____exports.logTableShallow(luaTable)
    log("Logging a Lua table (shallow):", false)
    if not isTable(nil, luaTable) then
        log(
            ("n/a (encountered a variable of type \"" .. __TS__TypeOf(luaTable)) .. "\" instead of a table)",
            false
        )
        return
    end
    local numElements = 0
    local indentation = "  "
    iterateTableInOrder(
        nil,
        luaTable,
        function(____, key, value)
            log(
                ((indentation .. tostring(key)) .. " --> ") .. tostring(value),
                false
            )
            numElements = numElements + 1
        end
    )
    log(
        (indentation .. "The size of the table was: ") .. tostring(numElements),
        false
    )
end
--- Helper function for log every tear flag that is turned on. Useful when debugging.
function ____exports.logTearFlags(tearFlags)
    ____exports.logFlags(tearFlags, TearFlag, "tear")
end
--- Helper function for printing out every use flag that is turned on. Useful when debugging.
function ____exports.logUseFlags(useFlags)
    ____exports.logFlags(useFlags, UseFlag, "use")
end
--- Helper function to enumerate all of the properties of a "userdata" object (i.e. an object from
-- the Isaac API).
function ____exports.logUserdata(userdata)
    if not isUserdata(nil, userdata) then
        log("Userdata: [not userdata]")
        return
    end
    local metatable = getmetatable(userdata)
    if metatable == nil then
        log("Userdata: [no metatable]")
        return
    end
    local classType = getIsaacAPIClassName(nil, userdata)
    if classType == nil then
        log("Userdata: [no class type]")
    else
        log("Userdata: " .. classType)
    end
    ____exports.logTable(metatable)
end
--- Helper function to log a `Vector` object.
-- 
-- @param vector The `Vector` object to log.
-- @param name Optional. The name of the object, which will be logged before the properties.
-- @param round Optional. If true, will round the vector values to the nearest integer. Default is
-- false.
function ____exports.logVector(vector, name, round)
    if round == nil then
        round = false
    end
    if name == nil then
        name = "vector"
    end
    local vectorString = vectorToString(nil, vector, round)
    log((("Logging " .. name) .. ": ") .. vectorString)
end
return ____exports
