package net.minecraft.world.item.enchantment;

import java.util.AbstractList;
import java.util.Collection;
import net.minecraft.Util;
import net.minecraft.util.WeighedRandom;
import net.minecraft.world.level.ItemLike;
import net.minecraft.world.item.Item;
import java.util.Random;
import javax.annotation.Nullable;
import java.util.List;
import com.google.common.collect.Lists;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import org.apache.commons.lang3.mutable.MutableFloat;
import net.minecraft.world.entity.MobType;
import org.apache.commons.lang3.mutable.MutableInt;
import net.minecraft.world.damagesource.DamageSource;
import java.util.Iterator;
import net.minecraft.nbt.Tag;
import com.google.common.collect.Maps;
import net.minecraft.world.item.EnchantedBookItem;
import net.minecraft.world.item.Items;
import java.util.Map;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.util.Mth;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.core.Registry;
import net.minecraft.world.item.ItemStack;

public class EnchantmentHelper {
    public static int getItemEnchantmentLevel(final Enchantment bhx, final ItemStack ben) {
        if (ben.isEmpty()) {
            return 0;
        }
        final ResourceLocation sm3 = Registry.ENCHANTMENT.getKey(bhx);
        final ListTag jz4 = ben.getEnchantmentTags();
        for (int integer5 = 0; integer5 < jz4.size(); ++integer5) {
            final CompoundTag jt6 = jz4.getCompound(integer5);
            final ResourceLocation sm4 = ResourceLocation.tryParse(jt6.getString("id"));
            if (sm4 != null && sm4.equals(sm3)) {
                return Mth.clamp(jt6.getInt("lvl"), 0, 255);
            }
        }
        return 0;
    }
    
    public static Map<Enchantment, Integer> getEnchantments(final ItemStack ben) {
        final ListTag jz2 = (ben.getItem() == Items.ENCHANTED_BOOK) ? EnchantedBookItem.getEnchantments(ben) : ben.getEnchantmentTags();
        return deserializeEnchantments(jz2);
    }
    
    public static Map<Enchantment, Integer> deserializeEnchantments(final ListTag jz) {
        final Map<Enchantment, Integer> map2 = Maps.newLinkedHashMap();
        for (int integer3 = 0; integer3 < jz.size(); ++integer3) {
            final CompoundTag jt4 = jz.getCompound(integer3);
            final Integer n;
            Registry.ENCHANTMENT.getOptional(ResourceLocation.tryParse(jt4.getString("id"))).ifPresent(bhx -> n = map2.put(bhx, jt4.getInt("lvl")));
        }
        return map2;
    }
    
    public static void setEnchantments(final Map<Enchantment, Integer> map, final ItemStack ben) {
        final ListTag jz3 = new ListTag();
        for (final Map.Entry<Enchantment, Integer> entry5 : map.entrySet()) {
            final Enchantment bhx6 = entry5.getKey();
            if (bhx6 == null) {
                continue;
            }
            final int integer7 = entry5.getValue();
            final CompoundTag jt8 = new CompoundTag();
            jt8.putString("id", String.valueOf(Registry.ENCHANTMENT.getKey(bhx6)));
            jt8.putShort("lvl", (short)integer7);
            ((AbstractList<CompoundTag>)jz3).add(jt8);
            if (ben.getItem() != Items.ENCHANTED_BOOK) {
                continue;
            }
            EnchantedBookItem.addEnchantment(ben, new EnchantmentInstance(bhx6, integer7));
        }
        if (jz3.isEmpty()) {
            ben.removeTagKey("Enchantments");
        }
        else if (ben.getItem() != Items.ENCHANTED_BOOK) {
            ben.addTagElement("Enchantments", jz3);
        }
    }
    
    private static void runIterationOnItem(final EnchantmentVisitor a, final ItemStack ben) {
        if (ben.isEmpty()) {
            return;
        }
        final ListTag jz3 = ben.getEnchantmentTags();
        for (int integer4 = 0; integer4 < jz3.size(); ++integer4) {
            final String string5 = jz3.getCompound(integer4).getString("id");
            final int integer5 = jz3.getCompound(integer4).getInt("lvl");
            Registry.ENCHANTMENT.getOptional(ResourceLocation.tryParse(string5)).ifPresent(bhx -> a.accept(bhx, integer5));
        }
    }
    
    private static void runIterationOnInventory(final EnchantmentVisitor a, final Iterable<ItemStack> iterable) {
        for (final ItemStack ben4 : iterable) {
            runIterationOnItem(a, ben4);
        }
    }
    
    public static int getDamageProtection(final Iterable<ItemStack> iterable, final DamageSource ajz) {
        final MutableInt mutableInt3 = new MutableInt();
        runIterationOnInventory((bhx, integer) -> mutableInt3.add(bhx.getDamageProtection(integer, ajz)), iterable);
        return mutableInt3.intValue();
    }
    
    public static float getDamageBonus(final ItemStack ben, final MobType ald) {
        final MutableFloat mutableFloat3 = new MutableFloat();
        runIterationOnItem((bhx, integer) -> mutableFloat3.add(bhx.getDamageBonus(integer, ald)), ben);
        return mutableFloat3.floatValue();
    }
    
    public static float getSweepingDamageRatio(final LivingEntity akz) {
        final int integer2 = getEnchantmentLevel(Enchantments.SWEEPING_EDGE, akz);
        if (integer2 > 0) {
            return SweepingEdgeEnchantment.getSweepingDamageRatio(integer2);
        }
        return 0.0f;
    }
    
    public static void doPostHurtEffects(final LivingEntity akz, final Entity akq) {
        final EnchantmentVisitor a3 = (bhx, integer) -> bhx.doPostHurt(akz, akq, integer);
        if (akz != null) {
            runIterationOnInventory(a3, akz.getAllSlots());
        }
        if (akq instanceof Player) {
            runIterationOnItem(a3, akz.getMainHandItem());
        }
    }
    
    public static void doPostDamageEffects(final LivingEntity akz, final Entity akq) {
        final EnchantmentVisitor a3 = (bhx, integer) -> bhx.doPostAttack(akz, akq, integer);
        if (akz != null) {
            runIterationOnInventory(a3, akz.getAllSlots());
        }
        if (akz instanceof Player) {
            runIterationOnItem(a3, akz.getMainHandItem());
        }
    }
    
    public static int getEnchantmentLevel(final Enchantment bhx, final LivingEntity akz) {
        final Iterable<ItemStack> iterable3 = bhx.getSlotItems(akz).values();
        if (iterable3 == null) {
            return 0;
        }
        int integer4 = 0;
        for (final ItemStack ben6 : iterable3) {
            final int integer5 = getItemEnchantmentLevel(bhx, ben6);
            if (integer5 > integer4) {
                integer4 = integer5;
            }
        }
        return integer4;
    }
    
    public static int getKnockbackBonus(final LivingEntity akz) {
        return getEnchantmentLevel(Enchantments.KNOCKBACK, akz);
    }
    
    public static int getFireAspect(final LivingEntity akz) {
        return getEnchantmentLevel(Enchantments.FIRE_ASPECT, akz);
    }
    
    public static int getRespiration(final LivingEntity akz) {
        return getEnchantmentLevel(Enchantments.RESPIRATION, akz);
    }
    
    public static int getDepthStrider(final LivingEntity akz) {
        return getEnchantmentLevel(Enchantments.DEPTH_STRIDER, akz);
    }
    
    public static int getBlockEfficiency(final LivingEntity akz) {
        return getEnchantmentLevel(Enchantments.BLOCK_EFFICIENCY, akz);
    }
    
    public static int getFishingLuckBonus(final ItemStack ben) {
        return getItemEnchantmentLevel(Enchantments.FISHING_LUCK, ben);
    }
    
    public static int getFishingSpeedBonus(final ItemStack ben) {
        return getItemEnchantmentLevel(Enchantments.FISHING_SPEED, ben);
    }
    
    public static int getMobLooting(final LivingEntity akz) {
        return getEnchantmentLevel(Enchantments.MOB_LOOTING, akz);
    }
    
    public static boolean hasAquaAffinity(final LivingEntity akz) {
        return getEnchantmentLevel(Enchantments.AQUA_AFFINITY, akz) > 0;
    }
    
    public static boolean hasFrostWalker(final LivingEntity akz) {
        return getEnchantmentLevel(Enchantments.FROST_WALKER, akz) > 0;
    }
    
    public static boolean hasBindingCurse(final ItemStack ben) {
        return getItemEnchantmentLevel(Enchantments.BINDING_CURSE, ben) > 0;
    }
    
    public static boolean hasVanishingCurse(final ItemStack ben) {
        return getItemEnchantmentLevel(Enchantments.VANISHING_CURSE, ben) > 0;
    }
    
    public static int getLoyalty(final ItemStack ben) {
        return getItemEnchantmentLevel(Enchantments.LOYALTY, ben);
    }
    
    public static int getRiptide(final ItemStack ben) {
        return getItemEnchantmentLevel(Enchantments.RIPTIDE, ben);
    }
    
    public static boolean hasChanneling(final ItemStack ben) {
        return getItemEnchantmentLevel(Enchantments.CHANNELING, ben) > 0;
    }
    
    @Nullable
    public static Map.Entry<EquipmentSlot, ItemStack> getRandomItemWith(final Enchantment bhx, final LivingEntity akz) {
        final Map<EquipmentSlot, ItemStack> map3 = bhx.getSlotItems(akz);
        if (map3.isEmpty()) {
            return null;
        }
        final List<Map.Entry<EquipmentSlot, ItemStack>> list4 = Lists.newArrayList();
        for (final Map.Entry<EquipmentSlot, ItemStack> entry6 : map3.entrySet()) {
            final ItemStack ben7 = entry6.getValue();
            if (!ben7.isEmpty() && getItemEnchantmentLevel(bhx, ben7) > 0) {
                list4.add(entry6);
            }
        }
        return list4.isEmpty() ? null : list4.get(akz.getRandom().nextInt(list4.size()));
    }
    
    public static int getEnchantmentCost(final Random random, final int integer2, int integer3, final ItemStack ben) {
        final Item bei5 = ben.getItem();
        final int integer4 = bei5.getEnchantmentValue();
        if (integer4 <= 0) {
            return 0;
        }
        if (integer3 > 15) {
            integer3 = 15;
        }
        final int integer5 = random.nextInt(8) + 1 + (integer3 >> 1) + random.nextInt(integer3 + 1);
        if (integer2 == 0) {
            return Math.max(integer5 / 3, 1);
        }
        if (integer2 == 1) {
            return integer5 * 2 / 3 + 1;
        }
        return Math.max(integer5, integer3 * 2);
    }
    
    public static ItemStack enchantItem(final Random random, ItemStack ben, final int integer, final boolean boolean4) {
        final List<EnchantmentInstance> list5 = selectEnchantment(random, ben, integer, boolean4);
        final boolean boolean5 = ben.getItem() == Items.BOOK;
        if (boolean5) {
            ben = new ItemStack(Items.ENCHANTED_BOOK);
        }
        for (final EnchantmentInstance bia8 : list5) {
            if (boolean5) {
                EnchantedBookItem.addEnchantment(ben, bia8);
            }
            else {
                ben.enchant(bia8.enchantment, bia8.level);
            }
        }
        return ben;
    }
    
    public static List<EnchantmentInstance> selectEnchantment(final Random random, final ItemStack ben, int integer, final boolean boolean4) {
        final List<EnchantmentInstance> list5 = Lists.newArrayList();
        final Item bei6 = ben.getItem();
        final int integer2 = bei6.getEnchantmentValue();
        if (integer2 <= 0) {
            return list5;
        }
        integer += 1 + random.nextInt(integer2 / 4 + 1) + random.nextInt(integer2 / 4 + 1);
        final float float8 = (random.nextFloat() + random.nextFloat() - 1.0f) * 0.15f;
        integer = Mth.clamp(Math.round(integer + integer * float8), 1, Integer.MAX_VALUE);
        final List<EnchantmentInstance> list6 = getAvailableEnchantmentResults(integer, ben, boolean4);
        if (!list6.isEmpty()) {
            list5.add(WeighedRandom.<EnchantmentInstance>getRandomItem(random, list6));
            while (random.nextInt(50) <= integer) {
                filterCompatibleEnchantments(list6, Util.<EnchantmentInstance>lastOf(list5));
                if (list6.isEmpty()) {
                    break;
                }
                list5.add(WeighedRandom.<EnchantmentInstance>getRandomItem(random, list6));
                integer /= 2;
            }
        }
        return list5;
    }
    
    public static void filterCompatibleEnchantments(final List<EnchantmentInstance> list, final EnchantmentInstance bia) {
        final Iterator<EnchantmentInstance> iterator3 = list.iterator();
        while (iterator3.hasNext()) {
            if (!bia.enchantment.isCompatibleWith(iterator3.next().enchantment)) {
                iterator3.remove();
            }
        }
    }
    
    public static boolean isEnchantmentCompatible(final Collection<Enchantment> collection, final Enchantment bhx) {
        for (final Enchantment bhx2 : collection) {
            if (!bhx2.isCompatibleWith(bhx)) {
                return false;
            }
        }
        return true;
    }
    
    public static List<EnchantmentInstance> getAvailableEnchantmentResults(final int integer, final ItemStack ben, final boolean boolean3) {
        final List<EnchantmentInstance> list4 = Lists.newArrayList();
        final Item bei5 = ben.getItem();
        final boolean boolean4 = ben.getItem() == Items.BOOK;
        for (final Enchantment bhx8 : Registry.ENCHANTMENT) {
            if (bhx8.isTreasureOnly() && !boolean3) {
                continue;
            }
            if (!bhx8.category.canEnchant(bei5) && !boolean4) {
                continue;
            }
            for (int integer2 = bhx8.getMaxLevel(); integer2 > bhx8.getMinLevel() - 1; --integer2) {
                if (integer >= bhx8.getMinCost(integer2) && integer <= bhx8.getMaxCost(integer2)) {
                    list4.add(new EnchantmentInstance(bhx8, integer2));
                    break;
                }
            }
        }
        return list4;
    }
    
    @FunctionalInterface
    interface EnchantmentVisitor {
        void accept(final Enchantment bhx, final int integer);
    }
}
