/*
 * Decompiled with CFR 0.152.
 * 
 * Could not load the following classes:
 *  it.unimi.dsi.fastutil.objects.Object2IntMap
 *  it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap
 *  net.minecraft.class_1280
 *  net.minecraft.class_1282
 *  net.minecraft.class_1293
 *  net.minecraft.class_1294
 *  net.minecraft.class_1297
 *  net.minecraft.class_1304
 *  net.minecraft.class_1309
 *  net.minecraft.class_1322
 *  net.minecraft.class_1324
 *  net.minecraft.class_1657
 *  net.minecraft.class_1792
 *  net.minecraft.class_1799
 *  net.minecraft.class_1887
 *  net.minecraft.class_1893
 *  net.minecraft.class_1922
 *  net.minecraft.class_1934
 *  net.minecraft.class_2246
 *  net.minecraft.class_2338
 *  net.minecraft.class_2374
 *  net.minecraft.class_238
 *  net.minecraft.class_239$class_240
 *  net.minecraft.class_243
 *  net.minecraft.class_2680
 *  net.minecraft.class_2902$class_2903
 *  net.minecraft.class_3483
 *  net.minecraft.class_3959
 *  net.minecraft.class_3959$class_242
 *  net.minecraft.class_3959$class_3960
 *  net.minecraft.class_3965
 *  net.minecraft.class_5134
 *  net.minecraft.class_5321
 *  net.minecraft.class_6880
 *  net.minecraft.class_8103
 *  net.minecraft.class_9274
 *  net.minecraft.class_9285
 *  net.minecraft.class_9334
 *  net.minecraft.class_9362
 *  org.jetbrains.annotations.Nullable
 */
package meteordevelopment.meteorclient.utils.entity;

import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import java.util.function.BiFunction;
import meteordevelopment.meteorclient.MeteorClient;
import meteordevelopment.meteorclient.utils.Utils;
import meteordevelopment.meteorclient.utils.entity.EntityUtils;
import meteordevelopment.meteorclient.utils.entity.fakeplayer.FakePlayerEntity;
import meteordevelopment.meteorclient.utils.player.PlayerUtils;
import net.minecraft.class_1280;
import net.minecraft.class_1282;
import net.minecraft.class_1293;
import net.minecraft.class_1294;
import net.minecraft.class_1297;
import net.minecraft.class_1304;
import net.minecraft.class_1309;
import net.minecraft.class_1322;
import net.minecraft.class_1324;
import net.minecraft.class_1657;
import net.minecraft.class_1792;
import net.minecraft.class_1799;
import net.minecraft.class_1887;
import net.minecraft.class_1893;
import net.minecraft.class_1922;
import net.minecraft.class_1934;
import net.minecraft.class_2246;
import net.minecraft.class_2338;
import net.minecraft.class_2374;
import net.minecraft.class_238;
import net.minecraft.class_239;
import net.minecraft.class_243;
import net.minecraft.class_2680;
import net.minecraft.class_2902;
import net.minecraft.class_3483;
import net.minecraft.class_3959;
import net.minecraft.class_3965;
import net.minecraft.class_5134;
import net.minecraft.class_5321;
import net.minecraft.class_6880;
import net.minecraft.class_8103;
import net.minecraft.class_9274;
import net.minecraft.class_9285;
import net.minecraft.class_9334;
import net.minecraft.class_9362;
import org.jetbrains.annotations.Nullable;

public class DamageUtils {
    public static final RaycastFactory HIT_FACTORY = (context, blockPos) -> {
        class_2680 blockState = MeteorClient.mc.field_1687.method_8320(blockPos);
        if (blockState.method_26204().method_9520() < 600.0f) {
            return null;
        }
        return blockState.method_26220((class_1922)MeteorClient.mc.field_1687, blockPos).method_1092(context.start(), context.end(), blockPos);
    };

    private DamageUtils() {
    }

    public static float crystalDamage(class_1309 target, class_243 targetPos, class_238 targetBox, class_243 explosionPos, RaycastFactory raycastFactory) {
        return DamageUtils.explosionDamage(target, targetPos, targetBox, explosionPos, 12.0f, raycastFactory);
    }

    public static float bedDamage(class_1309 target, class_243 targetPos, class_238 targetBox, class_243 explosionPos, RaycastFactory raycastFactory) {
        return DamageUtils.explosionDamage(target, targetPos, targetBox, explosionPos, 10.0f, raycastFactory);
    }

    public static float anchorDamage(class_1309 target, class_243 targetPos, class_238 targetBox, class_243 explosionPos, RaycastFactory raycastFactory) {
        return DamageUtils.explosionDamage(target, targetPos, targetBox, explosionPos, 10.0f, raycastFactory);
    }

    public static float explosionDamage(class_1309 target, class_243 targetPos, class_238 targetBox, class_243 explosionPos, float power, RaycastFactory raycastFactory) {
        double modDistance = PlayerUtils.distance(targetPos.field_1352, targetPos.field_1351, targetPos.field_1350, explosionPos.field_1352, explosionPos.field_1351, explosionPos.field_1350);
        if (modDistance > (double)power) {
            return 0.0f;
        }
        double exposure = DamageUtils.getExposure(explosionPos, targetBox, raycastFactory);
        double impact = (1.0 - modDistance / (double)power) * exposure;
        float damage = (int)((impact * impact + impact) / 2.0 * 7.0 * 12.0 + 1.0);
        return DamageUtils.calculateReductions(damage, (class_1297)target, MeteorClient.mc.field_1687.method_48963().method_48807(null));
    }

    public static float crystalDamage(class_1309 target, class_243 crystal, boolean predictMovement, class_2338 obsidianPos) {
        return DamageUtils.overridingExplosionDamage(target, crystal, 12.0f, predictMovement, obsidianPos, class_2246.field_10540.method_9564());
    }

    public static float crystalDamage(class_1309 target, class_243 crystal) {
        return DamageUtils.explosionDamage(target, crystal, 12.0f, false);
    }

    public static float bedDamage(class_1309 target, class_243 bed) {
        return DamageUtils.explosionDamage(target, bed, 10.0f, false);
    }

    public static float anchorDamage(class_1309 target, class_243 anchor) {
        return DamageUtils.overridingExplosionDamage(target, anchor, 10.0f, false, class_2338.method_49638((class_2374)anchor), class_2246.field_10124.method_9564());
    }

    private static float overridingExplosionDamage(class_1309 target, class_243 explosionPos, float power, boolean predictMovement, class_2338 overridePos, class_2680 overrideState) {
        return DamageUtils.explosionDamage(target, explosionPos, power, predictMovement, DamageUtils.getOverridingHitFactory(overridePos, overrideState));
    }

    private static float explosionDamage(class_1309 target, class_243 explosionPos, float power, boolean predictMovement) {
        return DamageUtils.explosionDamage(target, explosionPos, power, predictMovement, HIT_FACTORY);
    }

    private static float explosionDamage(class_1309 target, class_243 explosionPos, float power, boolean predictMovement, RaycastFactory raycastFactory) {
        class_1657 player;
        if (target == null) {
            return 0.0f;
        }
        if (target instanceof class_1657 && EntityUtils.getGameMode(player = (class_1657)target) == class_1934.field_9220 && !(player instanceof FakePlayerEntity)) {
            return 0.0f;
        }
        class_243 position = predictMovement ? target.method_73189().method_1019(target.method_18798()) : target.method_73189();
        class_238 box = target.method_5829();
        if (predictMovement) {
            box = box.method_997(target.method_18798());
        }
        return DamageUtils.explosionDamage(target, position, box, explosionPos, power, raycastFactory);
    }

    public static RaycastFactory getOverridingHitFactory(class_2338 overridePos, class_2680 overrideState) {
        return (context, blockPos) -> {
            class_2680 blockState;
            if (blockPos.equals((Object)overridePos)) {
                blockState = overrideState;
            } else {
                blockState = MeteorClient.mc.field_1687.method_8320(blockPos);
                if (blockState.method_26204().method_9520() < 600.0f) {
                    return null;
                }
            }
            return blockState.method_26220((class_1922)MeteorClient.mc.field_1687, blockPos).method_1092(context.start(), context.end(), blockPos);
        };
    }

    public static float getAttackDamage(class_1309 attacker, class_1297 target) {
        class_1282 class_12822;
        float itemDamage = (float)attacker.method_45325(class_5134.field_23721);
        if (attacker instanceof class_1657) {
            class_1657 player = (class_1657)attacker;
            class_12822 = MeteorClient.mc.field_1687.method_48963().method_48802(player);
        } else {
            class_12822 = MeteorClient.mc.field_1687.method_48963().method_48812(attacker);
        }
        class_1282 damageSource = class_12822;
        float damage = DamageUtils.modifyAttackDamage(attacker, target, attacker.method_59958(), damageSource, itemDamage);
        return DamageUtils.calculateReductions(damage, target, damageSource);
    }

    public static float getAttackDamage(class_1309 attacker, class_1297 target, class_1799 weapon) {
        class_1282 class_12822;
        class_1324 original = attacker.method_5996(class_5134.field_23721);
        class_1324 copy = new class_1324(class_5134.field_23721, o -> {});
        copy.method_6192(original.method_6201());
        for (class_1322 modifier2 : original.method_6195()) {
            copy.method_26835(modifier2);
        }
        copy.method_6200(class_1792.field_8006);
        class_9285 attributeModifiers = (class_9285)weapon.method_58694(class_9334.field_49636);
        if (attributeModifiers != null) {
            attributeModifiers.method_57482(class_1304.field_6173, (entry, modifier) -> {
                if (entry == class_5134.field_23721) {
                    copy.method_55696(modifier);
                }
            });
        }
        float itemDamage = (float)copy.method_6194();
        if (attacker instanceof class_1657) {
            class_1657 player = (class_1657)attacker;
            class_12822 = MeteorClient.mc.field_1687.method_48963().method_48802(player);
        } else {
            class_12822 = MeteorClient.mc.field_1687.method_48963().method_48812(attacker);
        }
        class_1282 damageSource = class_12822;
        float damage = DamageUtils.modifyAttackDamage(attacker, target, weapon, damageSource, itemDamage);
        return DamageUtils.calculateReductions(damage, target, damageSource);
    }

    private static float modifyAttackDamage(class_1309 attacker, class_1297 target, class_1799 weapon, class_1282 damageSource, float damage) {
        int smite;
        int impaling;
        int baneOfArthropods;
        Object2IntOpenHashMap enchantments = new Object2IntOpenHashMap();
        Utils.getEnchantments(weapon, (Object2IntMap<class_6880<class_1887>>)enchantments);
        float enchantDamage = 0.0f;
        int sharpness = Utils.getEnchantmentLevel((Object2IntMap<class_6880<class_1887>>)enchantments, (class_5321<class_1887>)class_1893.field_9118);
        if (sharpness > 0) {
            enchantDamage += 1.0f + 0.5f * (float)(sharpness - 1);
        }
        if ((baneOfArthropods = Utils.getEnchantmentLevel((Object2IntMap<class_6880<class_1887>>)enchantments, (class_5321<class_1887>)class_1893.field_9112)) > 0 && target.method_5864().method_20210(class_3483.field_48285)) {
            enchantDamage += 2.5f * (float)baneOfArthropods;
        }
        if ((impaling = Utils.getEnchantmentLevel((Object2IntMap<class_6880<class_1887>>)enchantments, (class_5321<class_1887>)class_1893.field_9106)) > 0 && target.method_5864().method_20210(class_3483.field_48284)) {
            enchantDamage += 2.5f * (float)impaling;
        }
        if ((smite = Utils.getEnchantmentLevel((Object2IntMap<class_6880<class_1887>>)enchantments, (class_5321<class_1887>)class_1893.field_9123)) > 0 && target.method_5864().method_20210(class_3483.field_49931)) {
            enchantDamage += 2.5f * (float)smite;
        }
        if (attacker instanceof class_1657) {
            class_9362 item;
            float bonusDamage;
            class_1657 playerEntity = (class_1657)attacker;
            float charge = playerEntity.method_7261(0.5f);
            damage *= 0.2f + charge * charge * 0.8f;
            enchantDamage *= charge;
            class_1792 class_17922 = weapon.method_7909();
            if (class_17922 instanceof class_9362 && (bonusDamage = (item = (class_9362)class_17922).method_58403(target, damage, damageSource)) > 0.0f) {
                int density = Utils.getEnchantmentLevel(weapon, (class_5321<class_1887>)class_1893.field_50157);
                if (density > 0) {
                    bonusDamage += (float)(0.5 * attacker.field_6017);
                }
                damage += bonusDamage;
            }
            if (!(!(charge > 0.9f) || !(attacker.field_6017 > 0.0) || attacker.method_24828() || attacker.method_6101() || attacker.method_5799() || attacker.method_6059(class_1294.field_5919) || attacker.method_5765())) {
                damage *= 1.5f;
            }
        }
        return damage + enchantDamage;
    }

    public static float fallDamage(class_1309 entity) {
        if (entity instanceof class_1657) {
            class_1657 player = (class_1657)entity;
            if (player.method_31549().field_7479) {
                return 0.0f;
            }
        }
        if (entity.method_6059(class_1294.field_5906) || entity.method_6059(class_1294.field_5902)) {
            return 0.0f;
        }
        int surface = MeteorClient.mc.field_1687.method_8500(entity.method_24515()).method_12032(class_2902.class_2903.field_13197).method_12603(entity.method_31477() & 0xF, entity.method_31479() & 0xF);
        if (entity.method_31478() >= surface) {
            return DamageUtils.fallDamageReductions(entity, surface);
        }
        class_3965 raycastResult = MeteorClient.mc.field_1687.method_17742(new class_3959(entity.method_73189(), new class_243(entity.method_23317(), (double)MeteorClient.mc.field_1687.method_31607(), entity.method_23321()), class_3959.class_3960.field_17558, class_3959.class_242.field_36338, (class_1297)entity));
        if (raycastResult.method_17783() == class_239.class_240.field_1333) {
            return 0.0f;
        }
        return DamageUtils.fallDamageReductions(entity, raycastResult.method_17777().method_10264());
    }

    private static float fallDamageReductions(class_1309 entity, int surface) {
        int fallHeight = (int)(entity.method_23318() - (double)surface + entity.field_6017 - 3.0);
        @Nullable class_1293 jumpBoostInstance = entity.method_6112(class_1294.field_5913);
        if (jumpBoostInstance != null) {
            fallHeight -= jumpBoostInstance.method_5578() + 1;
        }
        return DamageUtils.calculateReductions(fallHeight, (class_1297)entity, MeteorClient.mc.field_1687.method_48963().method_48827());
    }

    public static float calculateReductions(float damage, class_1297 entity, class_1282 damageSource) {
        if (damageSource.method_5514()) {
            switch (MeteorClient.mc.field_1687.method_8407()) {
                case field_5801: {
                    break;
                }
                case field_5805: {
                    damage = Math.min(damage / 2.0f + 1.0f, damage);
                    break;
                }
                case field_5802: {
                    break;
                }
                case field_5807: {
                    damage *= 1.5f;
                }
            }
        }
        if (entity instanceof class_1309) {
            class_1309 livingEntity = (class_1309)entity;
            damage = class_1280.method_5496((class_1309)livingEntity, (float)damage, (class_1282)damageSource, (float)DamageUtils.getArmor(livingEntity), (float)((float)livingEntity.method_45325(class_5134.field_23725)));
            damage = DamageUtils.resistanceReduction(livingEntity, damage);
            damage = DamageUtils.protectionReduction(livingEntity, damage, damageSource);
        }
        return Math.max(damage, 0.0f);
    }

    private static float getArmor(class_1309 entity) {
        return (float)Math.floor(entity.method_45325(class_5134.field_23724));
    }

    private static float protectionReduction(class_1309 player, float damage, class_1282 source) {
        if (source.method_48789(class_8103.field_42242)) {
            return damage;
        }
        int damageProtection = 0;
        for (class_1304 slot : class_9274.field_49224) {
            int featherFalling;
            int projectileProtection;
            int blastProtection;
            int fireProtection;
            class_1799 stack = player.method_6118(slot);
            Object2IntOpenHashMap enchantments = new Object2IntOpenHashMap();
            Utils.getEnchantments(stack, (Object2IntMap<class_6880<class_1887>>)enchantments);
            int protection = Utils.getEnchantmentLevel((Object2IntMap<class_6880<class_1887>>)enchantments, (class_5321<class_1887>)class_1893.field_9111);
            if (protection > 0) {
                damageProtection += protection;
            }
            if ((fireProtection = Utils.getEnchantmentLevel((Object2IntMap<class_6880<class_1887>>)enchantments, (class_5321<class_1887>)class_1893.field_9095)) > 0 && source.method_48789(class_8103.field_42246)) {
                damageProtection += 2 * fireProtection;
            }
            if ((blastProtection = Utils.getEnchantmentLevel((Object2IntMap<class_6880<class_1887>>)enchantments, (class_5321<class_1887>)class_1893.field_9107)) > 0 && source.method_48789(class_8103.field_42249)) {
                damageProtection += 2 * blastProtection;
            }
            if ((projectileProtection = Utils.getEnchantmentLevel((Object2IntMap<class_6880<class_1887>>)enchantments, (class_5321<class_1887>)class_1893.field_9096)) > 0 && source.method_48789(class_8103.field_42247)) {
                damageProtection += 2 * projectileProtection;
            }
            if ((featherFalling = Utils.getEnchantmentLevel((Object2IntMap<class_6880<class_1887>>)enchantments, (class_5321<class_1887>)class_1893.field_9129)) <= 0 || !source.method_48789(class_8103.field_42250)) continue;
            damageProtection += 3 * featherFalling;
        }
        return class_1280.method_5497((float)damage, (float)damageProtection);
    }

    private static float resistanceReduction(class_1309 player, float damage) {
        class_1293 resistance = player.method_6112(class_1294.field_5907);
        if (resistance != null) {
            int lvl = resistance.method_5578() + 1;
            damage *= 1.0f - (float)lvl * 0.2f;
        }
        return Math.max(damage, 0.0f);
    }

    private static float getExposure(class_243 source, class_238 box, RaycastFactory raycastFactory) {
        double xDiff = box.field_1320 - box.field_1323;
        double yDiff = box.field_1325 - box.field_1322;
        double zDiff = box.field_1324 - box.field_1321;
        double xStep = 1.0 / (xDiff * 2.0 + 1.0);
        double yStep = 1.0 / (yDiff * 2.0 + 1.0);
        double zStep = 1.0 / (zDiff * 2.0 + 1.0);
        if (xStep > 0.0 && yStep > 0.0 && zStep > 0.0) {
            int misses = 0;
            int hits = 0;
            double xOffset = (1.0 - Math.floor(1.0 / xStep) * xStep) * 0.5;
            double zOffset = (1.0 - Math.floor(1.0 / zStep) * zStep) * 0.5;
            xStep *= xDiff;
            yStep *= yDiff;
            zStep *= zDiff;
            double startX = box.field_1323 + xOffset;
            double startY = box.field_1322;
            double startZ = box.field_1321 + zOffset;
            double endX = box.field_1320 + xOffset;
            double endY = box.field_1325;
            double endZ = box.field_1324 + zOffset;
            for (double x = startX; x <= endX; x += xStep) {
                for (double y = startY; y <= endY; y += yStep) {
                    for (double z = startZ; z <= endZ; z += zStep) {
                        class_243 position = new class_243(x, y, z);
                        if (DamageUtils.raycast(new ExposureRaycastContext(position, source), raycastFactory) == null) {
                            ++misses;
                        }
                        ++hits;
                    }
                }
            }
            return (float)misses / (float)hits;
        }
        return 0.0f;
    }

    private static class_3965 raycast(ExposureRaycastContext context, RaycastFactory raycastFactory) {
        return (class_3965)class_1922.method_17744((class_243)context.start, (class_243)context.end, (Object)context, (BiFunction)raycastFactory, ctx -> null);
    }

    @FunctionalInterface
    public static interface RaycastFactory
    extends BiFunction<ExposureRaycastContext, class_2338, class_3965> {
    }

    public record ExposureRaycastContext(class_243 start, class_243 end) {
    }
}

