local ____lualib = require("lualib_bundle")
local __TS__New = ____lualib.__TS__New
local __TS__ArrayFilter = ____lualib.__TS__ArrayFilter
local ____exports = {}
local NON_ALIVE_NPCS_TYPE_VARIANT, NON_ALIVE_NPCS_TYPE_VARIANT_SUB_TYPE
local ____isaac_2Dtypescript_2Ddefinitions = require("isaac-typescript-definitions")
local BegottenVariant = ____isaac_2Dtypescript_2Ddefinitions.BegottenVariant
local BigHornVariant = ____isaac_2Dtypescript_2Ddefinitions.BigHornVariant
local ChargerSubType = ____isaac_2Dtypescript_2Ddefinitions.ChargerSubType
local ChargerVariant = ____isaac_2Dtypescript_2Ddefinitions.ChargerVariant
local DarkEsauVariant = ____isaac_2Dtypescript_2Ddefinitions.DarkEsauVariant
local DeathVariant = ____isaac_2Dtypescript_2Ddefinitions.DeathVariant
local DumpVariant = ____isaac_2Dtypescript_2Ddefinitions.DumpVariant
local EntityType = ____isaac_2Dtypescript_2Ddefinitions.EntityType
local HopperVariant = ____isaac_2Dtypescript_2Ddefinitions.HopperVariant
local MamaGurdyVariant = ____isaac_2Dtypescript_2Ddefinitions.MamaGurdyVariant
local MotherSubType = ____isaac_2Dtypescript_2Ddefinitions.MotherSubType
local MotherVariant = ____isaac_2Dtypescript_2Ddefinitions.MotherVariant
local NPCState = ____isaac_2Dtypescript_2Ddefinitions.NPCState
local PeepVariant = ____isaac_2Dtypescript_2Ddefinitions.PeepVariant
local RaglingVariant = ____isaac_2Dtypescript_2Ddefinitions.RaglingVariant
local VisVariant = ____isaac_2Dtypescript_2Ddefinitions.VisVariant
local ____constants = require("core.constants")
local EGGY_STATE_FRAME_OF_FINAL_SPIDER = ____constants.EGGY_STATE_FRAME_OF_FINAL_SPIDER
local ____ReadonlySet = require("types.ReadonlySet")
local ReadonlySet = ____ReadonlySet.ReadonlySet
local ____entitiesSpecific = require("functions.entitiesSpecific")
local getNPCs = ____entitiesSpecific.getNPCs
--- Checks for specific NPCs that have "CanShutDoors" set to true naturally by the game, but should
-- not actually keep the doors closed (like Death's scythes).
function ____exports.isAliveExceptionNPC(self, npc)
    local entityTypeVariant = (tostring(npc.Type) .. ".") .. tostring(npc.Variant)
    if NON_ALIVE_NPCS_TYPE_VARIANT:has(entityTypeVariant) then
        return true
    end
    local entityTypeVariantSubType = (((tostring(npc.Type) .. ".") .. tostring(npc.Variant)) .. ".") .. tostring(npc.SubType)
    if NON_ALIVE_NPCS_TYPE_VARIANT_SUB_TYPE:has(entityTypeVariantSubType) then
        return true
    end
    if ____exports.isDyingEggyWithNoSpidersLeft(nil, npc) then
        return true
    end
    if ____exports.isDaddyLongLegsChildStompEntity(nil, npc) then
        return true
    end
    if ____exports.isRaglingDeathPatch(nil, npc) then
        return true
    end
    if ____exports.isDyingDump(nil, npc) then
        return true
    end
    return false
end
--- Helper function to distinguish between a normal Daddy Long Legs / Triachnid and the child entity
-- that is spawned when the boss does the multi-stomp attack.
-- 
-- When this attack occurs, four extra copies of Daddy Long Legs will be spawned with the same
-- entity type, variant, and sub-type. The `Entity.Parent` field will be undefined in this case, so
-- the way to tell them apart is to check for a non-undefined `Entity.SpawnerEntity` field.
function ____exports.isDaddyLongLegsChildStompEntity(self, npc)
    return npc.Type == EntityType.DADDY_LONG_LEGS and npc.SpawnerEntity ~= nil
end
--- Helper function to detect the custom death state of a Dump. When Dumps die, they go to
-- `NPCState.SPECIAL`, spit out their head, and then slowly fade away while shooting a burst of
-- tears.
function ____exports.isDyingDump(self, npc)
    return npc.Type == EntityType.DUMP and npc.Variant == DumpVariant.DUMP and npc.State == NPCState.SPECIAL
end
--- Helper function to detect the custom death state of an Eggy. Eggies are never actually marked
-- dead by the game. Instead, when Eggies take fatal damage, they go into NPCState.STATE_SUICIDE and
-- spawn 14 Swarm Spiders while their StateFrame ticks upwards.
function ____exports.isDyingEggyWithNoSpidersLeft(self, npc)
    return npc.Type == EntityType.HOPPER and npc.Variant == HopperVariant.EGGY and npc.State == NPCState.SUICIDE and npc.StateFrame >= EGGY_STATE_FRAME_OF_FINAL_SPIDER
end
--- Helper function to detect the custom death state of a Rag Man Ragling. When Rag Man Raglings die,
-- they turn into a patch on the ground and can be revived by Rag Man at a later time. This causes
-- them to show up as an "alive" enemy, so they should usually be filtered out of lists of alive
-- enemies.
function ____exports.isRaglingDeathPatch(self, npc)
    return npc.Type == EntityType.RAGLING and npc.Variant == RaglingVariant.RAG_MANS_RAGLING and npc.State == NPCState.SPECIAL
end
NON_ALIVE_NPCS_TYPE_VARIANT = __TS__New(
    ReadonlySet,
    {
        (tostring(EntityType.VIS) .. ".") .. tostring(VisVariant.CHUBBER_PROJECTILE),
        (tostring(EntityType.DEATH) .. ".") .. tostring(DeathVariant.DEATH_SCYTHE),
        (tostring(EntityType.PEEP) .. ".") .. tostring(PeepVariant.PEEP_EYE),
        (tostring(EntityType.PEEP) .. ".") .. tostring(PeepVariant.BLOAT_EYE),
        (tostring(EntityType.BEGOTTEN) .. ".") .. tostring(BegottenVariant.BEGOTTEN_CHAIN),
        (tostring(EntityType.MAMA_GURDY) .. ".") .. tostring(MamaGurdyVariant.LEFT_HAND),
        (tostring(EntityType.MAMA_GURDY) .. ".") .. tostring(MamaGurdyVariant.RIGHT_HAND),
        (tostring(EntityType.BIG_HORN) .. ".") .. tostring(BigHornVariant.SMALL_HOLE),
        (tostring(EntityType.BIG_HORN) .. ".") .. tostring(BigHornVariant.BIG_HOLE),
        (tostring(EntityType.DARK_ESAU) .. ".") .. tostring(DarkEsauVariant.DARK_ESAU),
        (tostring(EntityType.DARK_ESAU) .. ".") .. tostring(DarkEsauVariant.PIT)
    }
)
NON_ALIVE_NPCS_TYPE_VARIANT_SUB_TYPE = __TS__New(
    ReadonlySet,
    {
        (((tostring(EntityType.CHARGER) .. ".") .. tostring(ChargerVariant.CHARGER)) .. ".") .. tostring(ChargerSubType.MY_SHADOW),
        (((tostring(EntityType.MOTHER) .. ".") .. tostring(MotherVariant.MOTHER_1)) .. ".") .. tostring(MotherSubType.PHASE_2)
    }
)
--- Helper function to get all of the non-dead NPCs in the room.
-- 
-- This function will not include NPCs on an internal blacklist, such as Death's scythes or Big Horn
-- holes.
-- 
-- @param entityType Optional. If specified, will only get the NPCs that match the type. Default is
-- -1, which matches every type.
-- @param variant Optional. If specified, will only get the NPCs that match the variant. Default is
-- -1, which matches every variant.
-- @param subType Optional. If specified, will only get the NPCs that match the sub-type. Default is
-- -1, which matches every sub-type.
-- @param ignoreFriendly Optional. Default is false.
function ____exports.getAliveNPCs(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 npcs = getNPCs(
        nil,
        entityType,
        variant,
        subType,
        ignoreFriendly
    )
    return __TS__ArrayFilter(
        npcs,
        function(____, npc) return not npc:IsDead() and not ____exports.isAliveExceptionNPC(nil, npc) end
    )
end
return ____exports
