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 java.util.List;
import com.google.common.collect.Lists;
import java.util.function.Predicate;
import javax.annotation.Nullable;
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 bnw, final ItemStack bki) {
        if (bki.isEmpty()) {
            return 0;
        }
        final ResourceLocation uh3 = Registry.ENCHANTMENT.getKey(bnw);
        final ListTag lk4 = bki.getEnchantmentTags();
        for (int integer5 = 0; integer5 < lk4.size(); ++integer5) {
            final CompoundTag le6 = lk4.getCompound(integer5);
            final ResourceLocation uh4 = ResourceLocation.tryParse(le6.getString("id"));
            if (uh4 != null && uh4.equals(uh3)) {
                return Mth.clamp(le6.getInt("lvl"), 0, 255);
            }
        }
        return 0;
    }
    
    public static Map<Enchantment, Integer> getEnchantments(final ItemStack bki) {
        final ListTag lk2 = (bki.getItem() == Items.ENCHANTED_BOOK) ? EnchantedBookItem.getEnchantments(bki) : bki.getEnchantmentTags();
        return deserializeEnchantments(lk2);
    }
    
    public static Map<Enchantment, Integer> deserializeEnchantments(final ListTag lk) {
        final Map<Enchantment, Integer> map2 = Maps.newLinkedHashMap();
        for (int integer3 = 0; integer3 < lk.size(); ++integer3) {
            final CompoundTag le4 = lk.getCompound(integer3);
            final Integer n;
            Registry.ENCHANTMENT.getOptional(ResourceLocation.tryParse(le4.getString("id"))).ifPresent(bnw -> n = map2.put(bnw, le4.getInt("lvl")));
        }
        return map2;
    }
    
    public static void setEnchantments(final Map<Enchantment, Integer> map, final ItemStack bki) {
        final ListTag lk3 = new ListTag();
        for (final Map.Entry<Enchantment, Integer> entry5 : map.entrySet()) {
            final Enchantment bnw6 = entry5.getKey();
            if (bnw6 == null) {
                continue;
            }
            final int integer7 = entry5.getValue();
            final CompoundTag le8 = new CompoundTag();
            le8.putString("id", String.valueOf(Registry.ENCHANTMENT.getKey(bnw6)));
            le8.putShort("lvl", (short)integer7);
            ((AbstractList<CompoundTag>)lk3).add(le8);
            if (bki.getItem() != Items.ENCHANTED_BOOK) {
                continue;
            }
            EnchantedBookItem.addEnchantment(bki, new EnchantmentInstance(bnw6, integer7));
        }
        if (lk3.isEmpty()) {
            bki.removeTagKey("Enchantments");
        }
        else if (bki.getItem() != Items.ENCHANTED_BOOK) {
            bki.addTagElement("Enchantments", lk3);
        }
    }
    
    private static void runIterationOnItem(final EnchantmentVisitor a, final ItemStack bki) {
        if (bki.isEmpty()) {
            return;
        }
        final ListTag lk3 = bki.getEnchantmentTags();
        for (int integer4 = 0; integer4 < lk3.size(); ++integer4) {
            final String string5 = lk3.getCompound(integer4).getString("id");
            final int integer5 = lk3.getCompound(integer4).getInt("lvl");
            Registry.ENCHANTMENT.getOptional(ResourceLocation.tryParse(string5)).ifPresent(bnw -> a.accept(bnw, integer5));
        }
    }
    
    private static void runIterationOnInventory(final EnchantmentVisitor a, final Iterable<ItemStack> iterable) {
        for (final ItemStack bki4 : iterable) {
            runIterationOnItem(a, bki4);
        }
    }
    
    public static int getDamageProtection(final Iterable<ItemStack> iterable, final DamageSource anw) {
        final MutableInt mutableInt3 = new MutableInt();
        runIterationOnInventory((bnw, integer) -> mutableInt3.add(bnw.getDamageProtection(integer, anw)), iterable);
        return mutableInt3.intValue();
    }
    
    public static float getDamageBonus(final ItemStack bki, final MobType apc) {
        final MutableFloat mutableFloat3 = new MutableFloat();
        runIterationOnItem((bnw, integer) -> mutableFloat3.add(bnw.getDamageBonus(integer, apc)), bki);
        return mutableFloat3.floatValue();
    }
    
    public static float getSweepingDamageRatio(final LivingEntity aoy) {
        final int integer2 = getEnchantmentLevel(Enchantments.SWEEPING_EDGE, aoy);
        if (integer2 > 0) {
            return SweepingEdgeEnchantment.getSweepingDamageRatio(integer2);
        }
        return 0.0f;
    }
    
    public static void doPostHurtEffects(final LivingEntity aoy, final Entity aom) {
        final EnchantmentVisitor a3 = (bnw, integer) -> bnw.doPostHurt(aoy, aom, integer);
        if (aoy != null) {
            runIterationOnInventory(a3, aoy.getAllSlots());
        }
        if (aom instanceof Player) {
            runIterationOnItem(a3, aoy.getMainHandItem());
        }
    }
    
    public static void doPostDamageEffects(final LivingEntity aoy, final Entity aom) {
        final EnchantmentVisitor a3 = (bnw, integer) -> bnw.doPostAttack(aoy, aom, integer);
        if (aoy != null) {
            runIterationOnInventory(a3, aoy.getAllSlots());
        }
        if (aoy instanceof Player) {
            runIterationOnItem(a3, aoy.getMainHandItem());
        }
    }
    
    public static int getEnchantmentLevel(final Enchantment bnw, final LivingEntity aoy) {
        final Iterable<ItemStack> iterable3 = bnw.getSlotItems(aoy).values();
        if (iterable3 == null) {
            return 0;
        }
        int integer4 = 0;
        for (final ItemStack bki6 : iterable3) {
            final int integer5 = getItemEnchantmentLevel(bnw, bki6);
            if (integer5 > integer4) {
                integer4 = integer5;
            }
        }
        return integer4;
    }
    
    public static int getKnockbackBonus(final LivingEntity aoy) {
        return getEnchantmentLevel(Enchantments.KNOCKBACK, aoy);
    }
    
    public static int getFireAspect(final LivingEntity aoy) {
        return getEnchantmentLevel(Enchantments.FIRE_ASPECT, aoy);
    }
    
    public static int getRespiration(final LivingEntity aoy) {
        return getEnchantmentLevel(Enchantments.RESPIRATION, aoy);
    }
    
    public static int getDepthStrider(final LivingEntity aoy) {
        return getEnchantmentLevel(Enchantments.DEPTH_STRIDER, aoy);
    }
    
    public static int getBlockEfficiency(final LivingEntity aoy) {
        return getEnchantmentLevel(Enchantments.BLOCK_EFFICIENCY, aoy);
    }
    
    public static int getFishingLuckBonus(final ItemStack bki) {
        return getItemEnchantmentLevel(Enchantments.FISHING_LUCK, bki);
    }
    
    public static int getFishingSpeedBonus(final ItemStack bki) {
        return getItemEnchantmentLevel(Enchantments.FISHING_SPEED, bki);
    }
    
    public static int getMobLooting(final LivingEntity aoy) {
        return getEnchantmentLevel(Enchantments.MOB_LOOTING, aoy);
    }
    
    public static boolean hasAquaAffinity(final LivingEntity aoy) {
        return getEnchantmentLevel(Enchantments.AQUA_AFFINITY, aoy) > 0;
    }
    
    public static boolean hasFrostWalker(final LivingEntity aoy) {
        return getEnchantmentLevel(Enchantments.FROST_WALKER, aoy) > 0;
    }
    
    public static boolean hasSoulSpeed(final LivingEntity aoy) {
        return getEnchantmentLevel(Enchantments.SOUL_SPEED, aoy) > 0;
    }
    
    public static boolean hasBindingCurse(final ItemStack bki) {
        return getItemEnchantmentLevel(Enchantments.BINDING_CURSE, bki) > 0;
    }
    
    public static boolean hasVanishingCurse(final ItemStack bki) {
        return getItemEnchantmentLevel(Enchantments.VANISHING_CURSE, bki) > 0;
    }
    
    public static int getLoyalty(final ItemStack bki) {
        return getItemEnchantmentLevel(Enchantments.LOYALTY, bki);
    }
    
    public static int getRiptide(final ItemStack bki) {
        return getItemEnchantmentLevel(Enchantments.RIPTIDE, bki);
    }
    
    public static boolean hasChanneling(final ItemStack bki) {
        return getItemEnchantmentLevel(Enchantments.CHANNELING, bki) > 0;
    }
    
    @Nullable
    public static Map.Entry<EquipmentSlot, ItemStack> getRandomItemWith(final Enchantment bnw, final LivingEntity aoy) {
        return getRandomItemWith(bnw, aoy, bki -> true);
    }
    
    @Nullable
    public static Map.Entry<EquipmentSlot, ItemStack> getRandomItemWith(final Enchantment bnw, final LivingEntity aoy, final Predicate<ItemStack> predicate) {
        final Map<EquipmentSlot, ItemStack> map4 = bnw.getSlotItems(aoy);
        if (map4.isEmpty()) {
            return null;
        }
        final List<Map.Entry<EquipmentSlot, ItemStack>> list5 = Lists.newArrayList();
        for (final Map.Entry<EquipmentSlot, ItemStack> entry7 : map4.entrySet()) {
            final ItemStack bki8 = entry7.getValue();
            if (!bki8.isEmpty() && getItemEnchantmentLevel(bnw, bki8) > 0 && predicate.test(bki8)) {
                list5.add(entry7);
            }
        }
        return list5.isEmpty() ? null : list5.get(aoy.getRandom().nextInt(list5.size()));
    }
    
    public static int getEnchantmentCost(final Random random, final int integer2, int integer3, final ItemStack bki) {
        final Item bke5 = bki.getItem();
        final int integer4 = bke5.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 bki, final int integer, final boolean boolean4) {
        final List<EnchantmentInstance> list5 = selectEnchantment(random, bki, integer, boolean4);
        final boolean boolean5 = bki.getItem() == Items.BOOK;
        if (boolean5) {
            bki = new ItemStack(Items.ENCHANTED_BOOK);
        }
        for (final EnchantmentInstance bnz8 : list5) {
            if (boolean5) {
                EnchantedBookItem.addEnchantment(bki, bnz8);
            }
            else {
                bki.enchant(bnz8.enchantment, bnz8.level);
            }
        }
        return bki;
    }
    
    public static List<EnchantmentInstance> selectEnchantment(final Random random, final ItemStack bki, int integer, final boolean boolean4) {
        final List<EnchantmentInstance> list5 = Lists.newArrayList();
        final Item bke6 = bki.getItem();
        final int integer2 = bke6.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, bki, 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 bnz) {
        final Iterator<EnchantmentInstance> iterator3 = list.iterator();
        while (iterator3.hasNext()) {
            if (!bnz.enchantment.isCompatibleWith(iterator3.next().enchantment)) {
                iterator3.remove();
            }
        }
    }
    
    public static boolean isEnchantmentCompatible(final Collection<Enchantment> collection, final Enchantment bnw) {
        for (final Enchantment bnw2 : collection) {
            if (!bnw2.isCompatibleWith(bnw)) {
                return false;
            }
        }
        return true;
    }
    
    public static List<EnchantmentInstance> getAvailableEnchantmentResults(final int integer, final ItemStack bki, final boolean boolean3) {
        final List<EnchantmentInstance> list4 = Lists.newArrayList();
        final Item bke5 = bki.getItem();
        final boolean boolean4 = bki.getItem() == Items.BOOK;
        for (final Enchantment bnw8 : Registry.ENCHANTMENT) {
            if (bnw8.isTreasureOnly() && !boolean3) {
                continue;
            }
            if (!bnw8.isDiscoverable()) {
                continue;
            }
            if (!bnw8.category.canEnchant(bke5) && !boolean4) {
                continue;
            }
            for (int integer2 = bnw8.getMaxLevel(); integer2 > bnw8.getMinLevel() - 1; --integer2) {
                if (integer >= bnw8.getMinCost(integer2) && integer <= bnw8.getMaxCost(integer2)) {
                    list4.add(new EnchantmentInstance(bnw8, integer2));
                    break;
                }
            }
        }
        return list4;
    }
    
    @FunctionalInterface
    interface EnchantmentVisitor {
        void accept(final Enchantment bnw, final int integer);
    }
}
