local ____lualib = require("lualib_bundle")
local __TS__ArraySome = ____lualib.__TS__ArraySome
local Map = ____lualib.Map
local __TS__New = ____lualib.__TS__New
local ____exports = {}
local ____isaac_2Dtypescript_2Ddefinitions = require("isaac-typescript-definitions")
local EffectVariant = ____isaac_2Dtypescript_2Ddefinitions.EffectVariant
local HeavenLightDoorSubType = ____isaac_2Dtypescript_2Ddefinitions.HeavenLightDoorSubType
local ____cachedClasses = require("core.cachedClasses")
local game = ____cachedClasses.game
local ____constants = require("core.constants")
local DISTANCE_OF_GRID_TILE = ____constants.DISTANCE_OF_GRID_TILE
local ____entities = require("functions.entities")
local getEntities = ____entities.getEntities
local ____entitiesSpecific = require("functions.entitiesSpecific")
local getEffects = ____entitiesSpecific.getEffects
local ____playerIndex = require("functions.playerIndex")
local getPlayers = ____playerIndex.getPlayers
local ____players = require("functions.players")
local getPlayerCloserThan = ____players.getPlayerCloserThan
local MAX_FIND_FREE_POSITION_ATTEMPTS = 100
function ____exports.anyEntityCloserThan(self, entities, position, distance)
    return __TS__ArraySome(
        entities,
        function(____, entity) return position:Distance(entity.Position) <= distance end
    )
end
--- Iterates over all players and checks if any player is close enough to the specified position.
-- 
-- Note that this function does not consider players with a non-undefined parent, since they are not
-- real players (e.g. the Strawman Keeper).
function ____exports.anyPlayerCloserThan(self, position, distance)
    local players = getPlayers(nil)
    return __TS__ArraySome(
        players,
        function(____, player) return player.Position:Distance(position) <= distance end
    )
end
--- Helper function to get a room position that is not overlapping with a grid entity, a heaven door,
-- or a player. The `Room.FindFreePickupSpawnPosition` method will return locations that overlap
-- with heaven doors and partially overlap with players, if the thing being spawned is bigger than a
-- tile (like a Blood Donation Machine). Use this function instead if you want to account for those
-- specific situations.
-- 
-- @param startingPosition The position to start searching from. If this position is not overlapping
-- with anything, then it will be returned.
-- @param avoidActiveEntities Optional. Default is false.
-- @param minimumDistance Optional. If specified, will ensure that the randomly generated position
-- is equal to or greater than the distance provided.
function ____exports.findFreePosition(self, startingPosition, avoidActiveEntities, minimumDistance)
    if avoidActiveEntities == nil then
        avoidActiveEntities = false
    end
    local room = game:GetRoom()
    local heavenDoors = getEffects(nil, EffectVariant.HEAVEN_LIGHT_DOOR, HeavenLightDoorSubType.HEAVEN_DOOR)
    do
        local initialStep = 0
        while initialStep < MAX_FIND_FREE_POSITION_ATTEMPTS do
            do
                local position = room:FindFreePickupSpawnPosition(startingPosition, initialStep, avoidActiveEntities)
                local closePlayer = getPlayerCloserThan(nil, position, DISTANCE_OF_GRID_TILE)
                if closePlayer ~= nil then
                    goto __continue8
                end
                local isCloseHeavenDoor = ____exports.anyEntityCloserThan(nil, heavenDoors, position, DISTANCE_OF_GRID_TILE)
                if isCloseHeavenDoor then
                    goto __continue8
                end
                if minimumDistance ~= nil then
                    local distance = startingPosition:Distance(position)
                    if distance < minimumDistance then
                        goto __continue8
                    end
                end
                return position
            end
            ::__continue8::
            initialStep = initialStep + 1
        end
    end
    return room:FindFreePickupSpawnPosition(startingPosition)
end
--- Helper function to get a map containing the positions of every entity in the current room.
-- 
-- This is useful for rewinding entity positions at a later time. Also see `setEntityPositions`.
-- 
-- @param entities Optional. If provided, will only get the positions of the provided entities. Use
-- this with cached entities to avoid invoking the `Isaac.GetRoomEntities` method
-- multiple times.
function ____exports.getEntityPositions(self, entities)
    if entities == nil then
        entities = getEntities(nil)
    end
    local entityPositions = __TS__New(Map)
    for ____, entity in ipairs(entities) do
        local ptrHash = GetPtrHash(entity)
        entityPositions:set(ptrHash, entity.Position)
    end
    return entityPositions
end
--- Helper function to get a map containing the velocities of every entity in the current room.
-- 
-- This is useful for rewinding entity velocities at a later time. Also see `setEntityVelocities`.
-- 
-- @param entities Optional. If provided, will only get the velocities of the provided entities. Use
-- this with cached entities to avoid invoking the `Isaac.GetRoomEntities` method
-- multiple times.
function ____exports.getEntityVelocities(self, entities)
    if entities == nil then
        entities = getEntities(nil)
    end
    local entityVelocities = __TS__New(Map)
    for ____, entity in ipairs(entities) do
        local ptrHash = GetPtrHash(entity)
        entityVelocities:set(ptrHash, entity.Velocity)
    end
    return entityVelocities
end
--- Helper function to set the position of every entity in the room based on a map of positions. If
-- an entity is found that does not have matching element in the provided map, then that entity will
-- be skipped.
-- 
-- This function is useful for rewinding entity positions at a later time. Also see
-- `getEntityPositions`.
-- 
-- @param entityPositions The map providing the positions for every entity.
-- @param entities Optional. If provided, will only set the positions of the provided entities. Use
-- this with cached entities to avoid invoking the `Isaac.GetRoomEntities` method
-- multiple times.
function ____exports.setEntityPositions(self, entityPositions, entities)
    if entities == nil then
        entities = getEntities(nil)
    end
    for ____, entity in ipairs(entities) do
        local ptrHash = GetPtrHash(entity)
        local entityPosition = entityPositions:get(ptrHash)
        if entityPosition ~= nil then
            entity.Position = entityPosition
        end
    end
end
--- Helper function to set the velocity of every entity in the room based on a map of velocities. If
-- an entity is found that does not have matching element in the provided map, then that entity will
-- be skipped.
-- 
-- This function is useful for rewinding entity velocities at a later time. Also see
-- `getEntityVelocities`.
-- 
-- @param entityVelocities The map providing the velocities for every entity.
-- @param entities Optional. If provided, will only set the velocities of the provided entities. Use
-- this with cached entities to avoid invoking the `Isaac.GetRoomEntities` method
-- multiple times.
function ____exports.setEntityVelocities(self, entityVelocities, entities)
    if entities == nil then
        entities = getEntities(nil)
    end
    for ____, entity in ipairs(entities) do
        local ptrHash = GetPtrHash(entity)
        local entityVelocity = entityVelocities:get(ptrHash)
        if entityVelocity ~= nil then
            entity.Velocity = entityVelocity
        end
    end
end
return ____exports
