local ____lualib = require("lualib_bundle")
local __TS__Class = ____lualib.__TS__Class
local __TS__ClassExtends = ____lualib.__TS__ClassExtends
local Map = ____lualib.Map
local __TS__New = ____lualib.__TS__New
local __TS__ObjectAssign = ____lualib.__TS__ObjectAssign
local __TS__DecorateLegacy = ____lualib.__TS__DecorateLegacy
local ____exports = {}
local getRoomTypeMap
local ____isaac_2Dtypescript_2Ddefinitions = require("isaac-typescript-definitions")
local CollectibleType = ____isaac_2Dtypescript_2Ddefinitions.CollectibleType
local GridEntityType = ____isaac_2Dtypescript_2Ddefinitions.GridEntityType
local LevelCurse = ____isaac_2Dtypescript_2Ddefinitions.LevelCurse
local LevelStage = ____isaac_2Dtypescript_2Ddefinitions.LevelStage
local ModCallback = ____isaac_2Dtypescript_2Ddefinitions.ModCallback
local RoomShape = ____isaac_2Dtypescript_2Ddefinitions.RoomShape
local RoomType = ____isaac_2Dtypescript_2Ddefinitions.RoomType
local StageType = ____isaac_2Dtypescript_2Ddefinitions.StageType
local ____cachedClasses = require("core.cachedClasses")
local game = ____cachedClasses.game
local musicManager = ____cachedClasses.musicManager
local metadataJSON = require("customStageMetadata")
local ____decorators = require("decorators")
local Exported = ____decorators.Exported
local ____ISCFeature = require("enums.ISCFeature")
local ISCFeature = ____ISCFeature.ISCFeature
local ____ModCallbackCustom = require("enums.ModCallbackCustom")
local ModCallbackCustom = ____ModCallbackCustom.ModCallbackCustom
local ____array = require("functions.array")
local isArray = ____array.isArray
local ____doors = require("functions.doors")
local doorSlotsToDoorSlotFlags = ____doors.doorSlotsToDoorSlotFlags
local getDoorSlotsForRoomShape = ____doors.getDoorSlotsForRoomShape
local ____flag = require("functions.flag")
local hasFlag = ____flag.hasFlag
local removeFlag = ____flag.removeFlag
local ____log = require("functions.log")
local logError = ____log.logError
local ____rng = require("functions.rng")
local newRNG = ____rng.newRNG
local ____rockAlt = require("functions.rockAlt")
local removeUrnRewards = ____rockAlt.removeUrnRewards
local ____rooms = require("functions.rooms")
local getRoomDataForTypeVariant = ____rooms.getRoomDataForTypeVariant
local getRoomsInsideGrid = ____rooms.getRoomsInsideGrid
local inRoomType = ____rooms.inRoomType
local ____sound = require("functions.sound")
local getMusicForStage = ____sound.getMusicForStage
local ____stage = require("functions.stage")
local setStage = ____stage.setStage
local ____utils = require("functions.utils")
local assertDefined = ____utils.assertDefined
local ____Feature = require("classes.private.Feature")
local Feature = ____Feature.Feature
local ____backdrop = require("classes.features.other.customStages.backdrop")
local setCustomStageBackdrop = ____backdrop.setCustomStageBackdrop
local ____constants = require("classes.features.other.customStages.constants")
local CUSTOM_FLOOR_STAGE = ____constants.CUSTOM_FLOOR_STAGE
local CUSTOM_FLOOR_STAGE_TYPE = ____constants.CUSTOM_FLOOR_STAGE_TYPE
local DEFAULT_BASE_STAGE = ____constants.DEFAULT_BASE_STAGE
local DEFAULT_BASE_STAGE_TYPE = ____constants.DEFAULT_BASE_STAGE_TYPE
local ____gridEntities = require("classes.features.other.customStages.gridEntities")
local convertVanillaTrapdoors = ____gridEntities.convertVanillaTrapdoors
local setCustomDecorationGraphics = ____gridEntities.setCustomDecorationGraphics
local setCustomDoorGraphics = ____gridEntities.setCustomDoorGraphics
local setCustomPitGraphics = ____gridEntities.setCustomPitGraphics
local setCustomRockGraphics = ____gridEntities.setCustomRockGraphics
local ____shadows = require("classes.features.other.customStages.shadows")
local setShadows = ____shadows.setShadows
local ____streakText = require("classes.features.other.customStages.streakText")
local streakTextGetShaderParams = ____streakText.streakTextGetShaderParams
local streakTextPostRender = ____streakText.streakTextPostRender
local topStreakTextStart = ____streakText.topStreakTextStart
local ____utils = require("classes.features.other.customStages.utils")
local getRandomBossRoomFromPool = ____utils.getRandomBossRoomFromPool
local getRandomCustomStageRoom = ____utils.getRandomCustomStageRoom
local ____v = require("classes.features.other.customStages.v")
local v = ____v.v
local ____versusScreen = require("classes.features.other.customStages.versusScreen")
local playVersusScreenAnimation = ____versusScreen.playVersusScreenAnimation
local versusScreenPostRender = ____versusScreen.versusScreenPostRender
function getRoomTypeMap(self, customStageLua)
    local roomTypeMap = __TS__New(Map)
    for ____, roomMetadata in ipairs(customStageLua.roomsMetadata) do
        local roomType = roomMetadata.type
        local roomShapeMap = roomTypeMap:get(roomType)
        if roomShapeMap == nil then
            roomShapeMap = __TS__New(Map)
            roomTypeMap:set(roomType, roomShapeMap)
        end
        local roomShape = roomMetadata.shape
        local roomDoorSlotFlagMap = roomShapeMap:get(roomShape)
        if roomDoorSlotFlagMap == nil then
            roomDoorSlotFlagMap = __TS__New(Map)
            roomShapeMap:set(roomShape, roomDoorSlotFlagMap)
        end
        local doorSlotFlags = roomMetadata.doorSlotFlags
        local rooms = roomDoorSlotFlagMap:get(doorSlotFlags)
        if rooms == nil then
            rooms = {}
            roomDoorSlotFlagMap:set(doorSlotFlags, rooms)
        end
        rooms[#rooms + 1] = roomMetadata
    end
    return roomTypeMap
end
--- 60 does not work correctly (the music kicking in from stage -1 will mute it), so we use 70 to be
-- safe.
local MUSIC_DELAY_RENDER_FRAMES = 70
____exports.CustomStages = __TS__Class()
local CustomStages = ____exports.CustomStages
CustomStages.name = "CustomStages"
__TS__ClassExtends(CustomStages, Feature)
function CustomStages.prototype.____constructor(self, customGridEntities, customTrapdoors, disableAllSound, gameReorderedCallbacks, pause, runInNFrames)
    Feature.prototype.____constructor(self)
    self.v = v
    self.customStagesMap = __TS__New(Map)
    self.customStageCachedRoomData = __TS__New(Map)
    self.usingRedKey = false
    self.goToCustomStage = function(____, destinationName, destinationStage, _destinationStageType)
        assertDefined(nil, destinationName, "Failed to go to a custom stage since the custom trapdoors feature did not pass a destination name to the logic function.")
        local firstFloor = destinationStage == LevelStage.BASEMENT_1
        self:setCustomStage(destinationName, firstFloor)
    end
    self.postRender = function()
        local customStage = v.run.currentCustomStage
        if customStage == nil then
            return
        end
        streakTextPostRender(nil)
        versusScreenPostRender(nil, self.pause, self.disableAllSound)
        if customStage.music ~= nil then
            local currentMusic = musicManager:GetCurrentMusicID()
            local music = Isaac.GetMusicIdByName(customStage.music)
            if currentMusic == music then
                musicManager:Resume()
                musicManager:UpdateVolume()
            end
        end
    end
    self.postUseItemRedKey = function()
        local customStage = v.run.currentCustomStage
        if customStage == nil then
            return nil
        end
        if not self.usingRedKey then
            return nil
        end
        self.usingRedKey = false
        local level = game:GetLevel()
        level:SetStage(CUSTOM_FLOOR_STAGE, CUSTOM_FLOOR_STAGE_TYPE)
        return nil
    end
    self.postCurseEval = function(____, curses)
        local customStage = v.run.currentCustomStage
        if customStage == nil then
            return nil
        end
        if hasFlag(nil, curses, LevelCurse.LABYRINTH) then
            return removeFlag(nil, curses, LevelCurse.LABYRINTH)
        end
        return nil
    end
    self.getShaderParams = function(____, shaderName)
        local customStage = v.run.currentCustomStage
        if customStage == nil then
            return nil
        end
        streakTextGetShaderParams(nil, customStage, shaderName)
        return nil
    end
    self.preUseItemRedKey = function()
        local customStage = v.run.currentCustomStage
        if customStage == nil then
            return nil
        end
        self.usingRedKey = true
        local level = game:GetLevel()
        local stage = customStage.baseStage or DEFAULT_BASE_STAGE
        local stageType = customStage.baseStageType or DEFAULT_BASE_STAGE_TYPE
        level:SetStage(stage, stageType)
        return nil
    end
    self.postGridEntityBrokenRockAlt = function(____, gridEntity)
        local customStage = v.run.currentCustomStage
        if customStage == nil then
            return
        end
        if customStage.rocksPNGPath == nil then
            return
        end
        removeUrnRewards(nil, gridEntity)
    end
    self.postGridEntityInit = function(____, gridEntity)
        local customStage = v.run.currentCustomStage
        if customStage == nil then
            return
        end
        if self.customGridEntities:isCustomGridEntity(gridEntity) then
            return
        end
        setCustomDecorationGraphics(nil, customStage, gridEntity)
        setCustomRockGraphics(nil, customStage, gridEntity)
        setCustomPitGraphics(nil, customStage, gridEntity)
        setCustomDoorGraphics(nil, customStage, gridEntity)
        convertVanillaTrapdoors(
            nil,
            customStage,
            gridEntity,
            v.run.firstFloor,
            self.customTrapdoors
        )
    end
    self.postNewRoomReordered = function()
        local customStage = v.run.currentCustomStage
        if customStage == nil then
            return
        end
        setCustomStageBackdrop(nil, customStage)
        setShadows(nil, customStage)
        playVersusScreenAnimation(
            nil,
            customStage,
            self.disableAllSound,
            self.pause,
            self.runInNFrames
        )
        if customStage.music ~= nil and inRoomType(nil, RoomType.DEFAULT) then
            local music = Isaac.GetMusicIdByName(customStage.music)
            local currentMusic = musicManager:GetCurrentMusicID()
            if currentMusic ~= music then
                musicManager:Fadein(music)
            end
        end
    end
    self.featuresUsed = {
        ISCFeature.CUSTOM_GRID_ENTITIES,
        ISCFeature.CUSTOM_TRAPDOORS,
        ISCFeature.DISABLE_ALL_SOUND,
        ISCFeature.GAME_REORDERED_CALLBACKS,
        ISCFeature.PAUSE,
        ISCFeature.RUN_IN_N_FRAMES
    }
    self.callbacksUsed = {
        {ModCallback.POST_RENDER, self.postRender},
        {ModCallback.POST_USE_ITEM, self.postUseItemRedKey, {CollectibleType.RED_KEY}},
        {ModCallback.POST_CURSE_EVAL, self.postCurseEval},
        {ModCallback.GET_SHADER_PARAMS, self.getShaderParams},
        {ModCallback.PRE_USE_ITEM, self.preUseItemRedKey, {CollectibleType.RED_KEY}}
    }
    self.customCallbacksUsed = {{ModCallbackCustom.POST_GRID_ENTITY_BROKEN, self.postGridEntityBrokenRockAlt, {GridEntityType.ROCK_ALT}}, {ModCallbackCustom.POST_GRID_ENTITY_INIT, self.postGridEntityInit}, {ModCallbackCustom.POST_NEW_ROOM_REORDERED, self.postNewRoomReordered}}
    self.customGridEntities = customGridEntities
    self.customTrapdoors = customTrapdoors
    self.disableAllSound = disableAllSound
    self.gameReorderedCallbacks = gameReorderedCallbacks
    self.pause = pause
    self.runInNFrames = runInNFrames
    self:initCustomStageMetadata()
end
function CustomStages.prototype.initCustomStageMetadata(self)
    if not isArray(nil, metadataJSON) then
        error("The IsaacScript standard library attempted to read the custom stage metadata from the \"customStageMetadata.lua\" file, but it was not an array.")
    end
    local customStagesLua = metadataJSON
    for ____, customStageLua in ipairs(customStagesLua) do
        self:initRoomTypeMap(customStageLua)
        self:initCustomTrapdoorDestination(customStageLua)
    end
end
function CustomStages.prototype.initRoomTypeMap(self, customStageLua)
    local roomTypeMap = getRoomTypeMap(nil, customStageLua)
    local customStage = __TS__ObjectAssign({}, customStageLua, {roomTypeMap = roomTypeMap})
    self.customStagesMap:set(customStage.name, customStage)
end
function CustomStages.prototype.initCustomTrapdoorDestination(self, customStageLua)
    self.customTrapdoors:registerCustomTrapdoorDestination(customStageLua.name, self.goToCustomStage)
end
function CustomStages.prototype.setStageRoomsData(self, customStage, rng, verbose)
    local level = game:GetLevel()
    local startingRoomGridIndex = level:GetStartingRoomIndex()
    for ____, room in ipairs(getRoomsInsideGrid(nil)) do
        do
            if room.SafeGridIndex == startingRoomGridIndex then
                goto __continue35
            end
            if room.Data == nil then
                goto __continue35
            end
            local roomType = room.Data.Type
            local roomShapeMap = customStage.roomTypeMap:get(roomType)
            if roomShapeMap == nil then
                if roomType == RoomType.DEFAULT then
                    logError((((("Failed to find any custom rooms for RoomType." .. RoomType[roomType]) .. " (") .. tostring(roomType)) .. ") for custom stage: ") .. customStage.name)
                end
                goto __continue35
            end
            local roomShape = room.Data.Shape
            local roomDoorSlotFlagMap = roomShapeMap:get(roomShape)
            if roomDoorSlotFlagMap == nil then
                logError((((((((("Failed to find any custom rooms for RoomType." .. RoomType[roomType]) .. " (") .. tostring(roomType)) .. ") + RoomShape.") .. RoomShape[roomShape]) .. " (") .. tostring(roomShape)) .. ") for custom stage: ") .. customStage.name)
                goto __continue35
            end
            local doorSlotFlags = room.Data.Doors
            local roomsMetadata = roomDoorSlotFlagMap:get(doorSlotFlags)
            if roomsMetadata == nil then
                local allDoorSlots = getDoorSlotsForRoomShape(nil, roomShape)
                local allDoorSlotFlags = doorSlotsToDoorSlotFlags(nil, allDoorSlots)
                roomsMetadata = roomDoorSlotFlagMap:get(allDoorSlotFlags)
                if roomsMetadata == nil then
                    logError((((((((("Failed to find any custom rooms for RoomType." .. RoomType[roomType]) .. " (") .. tostring(roomType)) .. ") + RoomShape.") .. RoomShape[roomShape]) .. " (") .. tostring(roomShape)) .. ") + all doors enabled for custom stage: ") .. customStage.name)
                    goto __continue35
                end
            end
            local randomRoom
            if roomType == RoomType.BOSS then
                if customStage.bossPool == nil then
                    goto __continue35
                end
                randomRoom = getRandomBossRoomFromPool(
                    nil,
                    roomsMetadata,
                    customStage.bossPool,
                    rng,
                    verbose
                )
            else
                randomRoom = getRandomCustomStageRoom(nil, roomsMetadata, rng, verbose)
            end
            local newRoomData = self.customStageCachedRoomData:get(randomRoom.variant)
            if newRoomData == nil then
                newRoomData = getRoomDataForTypeVariant(
                    nil,
                    roomType,
                    randomRoom.variant,
                    false,
                    true
                )
                if newRoomData == nil then
                    logError((("Failed to get the room data for room variant " .. tostring(randomRoom.variant)) .. " for custom stage: ") .. customStage.name)
                    goto __continue35
                end
                self.customStageCachedRoomData:set(randomRoom.variant, newRoomData)
            end
            room.Data = newRoomData
        end
        ::__continue35::
    end
end
function CustomStages.prototype.setCustomStage(self, name, firstFloor, streakText, verbose)
    if firstFloor == nil then
        firstFloor = true
    end
    if streakText == nil then
        streakText = true
    end
    if verbose == nil then
        verbose = false
    end
    local customStage = self.customStagesMap:get(name)
    assertDefined(nil, customStage, ("Failed to set the custom stage of \"" .. name) .. "\" because it was not found in the custom stages map. (Try restarting IsaacScript / recompiling the mod / restarting the game, and try again. If that does not work, you probably forgot to define it in your \"tsconfig.json\" file.) See the website for more details on how to set up custom stages.")
    local level = game:GetLevel()
    local stage = level:GetStage()
    local seeds = game:GetSeeds()
    local startSeed = seeds:GetStartSeed()
    local rng = newRNG(nil, startSeed)
    v.run.currentCustomStage = customStage
    v.run.firstFloor = firstFloor
    if stage == CUSTOM_FLOOR_STAGE then
        level:SetStage(LevelStage.BASEMENT_1, StageType.ORIGINAL)
    end
    local baseStage = customStage.baseStage == nil and DEFAULT_BASE_STAGE or customStage.baseStage
    if not firstFloor then
        baseStage = baseStage + 1
    end
    local baseStageType = customStage.baseStageType == nil and DEFAULT_BASE_STAGE_TYPE or customStage.baseStageType
    local reseed = stage >= baseStage
    setStage(nil, baseStage, baseStageType, reseed)
    musicManager:Disable()
    self:setStageRoomsData(customStage, rng, verbose)
    local targetStage = CUSTOM_FLOOR_STAGE
    local targetStageType = CUSTOM_FLOOR_STAGE_TYPE
    level:SetStage(targetStage, targetStageType)
    self.gameReorderedCallbacks:reorderedCallbacksSetStage(targetStage, targetStageType)
    if streakText then
        self.runInNFrames:runNextGameFrame(function()
            topStreakTextStart(nil)
        end)
    end
    local customStageMusic
    if customStage.music ~= nil then
        customStageMusic = Isaac.GetMusicIdByName(customStage.music)
        if customStageMusic == -1 then
            logError("Failed to get the music ID associated with the name of: " .. customStage.music)
        end
    end
    local music = (customStageMusic == nil or customStageMusic == -1) and getMusicForStage(nil, baseStage, baseStageType) or customStageMusic
    self.runInNFrames:runInNRenderFrames(
        function()
            musicManager:Enable()
            musicManager:Play(music)
            musicManager:UpdateVolume()
        end,
        MUSIC_DELAY_RENDER_FRAMES
    )
end
__TS__DecorateLegacy({Exported}, CustomStages.prototype, "setCustomStage", true)
function CustomStages.prototype.disableCustomStage(self)
    v.run.currentCustomStage = nil
end
__TS__DecorateLegacy({Exported}, CustomStages.prototype, "disableCustomStage", true)
return ____exports
