local ____lualib = require("lualib_bundle")
local __TS__New = ____lualib.__TS__New
local __TS__ArrayFilter = ____lualib.__TS__ArrayFilter
local ____exports = {}
local ____isaac_2Dtypescript_2Ddefinitions = require("isaac-typescript-definitions")
local BossID = ____isaac_2Dtypescript_2Ddefinitions.BossID
local EntityType = ____isaac_2Dtypescript_2Ddefinitions.EntityType
local LokiVariant = ____isaac_2Dtypescript_2Ddefinitions.LokiVariant
local UltraGreedVariant = ____isaac_2Dtypescript_2Ddefinitions.UltraGreedVariant
local ____cachedClasses = require("core.cachedClasses")
local game = ____cachedClasses.game
local ____constants = require("core.constants")
local VectorZero = ____constants.VectorZero
local ____entityTypeVariantToBossIDMap = require("maps.entityTypeVariantToBossIDMap")
local ENTITY_TYPE_VARIANT_TO_BOSS_ID_MAP = ____entityTypeVariantToBossIDMap.ENTITY_TYPE_VARIANT_TO_BOSS_ID_MAP
local ____bossIDToEntityTypeVariant = require("objects.bossIDToEntityTypeVariant")
local BOSS_ID_TO_ENTITY_TYPE_VARIANT = ____bossIDToEntityTypeVariant.BOSS_ID_TO_ENTITY_TYPE_VARIANT
local ____bossNames = require("objects.bossNames")
local BOSS_NAMES = ____bossNames.BOSS_NAMES
local DEFAULT_BOSS_NAME = ____bossNames.DEFAULT_BOSS_NAME
local ____bossSets = require("sets.bossSets")
local ALL_BOSSES = ____bossSets.ALL_BOSSES
local BOSS_ID_TO_STAGE_IDS = ____bossSets.BOSS_ID_TO_STAGE_IDS
local NON_STORY_BOSSES = ____bossSets.NON_STORY_BOSSES
local STAGE_ID_TO_BOSS_IDS = ____bossSets.STAGE_ID_TO_BOSS_IDS
local STAGE_TO_COMBINED_BOSS_SET_MAP = ____bossSets.STAGE_TO_COMBINED_BOSS_SET_MAP
local ____repentanceBossIDsSet = require("sets.repentanceBossIDsSet")
local REPENTANCE_ONLY_BOSS_IDS_SET = ____repentanceBossIDsSet.REPENTANCE_ONLY_BOSS_IDS_SET
local ____sinEntityTypesSet = require("sets.sinEntityTypesSet")
local SIN_ENTITY_TYPES_SET = ____sinEntityTypesSet.SIN_ENTITY_TYPES_SET
local ____ReadonlySet = require("types.ReadonlySet")
local ReadonlySet = ____ReadonlySet.ReadonlySet
local ____entities = require("functions.entities")
local doesEntityExist = ____entities.doesEntityExist
local ____entitiesSpecific = require("functions.entitiesSpecific")
local getNPCs = ____entitiesSpecific.getNPCs
local spawnNPC = ____entitiesSpecific.spawnNPC
local ____npcs = require("functions.npcs")
local getAliveNPCs = ____npcs.getAliveNPCs
local ____rng = require("functions.rng")
local isRNG = ____rng.isRNG
local ____rooms = require("functions.rooms")
local inBeastRoom = ____rooms.inBeastRoom
local inDogmaRoom = ____rooms.inDogmaRoom
local ____utils = require("functions.utils")
local ____repeat = ____utils["repeat"]
local BOSSES_THAT_REQUIRE_MULTIPLE_SPAWNS = __TS__New(ReadonlySet, {
    EntityType.LARRY_JR,
    EntityType.CHUB,
    EntityType.LOKI,
    EntityType.GURGLING,
    EntityType.TURDLET
})
local DEFAULT_BOSS_MULTI_SEGMENTS = 4
--- Helper function to get all of the non-dead bosses in the room.
-- 
-- This function will not include bosses on an internal blacklist, such as Death's scythes or Big
-- Horn holes.
-- 
-- @param entityType Optional. If specified, will only get the bosses that match the type. Default
-- is -1, which matches every type.
-- @param variant Optional. If specified, will only get the bosses that match the variant. Default
-- is -1, which matches every variant.
-- @param subType Optional. If specified, will only get the bosses that match the sub-type. Default
-- is -1, which matches every sub-type.
-- @param ignoreFriendly Optional. Default is false.
function ____exports.getAliveBosses(self, entityType, variant, subType, ignoreFriendly)
    if entityType == nil then
        entityType = -1
    end
    if variant == nil then
        variant = -1
    end
    if subType == nil then
        subType = -1
    end
    if ignoreFriendly == nil then
        ignoreFriendly = false
    end
    local aliveNPCs = getAliveNPCs(
        nil,
        entityType,
        variant,
        subType,
        ignoreFriendly
    )
    return __TS__ArrayFilter(
        aliveNPCs,
        function(____, aliveNPC) return aliveNPC:IsBoss() end
    )
end
--- Helper function to get an array with every boss in the game. This is derived from the `BossID`
-- enum.
-- 
-- This includes:
-- - Ultra Greed
-- - Ultra Greedier
-- 
-- This does not include:
-- - mini-bosses (e.g. Ultra Pride, Krampus)
-- - bosses that do not appear in Boss Rooms (e.g. Uriel, Gabriel)
-- - the second phase of multi-phase bosses (e.g. Mega Satan 2)
-- - sub-bosses of The Beast fight (e.g. Ultra Famine, Ultra Pestilence, Ultra War, Ultra Death)
-- - bosses that do not have any Boss Rooms defined due to being unfinished (e.g. Raglich)
-- 
-- Also see the `getAllNonStoryBosses` function.
function ____exports.getAllBosses(self)
    return ALL_BOSSES
end
--- Helper function to get an array with every boss in the game. This is derived from the `BossID`
-- enum. This is the same thing as the `getAllBosses` helper function, but with story bosses
-- filtered out.
function ____exports.getAllNonStoryBosses(self)
    return NON_STORY_BOSSES
end
--- Helper function to get the boss ID corresponding to the current room. Returns undefined if the
-- current room is not a Boss Room.
-- 
-- Use this instead of the vanilla `Room.GetBossID` method since it has a saner return type and it
-- correctly handles Dogma, The Beast, and Ultra Greedier.
function ____exports.getBossID(self)
    if inDogmaRoom(nil) then
        return BossID.DOGMA
    end
    if inBeastRoom(nil) then
        return BossID.BEAST
    end
    local room = game:GetRoom()
    local bossID = room:GetBossID()
    if bossID == 0 then
        return nil
    end
    if bossID == BossID.ULTRA_GREED and doesEntityExist(nil, EntityType.ULTRA_GREED, UltraGreedVariant.ULTRA_GREEDIER) then
        return BossID.ULTRA_GREEDIER
    end
    return bossID
end
function ____exports.getBossIDFromEntityTypeVariant(self, entityType, variant)
    local entityTypeVariant = (tostring(entityType) .. ".") .. tostring(variant)
    return ENTITY_TYPE_VARIANT_TO_BOSS_ID_MAP:get(entityTypeVariant)
end
--- Helper function to get the set of vanilla bosses for a particular stage across all of the stage
-- types. For example, specifying `LevelStage.BASEMENT_2` will return a set with all of the bosses
-- for Basement, Cellar, Burning Basement, Downpour, and Dross.
-- 
-- Also see the `getAllBossesSet` and `getBossIDsForStageID` functions.
function ____exports.getBossIDsForStage(self, stage)
    return STAGE_TO_COMBINED_BOSS_SET_MAP:get(stage)
end
--- Helper function to get the set of vanilla bosses that can randomly appear on a particular stage
-- ID.
-- 
-- Also see the `getAllBossesSet` and `getBossIDsForStage` functions.
function ____exports.getBossIDsForStageID(self, stageID)
    return STAGE_ID_TO_BOSS_IDS:get(stageID)
end
--- Helper function to get the proper English name for a boss. For example, the name for
-- `BossID.WRETCHED` (36) is "The Wretched".
function ____exports.getBossName(self, bossID)
    return BOSS_NAMES[bossID] or DEFAULT_BOSS_NAME
end
--- Helper function to get the set of stage IDs that a particular boss naturally appears in.
function ____exports.getBossStageIDs(self, bossID)
    return BOSS_ID_TO_STAGE_IDS[bossID]
end
--- Helper function to get all of the bosses in the room.
-- 
-- @param entityType Optional. If specified, will only get the bosses that match the type. Default
-- is -1, which matches every type.
-- @param variant Optional. If specified, will only get the bosses that match the variant. Default
-- is -1, which matches every variant.
-- @param subType Optional. If specified, will only get the bosses that match the sub-type. Default
-- is -1, which matches every sub-type.
-- @param ignoreFriendly Optional. Default is false.
function ____exports.getBosses(self, entityType, variant, subType, ignoreFriendly)
    if ignoreFriendly == nil then
        ignoreFriendly = false
    end
    local npcs = getNPCs(
        nil,
        entityType,
        variant,
        subType,
        ignoreFriendly
    )
    return __TS__ArrayFilter(
        npcs,
        function(____, npc) return npc:IsBoss() end
    )
end
function ____exports.getEntityTypeVariantFromBossID(self, bossID)
    return BOSS_ID_TO_ENTITY_TYPE_VARIANT[bossID]
end
--- Helper function to check if a boss is only found on a Repentance floor such as Dross, Mines, and
-- so on.
-- 
-- For example, The Pile is a boss that was added in Repentance, but since it can appear in
-- Necropolis, it is not considered a Repentance boss for the purposes of this function.
function ____exports.isRepentanceBoss(self, bossID)
    return REPENTANCE_ONLY_BOSS_IDS_SET:has(bossID)
end
--- Helper function to check if the provided NPC is a Sin miniboss, such as Sloth or Lust.
function ____exports.isSin(self, npc)
    return SIN_ENTITY_TYPES_SET:has(npc.Type)
end
local function getNumBossSegments(self, entityType, variant, numSegments)
    if numSegments ~= nil then
        return numSegments
    end
    repeat
        local ____switch23 = entityType
        local ____cond23 = ____switch23 == EntityType.CHUB
        if ____cond23 then
            do
                return 3
            end
        end
        ____cond23 = ____cond23 or ____switch23 == EntityType.LOKI
        if ____cond23 then
            do
                return variant == LokiVariant.LOKII and 2 or 1
            end
        end
        ____cond23 = ____cond23 or ____switch23 == EntityType.GURGLING
        if ____cond23 then
            do
                return 2
            end
        end
        do
            do
                return DEFAULT_BOSS_MULTI_SEGMENTS
            end
        end
    until true
end
--- Helper function to spawn a boss.
-- 
-- Use this function instead of `spawnNPC` since it handles automatically spawning multiple segments
-- for multi-segment bosses.
-- 
-- By default, this will spawn Chub (and his variants) with 3 segments, Lokii with 2 copies,
-- Gurglings/Turdlings with 2 copies, and other multi-segment bosses with 4 segments. You can
-- customize this via the "numSegments" argument.
function ____exports.spawnBoss(self, entityType, variant, subType, positionOrGridIndex, velocity, spawner, seedOrRNG, numSegments)
    if velocity == nil then
        velocity = VectorZero
    end
    local seed = isRNG(nil, seedOrRNG) and seedOrRNG:Next() or seedOrRNG
    local npc = spawnNPC(
        nil,
        entityType,
        variant,
        subType,
        positionOrGridIndex,
        velocity,
        spawner,
        seed
    )
    if BOSSES_THAT_REQUIRE_MULTIPLE_SPAWNS:has(entityType) then
        local numBossSegments = getNumBossSegments(nil, entityType, variant, numSegments)
        local remainingSegmentsToSpawn = numBossSegments - 1
        ____repeat(
            nil,
            remainingSegmentsToSpawn,
            function()
                spawnNPC(
                    nil,
                    entityType,
                    variant,
                    subType,
                    positionOrGridIndex,
                    velocity,
                    spawner,
                    seed
                )
            end
        )
    end
    return npc
end
--- Helper function to spawn a boss with a specific seed.
-- 
-- For more information, see the documentation for the `spawnBoss` function.
function ____exports.spawnBossWithSeed(self, entityType, variant, subType, positionOrGridIndex, seedOrRNG, velocity, spawner, numSegments)
    if velocity == nil then
        velocity = VectorZero
    end
    local seed = isRNG(nil, seedOrRNG) and seedOrRNG:Next() or seedOrRNG
    return ____exports.spawnBoss(
        nil,
        entityType,
        variant,
        subType,
        positionOrGridIndex,
        velocity,
        spawner,
        seed,
        numSegments
    )
end
return ____exports
