local ____lualib = require("lualib_bundle")
local __TS__ArrayFilter = ____lualib.__TS__ArrayFilter
local ____exports = {}
local getChargesToAddWithAAAModifier, shouldPlayFullRechargeSound
local ____isaac_2Dtypescript_2Ddefinitions = require("isaac-typescript-definitions")
local ActiveSlot = ____isaac_2Dtypescript_2Ddefinitions.ActiveSlot
local CollectibleType = ____isaac_2Dtypescript_2Ddefinitions.CollectibleType
local ItemConfigChargeType = ____isaac_2Dtypescript_2Ddefinitions.ItemConfigChargeType
local SoundEffect = ____isaac_2Dtypescript_2Ddefinitions.SoundEffect
local TrinketType = ____isaac_2Dtypescript_2Ddefinitions.TrinketType
local ____cachedClasses = require("core.cachedClasses")
local game = ____cachedClasses.game
local sfxManager = ____cachedClasses.sfxManager
local ____collectibles = require("functions.collectibles")
local getCollectibleChargeType = ____collectibles.getCollectibleChargeType
local getCollectibleMaxCharges = ____collectibles.getCollectibleMaxCharges
local ____playerCollectibles = require("functions.playerCollectibles")
local getActiveItemSlots = ____playerCollectibles.getActiveItemSlots
local ____playerIndex = require("functions.playerIndex")
local getPlayers = ____playerIndex.getPlayers
local ____roomShape = require("functions.roomShape")
local getRoomShapeCharges = ____roomShape.getRoomShapeCharges
--- Helper function to add a charge to the player's active item. Will flash the HUD and play the
-- appropriate sound effect, depending on whether the charge is partially full or completely full.
-- 
-- If the player's active item is already fully charged, then this function will return 0 and not
-- flash the HUD or play a sound effect.
-- 
-- This function will take the following things into account:
-- - The Battery
-- - AAA Battery
-- 
-- @param player The player to grant the charges to.
-- @param activeSlot Optional. The slot to grant the charges to. Default is `ActiveSlot.PRIMARY`.
-- @param numCharges Optional. The amount of charges to grant. Default is 1.
-- @param playSoundEffect Optional. Whether to play a charge-related sound effect. Default is true.
-- @returns The amount of charges that were actually granted. For example, if the active item was
-- only one away from a full charge, but the `numCharges` provided to this function was 2,
-- then this function would return 1.
function ____exports.addCharge(self, player, activeSlot, numCharges, playSoundEffect)
    if activeSlot == nil then
        activeSlot = ActiveSlot.PRIMARY
    end
    if numCharges == nil then
        numCharges = 1
    end
    if playSoundEffect == nil then
        playSoundEffect = true
    end
    local hud = game:GetHUD()
    local chargesAwayFromMax = ____exports.getChargesAwayFromMax(nil, player, activeSlot)
    local chargesToAdd = math.min(numCharges, chargesAwayFromMax)
    local modifiedChargesToAdd = getChargesToAddWithAAAModifier(nil, player, activeSlot, chargesToAdd)
    local totalCharge = ____exports.getTotalCharge(nil, player, activeSlot)
    local newCharge = totalCharge + modifiedChargesToAdd
    if newCharge == totalCharge then
        return 0
    end
    player:SetActiveCharge(newCharge, activeSlot)
    hud:FlashChargeBar(player, activeSlot)
    if playSoundEffect then
        ____exports.playChargeSoundEffect(nil, player, activeSlot)
    end
    return modifiedChargesToAdd
end
--- Helper function to add a charge to one of a player's active items, emulating what happens when a
-- room is cleared.
-- 
-- This function will take the following things into account:
-- - L rooms and 2x2 rooms granting a double charge
-- - The Battery
-- - AAA Battery
-- - Not charging active items with `chargetype="special"`
-- 
-- @param player The player to grant the charges to.
-- @param activeSlot Optional. The active item slot to grant the charges to. Default is
-- `ActiveSlot.PRIMARY`.
-- @param bigRoomDoubleCharge Optional. If set to false, it will treat the current room as a 1x1
-- room for the purposes of calculating how much charge to grant. Default
-- is true.
-- @param playSoundEffect Optional. Whether to play a charge-related sound effect. Default is true.
function ____exports.addRoomClearChargeToSlot(self, player, activeSlot, bigRoomDoubleCharge, playSoundEffect)
    if activeSlot == nil then
        activeSlot = ActiveSlot.PRIMARY
    end
    if bigRoomDoubleCharge == nil then
        bigRoomDoubleCharge = true
    end
    if playSoundEffect == nil then
        playSoundEffect = true
    end
    local activeItem = player:GetActiveItem(activeSlot)
    if activeItem == CollectibleType.NULL then
        return
    end
    local chargeType = getCollectibleChargeType(nil, activeItem)
    if chargeType == ItemConfigChargeType.SPECIAL then
        return
    end
    local room = game:GetRoom()
    local roomShape = room:GetRoomShape()
    local numCharges = bigRoomDoubleCharge and getRoomShapeCharges(nil, roomShape) or 1
    if chargeType == ItemConfigChargeType.TIMED then
        numCharges = getCollectibleMaxCharges(nil, activeItem)
    end
    ____exports.addCharge(
        nil,
        player,
        activeSlot,
        numCharges,
        playSoundEffect
    )
end
function getChargesToAddWithAAAModifier(self, player, activeSlot, chargesToAdd)
    local hasAAABattery = player:HasTrinket(TrinketType.AAA_BATTERY)
    if not hasAAABattery then
        return chargesToAdd
    end
    local chargesAwayFromMax = ____exports.getChargesAwayFromMax(nil, player, activeSlot)
    local AAABatteryShouldApply = chargesToAdd == chargesAwayFromMax - 1
    return AAABatteryShouldApply and chargesToAdd + 1 or chargesToAdd
end
--- Helper function to get the amount of charges away from the maximum charge that a particular
-- player is.
-- 
-- This function accounts for The Battery. For example, if the player has 2/6 charges on a D6, this
-- function will return 10 (because there are 4 charges remaining on the base charge and 6 charges
-- remaining on The Battery charge).
-- 
-- @param player The player to get the charges from.
-- @param activeSlot Optional. The slot to get the charges from. Default is `ActiveSlot.PRIMARY`.
function ____exports.getChargesAwayFromMax(self, player, activeSlot)
    if activeSlot == nil then
        activeSlot = ActiveSlot.PRIMARY
    end
    local totalCharge = ____exports.getTotalCharge(nil, player, activeSlot)
    local activeItem = player:GetActiveItem(activeSlot)
    local hasBattery = player:HasCollectible(CollectibleType.BATTERY)
    local maxCharges = getCollectibleMaxCharges(nil, activeItem)
    local effectiveMaxCharges = hasBattery and maxCharges * 2 or maxCharges
    return effectiveMaxCharges - totalCharge
end
--- Helper function to get the combined normal charge and the battery charge for the player's active
-- item. This is useful because you have to add these two values together when setting the active
-- charge.
-- 
-- @param player The player to get the charges from.
-- @param activeSlot Optional. The slot to get the charges from. Default is `ActiveSlot.PRIMARY`.
function ____exports.getTotalCharge(self, player, activeSlot)
    if activeSlot == nil then
        activeSlot = ActiveSlot.PRIMARY
    end
    local activeCharge = player:GetActiveCharge(activeSlot)
    local batteryCharge = player:GetBatteryCharge(activeSlot)
    return activeCharge + batteryCharge
end
--- Helper function to play the appropriate sound effect for a player after getting one or more
-- charges on their active item. (There is a different sound depending on whether the item is fully
-- charged.)
-- 
-- @param player The player to play the sound effect for.
-- @param activeSlot Optional. The slot that was just charged. Default is `ActiveSlot.PRIMARY`.
function ____exports.playChargeSoundEffect(self, player, activeSlot)
    if activeSlot == nil then
        activeSlot = ActiveSlot.PRIMARY
    end
    for ____, soundEffect in ipairs({SoundEffect.BATTERY_CHARGE, SoundEffect.BEEP}) do
        sfxManager:Stop(soundEffect)
    end
    local chargeSoundEffect = shouldPlayFullRechargeSound(nil, player, activeSlot) and SoundEffect.BATTERY_CHARGE or SoundEffect.BEEP
    sfxManager:Play(chargeSoundEffect)
end
function shouldPlayFullRechargeSound(self, player, activeSlot)
    local activeItem = player:GetActiveItem(activeSlot)
    local activeCharge = player:GetActiveCharge(activeSlot)
    local batteryCharge = player:GetBatteryCharge(activeSlot)
    local hasBattery = player:HasCollectible(CollectibleType.BATTERY)
    local maxCharges = getCollectibleMaxCharges(nil, activeItem)
    if not hasBattery then
        return activeCharge == maxCharges
    end
    return batteryCharge == maxCharges or activeCharge == maxCharges and batteryCharge == 0
end
--- Helper function to add a charge to a player's active item(s), emulating what happens when a room
-- is cleared.
-- 
-- This function will take the following things into account:
-- - 2x2 rooms and L rooms granting a double charge
-- - The Battery
-- - AAA Battery
-- - Not charging active items with `chargetype="special"`
-- 
-- @param player The player to grant the charges to.
-- @param bigRoomDoubleCharge Optional. If set to false, it will treat the current room as a 1x1
-- room for the purposes of calculating how much charge to grant. Default
-- is true.
-- @param playSoundEffect Optional. Whether to play a charge-related sound effect. Default is true.
function ____exports.addRoomClearCharge(self, player, bigRoomDoubleCharge, playSoundEffect)
    if bigRoomDoubleCharge == nil then
        bigRoomDoubleCharge = true
    end
    if playSoundEffect == nil then
        playSoundEffect = true
    end
    for ____, activeSlot in ipairs({ActiveSlot.PRIMARY, ActiveSlot.SECONDARY, ActiveSlot.POCKET}) do
        ____exports.addRoomClearChargeToSlot(
            nil,
            player,
            activeSlot,
            bigRoomDoubleCharge,
            playSoundEffect
        )
    end
end
--- Helper function to add a charge to every player's active item, emulating what happens when a room
-- is cleared.
-- 
-- This function will take the following things into account:
-- - L rooms and 2x2 rooms granting a double charge
-- - The Battery
-- - AAA Battery
-- 
-- @param bigRoomDoubleCharge Optional. If set to false, it will treat the current room as a 1x1
-- room for the purposes of calculating how much charge to grant. Default
-- is true.
function ____exports.addRoomClearCharges(self, bigRoomDoubleCharge)
    if bigRoomDoubleCharge == nil then
        bigRoomDoubleCharge = true
    end
    for ____, player in ipairs(getPlayers(nil)) do
        ____exports.addRoomClearCharge(nil, player, bigRoomDoubleCharge)
    end
end
--- Helper function to find the active slots that the player has the corresponding collectible type
-- in and have enough charge to be used. Returns an empty array if the player does not have the
-- collectible in any active slot or does not have enough charges.
function ____exports.getUsableActiveItemSlots(self, player, collectibleType)
    local maxCharges = getCollectibleMaxCharges(nil, collectibleType)
    local activeSlots = getActiveItemSlots(nil, player, collectibleType)
    return __TS__ArrayFilter(
        activeSlots,
        function(____, activeSlot)
            local totalCharge = ____exports.getTotalCharge(nil, player, activeSlot)
            return totalCharge >= maxCharges
        end
    )
end
--- Helper function to check if a player's active item is "double charged", meaning that it has both
-- a full normal charge and a full charge from The Battery.
-- 
-- @param player The player to check.
-- @param activeSlot Optional. The slot to check. Default is `ActiveSlot.PRIMARY`.
function ____exports.isActiveSlotDoubleCharged(self, player, activeSlot)
    if activeSlot == nil then
        activeSlot = ActiveSlot.PRIMARY
    end
    local collectibleType = player:GetActiveItem(activeSlot)
    local batteryCharge = player:GetBatteryCharge(activeSlot)
    local maxCharges = getCollectibleMaxCharges(nil, collectibleType)
    return batteryCharge >= maxCharges
end
return ____exports
