---@class (exact) DeathAnimation : AvatarModule プレイヤーが死亡した際のキャラクターがヘリコプターで回収されるアニメーションを管理するクラス
---@field public dummyAvatarRoot? ModelPart 死亡アニメーションに使用されるダミーのアバターのルート。アバターが未生成の場合はnilが入っている。
---@field package deathCheckDelay integer 死亡チェックの遅延カウンター
---@field package animationCount integer 死亡アニメーションの再生カウンター
---@field package animationPos Vector3 アニメーションを再生している場所の座標
---@field package animationRot number アニメーションを再生している向き（度数法で示す）
---@field package costumeIndex integer 死亡アニメーションのコスチュームのインデックス
---@field package isPlayerInvisible boolean プレイヤーモデルが不可視状態かどうか
---@field package isScriptLoaded boolean スクリプトを全て読み込んだかどうか
---@field public removeUnsafeModel fun(target?: ModelPart) 存在しないかもしれないモデルパーツを安全に削除する
---@field public spawnHelicopterParticles fun(self: DeathAnimation) ヘリコプターの出現/消滅パーティクルを生成する
---@field public generateDummyAvatar fun(self: DeathAnimation, parent: ModelPart) 死亡アニメーション用のダミーアバターを生成する
---@field public resetDummyAvatar fun(avatarRoot: ModelPart) ダミーアバター状態をリセットする
---@field public setPhase1Pose fun(avatarRoot: ModelPart) ダミーアバターをフェーズ1のポーズにする
---@field public setPhase2Pose fun(avatarRoot: ModelPart) ダミーアバターをフェーズ2のポーズにする
---@field package play fun(self: DeathAnimation) 死亡アニメーションを再生する
---@field package stop fun(self: DeathAnimation) 死亡アニメーションを停止する

DeathAnimation = {
    ---コンストラクタ
    ---@param parent Avatar アバターのメインクラスへの参照
    ---@return DeathAnimation
    new = function (parent)
        ---@type DeathAnimation
        local instance = Avatar.instantiate(DeathAnimation, AvatarModule, parent)

        instance.dummyAvatarRoot = models.models.death_animation.Avatar
        instance.deathCheckDelay = -1
        instance.animationCount = 0
        instance.animationPos = vectors.vec3()
        instance.animationRot = 0
        instance.costumeIndex = 1
        instance.isPlayerInvisible = false
        instance.isScriptLoaded = false

        return instance
    end;

    ---初期化関数
    ---@param self DeathAnimation
    init = function (self)
        AvatarModule.init(self)

        events.TICK:register(function ()
            local health = player:getHealth()
            if self.deathCheckDelay == 0 and health == 0 then
                self:play()
                models.models.main:setVisible(false)
                for _, vanillaModel in ipairs({vanilla_model.RIGHT_ITEM, vanilla_model.LEFT_ITEM, vanilla_model.ELYTRA}) do
                    vanillaModel:setVisible(false)
                end
                self.isPlayerInvisible = true
            elseif self.isPlayerInvisible and health > 0 then
                models.models.main:setVisible(true)
                for _, vanillaModel in ipairs({vanilla_model.RIGHT_ITEM, vanilla_model.LEFT_ITEM, vanilla_model.ELYTRA}) do
                    vanillaModel:setVisible(true)
                end
                self.isPlayerInvisible = false
            end
            self.deathCheckDelay = math.max(self.deathCheckDelay - 1, -1)
        end)

        events.DAMAGE:register(function ()
            self.deathCheckDelay = 1
        end)

        self.parent.avatarEvents.SCRIPT_INIT:register(function ()
            self.isScriptLoaded = true
        end)
    end;

    ---存在しないかもしれないモデルパーツを安全に削除する。
    ---@param target? ModelPart 削除対象のモデルパーツ
    removeUnsafeModel = function (target)
        if target ~= nil then
            target:getParent():removeChild(target)
            target:remove()
        end
    end;

    ---ヘリコプターの出現/消滅パーティクルを生成する。
    ---@param self DeathAnimation
    spawnHelicopterParticles = function (self)
        local helicopterPos = self.parent.modelUtils.getModelWorldPos(models.models.death_animation.Helicopter)
        for _ = 1, 100 do
            particles:newParticle(self.parent.compatibilityUtils:checkParticle("minecraft:poof"), helicopterPos:copy():add(vectors.rotateAroundAxis(self.animationRot, math.random() * 9.375 - 4.6875, math.random() * 11.125 - 5.5625, math.random() * 23.875 - 11.9375, 0, math.abs(helicopterPos.y), 0)))
        end
    end;

    ---死亡アニメーション用のダミーアバターを生成する。
    ---@param self DeathAnimation
    ---@param parent ModelPart ダミーアバターをアタッチする親のモデルパーツ
    generateDummyAvatar = function (self, parent)
        for _, modelPart in ipairs({models.models.main.Avatar.Head, models.models.main.Avatar.Head.HeadRing}) do
            modelPart:setVisible(true)
        end
        models.models.main.Avatar.Head:setPrimaryRenderType()
        models.models.main.Avatar.Head:setOpacity(1)
        local isArmorVisible = {
            helmet = self.parent.armor.isArmorVisible.helmet;
            chestplate = self.parent.armor.isArmorVisible.chestplate;
            leggings = self.parent.armor.isArmorVisible.leggings;
            boots = self.parent.armor.isArmorVisible.boots;
        }
        if isArmorVisible.helmet then
            self.parent.armor:setHelmet(world.newItem(self.parent.compatibilityUtils:checkItem("minecraft:air")))
        end
        if isArmorVisible.chestplate then
            self.parent.armor:setChestplate(world.newItem(self.parent.compatibilityUtils:checkItem("minecraft:air")))
        end
        if isArmorVisible.leggings then
            self.parent.armor:setLeggings(world.newItem(self.parent.compatibilityUtils:checkItem("minecraft:air")))
        end
        if isArmorVisible.boots then
            self.parent.armor:setBoots(world.newItem(self.parent.compatibilityUtils:checkItem("minecraft:air")))
        end
        self.parent.physics:disable()
        if self.parent.characterData.deathAnimation.callbacks ~= nil and self.parent.characterData.deathAnimation.callbacks.onBeforeModelCopy ~= nil then
            self.parent.characterData.deathAnimation.callbacks.onBeforeModelCopy(self.parent.characterData, self.isScriptLoaded)
        end

        parent:addChild(self.parent.modelUtils:copyModel(models.models.main.Avatar))
        parent.Avatar.Head.FaceParts.Eyes.EyeRight:setUVPixels(self.parent.characterData.faceParts.leftEye.TIRED:copy():scale(6))
        parent.Avatar.Head.FaceParts.Eyes.EyeLeft:setUVPixels(self.parent.characterData.faceParts.rightEye.TIRED:copy():scale(6))
        parent.Avatar.Head.HeadRing:setRot()
        for _, modelPart in ipairs({parent.Avatar.UpperBody.Arms.RightArm.RightArmBottom.RightItemPivot, parent.Avatar.UpperBody.Arms.LeftArm.LeftArmBottom.LeftItemPivot}) do
            modelPart:remove()
        end
        local unsafeModels = {parent.Avatar.Head.FaceParts.Mouth, parent.Avatar.Head.ArmorH, parent.Avatar.UpperBody.Body.ArmorB, parent.Avatar.UpperBody.Arms.RightArm.ArmorRA, parent.Avatar.UpperBody.Arms.RightArm.RightArmBottom.ArmorRAB, parent.Avatar.UpperBody.Arms.LeftArm.ArmorLA, parent.Avatar.UpperBody.Arms.LeftArm.LeftArmBottom.ArmorLAB, parent.Avatar.LowerBody.Legs.RightLeg.ArmorRL, parent.Avatar.LowerBody.Legs.RightLeg.RightLegBottom.ArmorRLB, parent.Avatar.LowerBody.Legs.LeftLeg.ArmorLL, parent.Avatar.LowerBody.Legs.LeftLeg.LeftLegBottom.ArmorLLB}
        for i = 1, 11 do
            self.removeUnsafeModel(unsafeModels[i])
        end
        if parent.Avatar.UpperBody.Body.Gun ~= nil then
            if self.parent.characterData.gun.gunPosition.put.type == "BODY" then
                local leftHanded = player:isLeftHanded()
                parent.Avatar.UpperBody.Body.Gun:setPos(vectors.vec3(0, 12, 0):add(self.parent.characterData.gun.gunPosition.put.pos[leftHanded and "left" or "right"]))
                parent.Avatar.UpperBody.Body.Gun:setRot(self.parent.characterData.gun.gunPosition.put.rot[leftHanded and "left" or "right"])
            else
                parent.Avatar.UpperBody.Body.Gun:remove()
            end
        end

        if isArmorVisible.helmet then
            self.parent.armor:setHelmet(self.parent.armor.armorSlotItems[1])
        end
        if isArmorVisible.chestplate then
            self.parent.armor:setChestplate(self.parent.armor.armorSlotItems[2])
        end
        if isArmorVisible.leggings then
            self.parent.armor:setLeggings(self.parent.armor.armorSlotItems[3])
        end
        if isArmorVisible.boots then
            self.parent.armor:setBoots(self.parent.armor.armorSlotItems[4])
        end
        self.parent.physics:enable()
        if self.parent.characterData.deathAnimation.callbacks ~= nil and self.parent.characterData.deathAnimation.callbacks.onAfterModelCopy ~= nil then
            self.parent.characterData.deathAnimation.callbacks.onAfterModelCopy(self.parent.characterData, self.isScriptLoaded)
        end
    end;

    ---ダミーアバター状態をリセットする。
    ---@param avatarRoot ModelPart ダミーアバターのルート
    resetDummyAvatar = function (avatarRoot)
        for _, modelPart in ipairs({avatarRoot, avatarRoot.Head, avatarRoot.UpperBody, avatarRoot.UpperBody.Body, avatarRoot.UpperBody.Arms, avatarRoot.UpperBody.Arms.RightArm, avatarRoot.UpperBody.Arms.RightArm.RightArmBottom, avatarRoot.UpperBody.Arms.LeftArm, avatarRoot.UpperBody.Arms.LeftArm.LeftArmBottom, avatarRoot.LowerBody, avatarRoot.LowerBody.Legs, avatarRoot.LowerBody.Legs.RightLeg, avatarRoot.LowerBody.Legs.RightLeg.RightLegBottom, avatarRoot.LowerBody.Legs.LeftLeg, avatarRoot.LowerBody.Legs.LeftLeg.LeftLegBottom}) do
            modelPart:setPos()
            modelPart:setRot()
            modelPart:setScale()
        end
    end;

    ---ダミーアバターをフェーズ1のポーズにする。
    ---@param avatarRoot ModelPart ダミーアバターのルート
    setPhase1Pose = function (avatarRoot)
        avatarRoot:setPos(0, -12, 0)
        avatarRoot.Head:setRot(-30, 0, 0)
        avatarRoot.UpperBody.Arms.RightArm:setRot(35, 0, -20)
        avatarRoot.UpperBody.Arms.LeftArm:setRot(35, 0, 20)
        avatarRoot.LowerBody.Legs.RightLeg:setRot(90, -10, 0)
        avatarRoot.LowerBody.Legs.LeftLeg:setRot(90, 10, 0)
    end;

    ---ダミーアバターをフェーズ2のポーズにする。
    ---@param avatarRoot ModelPart ダミーアバターのルート
    setPhase2Pose = function (avatarRoot)
        avatarRoot:setPos(3, -210, 2)
        avatarRoot:setRot(105, 75, 90)
        avatarRoot.Head:setRot(0, -40, 0)
        avatarRoot.UpperBody.Arms.RightArm:setRot(47.5, 0, 20)
        avatarRoot.UpperBody.Arms.LeftArm:setRot(-30, 0, -15)
        avatarRoot.LowerBody.Legs.RightLeg:setRot(80, 0, 0)
        avatarRoot.LowerBody.Legs.RightLeg.RightLegBottom:setRot(-75, 0, 0)
        avatarRoot.LowerBody.Legs.LeftLeg:setRot(10, 0, 0)
        avatarRoot:setLight()
    end;

    ---死亡アニメーションを再生する。
    ---@param self DeathAnimation
    play = function (self)
        self:stop()
        self.costumeIndex = self.parent.costume.currentCostume

        --ダミーアバターを生成する。
        local unsafeModels = {models.models.death_animation.Avatar, models.models.death_animation.Helicopter.RopeLadder.RopeLadder2.RopeLadder3.RopeLadder4.RopeLadder5.RopeLadder6.RopeLadder7.RopeLadder8.RopeLadder9.RopeLadder10.RopeLadder11.RopeLadder12.RopeLadder13.RopeLadder14.Avatar}
        for i = 1, 2 do
            self.removeUnsafeModel(unsafeModels[i])
        end
        self:generateDummyAvatar(models.models.death_animation)

        --死亡アニメーションを生成する。
        self.resetDummyAvatar(models.models.death_animation.Avatar)
        self.setPhase1Pose(models.models.death_animation.Avatar)
        self.animationPos = player:getPos()
        models.models.death_animation:setPos(self.animationPos:copy():scale(16))
        self.animationRot = (player:getBodyYaw() * -1 + 180) % 360
        models.models.death_animation:setRot(0, self.animationRot)
        models.models.death_animation:setVisible(true)
        animations["models.death_animation"]["death_animation"]:play()
        if self.parent.characterData.deathAnimation.callbacks ~= nil and self.parent.characterData.deathAnimation.callbacks.onPhase1 ~= nil then
            self.parent.characterData.deathAnimation.callbacks.onPhase1(self.parent.characterData, models.models.death_animation.Avatar, self.parent.stringUtils.upper(self.parent.characterData.costume.costumes[self.parent.costume.currentCostume].name))
        end

        if events.TICK:getRegisteredCount("death_animation_tick") == 0 then
            events.TICK:register(function ()
                if not client:isPaused() then
                    local particleAnchorPos = self.parent.modelUtils.getModelWorldPos(models.models.death_animation.DeathAnimationParticleAnchor)
                    for _ = 1, 3 do
                        local particleRot = math.random() * math.pi * 2
                        local particleOffset = math.random() * 3
                        particles:newParticle(self.parent.compatibilityUtils:checkParticle("minecraft:poof"), particleAnchorPos:copy():add(math.cos(particleRot) * particleOffset, 0, math.sin(particleRot) * particleOffset)):setVelocity(math.cos(particleRot), 0, math.sin(particleRot))

                    end
                    if self.animationCount % 2 == 1 then
                        sounds:playSound(self.parent.compatibilityUtils:checkSound("minecraft:block.bamboo_wood_door.close"), self.parent.modelUtils.getModelWorldPos(models.models.death_animation.Helicopter.DeathAnimationSoundAnchor1), 1, 0.5):setAttenuation(2)
                    end
                    if self.animationCount < 120 then
                        models.models.death_animation.Avatar:setLight(world.getLightLevel(self.animationPos))
                    end
                    if self.animationCount == 1 then
                        self:spawnHelicopterParticles()
                    elseif self.animationCount == 10 then
                        sounds:playSound(self.parent.compatibilityUtils:checkSound("minecraft:block.iron_door.open"), self.parent.modelUtils.getModelWorldPos(models.models.death_animation.Helicopter.DeathAnimationSoundAnchor1), 1, 0.5)
                    elseif self.animationCount >= 57 and self.animationCount < 76 then
                        sounds:playSound(self.parent.compatibilityUtils:checkSound("minecraft:entity.player.attack.sweep"), self.parent.modelUtils.getModelWorldPos(models.models.death_animation.Helicopter.RopeLadder.RopeLadder2.RopeLadder3.RopeLadder4.RopeLadder5.RopeLadder6.RopeLadder7.RopeLadder8.RopeLadder9.RopeLadder10.RopeLadder11.RopeLadder12.RopeLadder13.RopeLadder14), 0.25, -0.056 * (self.animationCount - 57) + 2)
                    elseif self.animationCount == 120 then
                        self.dummyAvatarRoot = models.models.death_animation.Avatar:moveTo(models.models.death_animation.Helicopter.RopeLadder.RopeLadder2.RopeLadder3.RopeLadder4.RopeLadder5.RopeLadder6.RopeLadder7.RopeLadder8.RopeLadder9.RopeLadder10.RopeLadder11.RopeLadder12.RopeLadder13.RopeLadder14)
                        self.setPhase2Pose(models.models.death_animation.Helicopter.RopeLadder.RopeLadder2.RopeLadder3.RopeLadder4.RopeLadder5.RopeLadder6.RopeLadder7.RopeLadder8.RopeLadder9.RopeLadder10.RopeLadder11.RopeLadder12.RopeLadder13.RopeLadder14.Avatar)
                        if self.parent.characterData.deathAnimation.callbacks ~= nil and self.parent.characterData.deathAnimation.callbacks.onPhase2 ~= nil then
                            self.parent.characterData.deathAnimation.callbacks.onPhase2(self.parent.characterData, models.models.death_animation.Helicopter.RopeLadder.RopeLadder2.RopeLadder3.RopeLadder4.RopeLadder5.RopeLadder6.RopeLadder7.RopeLadder8.RopeLadder9.RopeLadder10.RopeLadder11.RopeLadder12.RopeLadder13.RopeLadder14.Avatar, self.parent.stringUtils.upper(self.parent.characterData.costume.costumes[self.parent.costume.currentCostume].name))
                        end
                    elseif self.animationCount == 180 then
                        models.models.death_animation.Helicopter.RopeLadder.RopeLadder2.RopeLadder3.RopeLadder4.RopeLadder5.RopeLadder6.RopeLadder7.RopeLadder8.RopeLadder9.RopeLadder10.RopeLadder11.RopeLadder12.RopeLadder13.RopeLadder14.Avatar:setVisible(false)
                    elseif self.animationCount == 230 then
                        sounds:playSound(self.parent.compatibilityUtils:checkSound("minecraft:block.iron_door.close"), self.parent.modelUtils.getModelWorldPos(models.models.death_animation.Helicopter.DeathAnimationSoundAnchor1), 1, 0.5)
                    elseif self.animationCount == 255 then
                        self:spawnHelicopterParticles()
                        self:stop()
                    end
                end
            end, "death_animation_tick")
        end
        if events.WORLD_TICK:getRegisteredCount("death_animation_world_tick") == 0 then
            events.WORLD_TICK:register(function ()
                if not client:isPaused() then
                    self.animationCount = self.animationCount + 1
                end
            end, "death_animation_world_tick")
        end
    end;

    ---死亡アニメーションを停止する。
    ---@param self DeathAnimation
    stop = function (self)
        models.models.death_animation:setVisible(false)
        animations["models.death_animation"]["death_animation"]:stop()
        events.TICK:remove("death_animation_tick")
        events.WORLD_TICK:remove("death_animation_world_tick")
        self.dummyAvatarRoot = nil
        self.animationCount = 0
    end;
}