local ____exports = {}
local ____isaac_2Dtypescript_2Ddefinitions = require("isaac-typescript-definitions")
local CollectibleType = ____isaac_2Dtypescript_2Ddefinitions.CollectibleType
local EntityType = ____isaac_2Dtypescript_2Ddefinitions.EntityType
local NullItemID = ____isaac_2Dtypescript_2Ddefinitions.NullItemID
local PlayerType = ____isaac_2Dtypescript_2Ddefinitions.PlayerType
local TrinketType = ____isaac_2Dtypescript_2Ddefinitions.TrinketType
local ____constants = require("core.constants")
local MAX_TAINTED_SAMSON_BERSERK_CHARGE = ____constants.MAX_TAINTED_SAMSON_BERSERK_CHARGE
local TAINTED_SAMSON_BERSERK_CHARGE_FROM_TAKING_DAMAGE = ____constants.TAINTED_SAMSON_BERSERK_CHARGE_FROM_TAKING_DAMAGE
local ____MysteriousPaperEffect = require("enums.MysteriousPaperEffect")
local MysteriousPaperEffect = ____MysteriousPaperEffect.MysteriousPaperEffect
local ____characters = require("functions.characters")
local getCharacterDeathAnimationName = ____characters.getCharacterDeathAnimationName
local ____frames = require("functions.frames")
local onGameFrame = ____frames.onGameFrame
local ____playerHealth = require("functions.playerHealth")
local getPlayerMaxHeartContainers = ____playerHealth.getPlayerMaxHeartContainers
local ____players = require("functions.players")
local getPlayerNumHitsRemaining = ____players.getPlayerNumHitsRemaining
local hasLostCurse = ____players.hasLostCurse
local isKeeper = ____players.isKeeper
local ____sprites = require("functions.sprites")
local getLastFrameOfAnimation = ____sprites.getLastFrameOfAnimation
local ____trinketGive = require("functions.trinketGive")
local giveTrinketsBack = ____trinketGive.giveTrinketsBack
local temporarilyRemoveTrinket = ____trinketGive.temporarilyRemoveTrinket
local ____trinkets = require("functions.trinkets")
local getMysteriousPaperEffectForFrame = ____trinkets.getMysteriousPaperEffectForFrame
--- Helper function to determine if the player will be revived by the Heartbreak collectible if they
-- take fatal damage. This is contingent on the character that they are playing as and the amount of
-- broken hearts that they already have.
function ____exports.willReviveFromHeartbreak(self, player)
    if not player:HasCollectible(CollectibleType.HEARTBREAK) then
        return false
    end
    local maxHeartContainers = getPlayerMaxHeartContainers(nil, player)
    local numBrokenHeartsThatWillBeAdded = isKeeper(nil, player) and 1 or 2
    local brokenHearts = player:GetBrokenHearts()
    local numBrokenHeartsAfterRevival = numBrokenHeartsThatWillBeAdded + brokenHearts
    return maxHeartContainers > numBrokenHeartsAfterRevival
end
--- Helper function to determine if the Spirit Shackles item is in an enabled state. (It can be
-- either enabled or disabled.)
function ____exports.willReviveFromSpiritShackles(self, player)
    if not player:HasCollectible(CollectibleType.SPIRIT_SHACKLES) then
        return false
    end
    local effects = player:GetEffects()
    local spiritShacklesEnabled = not effects:HasNullEffect(NullItemID.SPIRIT_SHACKLES_DISABLED)
    local playerInSoulForm = effects:HasNullEffect(NullItemID.SPIRIT_SHACKLES_SOUL)
    return spiritShacklesEnabled and not playerInSoulForm
end
--- Uses the player's current health and other miscellaneous things to determine if incoming damage
-- will be fatal.
function ____exports.isDamageToPlayerFatal(self, player, amount, source, lastDamageGameFrame)
    local character = player:GetPlayerType()
    local effects = player:GetEffects()
    local isBerserk = effects:HasCollectibleEffect(CollectibleType.BERSERK)
    if character == PlayerType.JACOB_B and source.Type == EntityType.DARK_ESAU then
        return false
    end
    if isBerserk then
        return false
    end
    local berserkChargeAfterHit = player.SamsonBerserkCharge + TAINTED_SAMSON_BERSERK_CHARGE_FROM_TAKING_DAMAGE
    if character == PlayerType.SAMSON_B and berserkChargeAfterHit >= MAX_TAINTED_SAMSON_BERSERK_CHARGE then
        return false
    end
    if ____exports.willReviveFromSpiritShackles(nil, player) then
        return false
    end
    if character == PlayerType.JACOB_2_B then
        return true
    end
    if hasLostCurse(nil, player) then
        return true
    end
    local playerNumAllHearts = getPlayerNumHitsRemaining(nil, player)
    if amount < playerNumAllHearts then
        return false
    end
    if ____exports.willReviveFromHeartbreak(nil, player) then
        return false
    end
    if player:HasCollectible(CollectibleType.BROKEN_GLASS_CANNON) and onGameFrame(nil, lastDamageGameFrame) then
        return false
    end
    local hearts = player:GetHearts()
    local eternalHearts = player:GetEternalHearts()
    local soulHearts = player:GetSoulHearts()
    local boneHearts = player:GetBoneHearts()
    if hearts > 0 and soulHearts > 0 or hearts > 0 and boneHearts > 0 or soulHearts > 0 and boneHearts > 0 or soulHearts > 0 and eternalHearts > 0 or boneHearts >= 2 then
        return false
    end
    return true
end
--- Assuming that we are on the frame of fatal damage, this function returns whether Mysterious Paper
-- would rotate to Missing Poster on the frame that the "Game Over" screen would appear (which would
-- subsequently save the player from fatal damage).
-- 
-- Mysterious Paper rotates between the 4 items on every frame, in order. The formula for whether
-- Mysterious Paper be Missing Power is: `gameFrameCount % 4 === 3`
function ____exports.willMysteriousPaperRevive(self, player)
    local sprite = player:GetSprite()
    local character = player:GetPlayerType()
    local animation = getCharacterDeathAnimationName(nil, character)
    local deathAnimationFrames = getLastFrameOfAnimation(nil, sprite, animation)
    local frameOfDeath = player.FrameCount + deathAnimationFrames
    local mysteriousPaperEffect = getMysteriousPaperEffectForFrame(nil, player, frameOfDeath)
    if mysteriousPaperEffect == nil then
        return false
    end
    return mysteriousPaperEffect == MysteriousPaperEffect.MISSING_POSTER
end
--- The `EntityPlayer.WillPlayerRevive` method does not properly account for Mysterious Paper, so use
-- this helper function instead for more robust revival detection.
function ____exports.willPlayerRevive(self, player)
    local trinketSituation = temporarilyRemoveTrinket(nil, player, TrinketType.MYSTERIOUS_PAPER)
    local willRevive = player:WillPlayerRevive() or trinketSituation ~= nil and ____exports.willMysteriousPaperRevive(nil, player)
    giveTrinketsBack(nil, player, trinketSituation)
    return willRevive
end
return ____exports
