package common.enchantment;

import java.util.Iterator;
import java.util.List;
import java.util.Map;

import common.collect.Lists;
import common.collect.Maps;
import common.entity.DamageSource;
import common.entity.Entity;
import common.entity.types.EntityLiving;
import common.init.Items;
import common.item.Item;
import common.item.ItemStack;
import common.rng.Random;
import common.rng.WeightedList;
import common.tags.TagObject;

public class EnchantmentHelper
{
    private static final Random enchantmentRand = new Random();
    private static final EnchantmentHelper.ModifierDamage enchantmentModifierDamage = new EnchantmentHelper.ModifierDamage();
    private static final EnchantmentHelper.ModifierLiving enchantmentModifierLiving = new EnchantmentHelper.ModifierLiving();
    private static final EnchantmentHelper.HurtIterator ENCHANTMENT_ITERATOR_HURT = new EnchantmentHelper.HurtIterator();
    private static final EnchantmentHelper.DamageIterator ENCHANTMENT_ITERATOR_DAMAGE = new EnchantmentHelper.DamageIterator();

    public static int getEnchantmentLevel(Enchantment enchID, ItemStack stack)
    {
        if (stack == null)
        {
            return 0;
        }
        else
        {
        	List<TagObject> list = stack.getEnchantmentTagList();

            if (list == null)
            {
                return 0;
            }
            else
            {
                for (int i = 0; i < list.size(); ++i)
                {
                	Enchantment j = Enchantment.getEnchantment(list.get(i).getString("id"));

                    if (j == enchID)
                    {
                        int k = list.get(i).getShort("lvl");
                        return k;
                    }
                }

                return 0;
            }
        }
    }

    public static Map<Enchantment, Integer> getEnchantments(ItemStack stack)
    {
        Map<Enchantment, Integer> map = Maps.<Enchantment, Integer>newLinkedHashMap();
        List<TagObject> list = stack.getItem() == Items.enchanted_book ? Items.enchanted_book.getEnchantments(stack) : stack.getEnchantmentTagList();

        if (list != null)
        {
            for (int i = 0; i < list.size(); ++i)
            {
            	Enchantment j = Enchantment.getEnchantment(list.get(i).getString("id"));
            	if(j != null) {
	                int k = list.get(i).getShort("lvl");
	                map.put(j, k);
            	}
            }
        }

        return map;
    }

    public static void setEnchantments(Map<Enchantment, Integer> enchMap, ItemStack stack)
    {
    	List<TagObject> list = Lists.newArrayList();
        Iterator<Enchantment> iterator = enchMap.keySet().iterator();

        while (iterator.hasNext())
        {
        	Enchantment enchantment = iterator.next();

            TagObject tag = new TagObject();
            tag.setString("id", enchantment.getName());
            tag.setShort("lvl", (short)enchMap.get(enchantment).intValue());
            list.add(tag);

            if (stack.getItem() == Items.enchanted_book)
            {
                Items.enchanted_book.addEnchantment(stack, new RngEnchantment(enchantment, enchMap.get(enchantment).intValue()));
            }
        }

        if (list.size() > 0)
        {
            if (stack.getItem() != Items.enchanted_book)
            {
                stack.setTagInfo("ench", list);
            }
        }
        else if (stack.hasTagCompound())
        {
            stack.getTagCompound().remove("ench");
        }
    }

    /**
     * Returns the biggest level of the enchantment on the array of ItemStack passed.
     */
    public static int getMaxEnchantmentLevel(Enchantment enchID, ItemStack[] stacks)
    {
        if (stacks == null)
        {
            return 0;
        }
        else
        {
            int i = 0;

            for (ItemStack itemstack : stacks)
            {
                int j = getEnchantmentLevel(enchID, itemstack);

                if (j > i)
                {
                    i = j;
                }
            }

            return i;
        }
    }

    /**
     * Executes the enchantment modifier on the ItemStack passed.
     */
    private static void applyEnchantmentModifier(EnchantmentHelper.IModifier modifier, ItemStack stack)
    {
        if (stack != null)
        {
        	List<TagObject> list = stack.getEnchantmentTagList();

            if (list != null)
            {
                for (int i = 0; i < list.size(); ++i)
                {
                    String j = list.get(i).getString("id");
                    int k = list.get(i).getShort("lvl");

                    if (Enchantment.getEnchantment(j) != null)
                    {
                        modifier.calculateModifier(Enchantment.getEnchantment(j), k);
                    }
                }
            }
        }
    }

    /**
     * Executes the enchantment modifier on the array of ItemStack passed.
     */
    private static void applyEnchantmentModifierArray(EnchantmentHelper.IModifier modifier, ItemStack[] stacks)
    {
        for (ItemStack itemstack : stacks)
        {
            applyEnchantmentModifier(modifier, itemstack);
        }
    }

    /**
     * Returns the modifier of protection enchantments on armors equipped on player.
     */
    public static int getEnchantmentModifierDamage(ItemStack[] stacks, DamageSource source)
    {
        enchantmentModifierDamage.damageModifier = 0;
        enchantmentModifierDamage.source = source;
        applyEnchantmentModifierArray(enchantmentModifierDamage, stacks);

        if (enchantmentModifierDamage.damageModifier > 25)
        {
            enchantmentModifierDamage.damageModifier = 25;
        }
        else if (enchantmentModifierDamage.damageModifier < 0)
        {
            enchantmentModifierDamage.damageModifier = 0;
        }

        return (enchantmentModifierDamage.damageModifier + 1 >> 1) + enchantmentRand.zrange((enchantmentModifierDamage.damageModifier >> 1) + 1);
    }

    public static int getDamageModifier(ItemStack p_152377_0_)
    {
        enchantmentModifierLiving.livingModifier = 0;
        applyEnchantmentModifier(enchantmentModifierLiving, p_152377_0_);
        return enchantmentModifierLiving.livingModifier;
    }

    public static void applyThornEnchantments(EntityLiving user, Entity attacker)
    {
        ENCHANTMENT_ITERATOR_HURT.attacker = attacker;
        ENCHANTMENT_ITERATOR_HURT.user = user;

        if (user != null)
        {
            applyEnchantmentModifierArray(ENCHANTMENT_ITERATOR_HURT, user.getInventory());
        }

        if (user != null && attacker != null) // && attacker.isPlayer())
        {
            applyEnchantmentModifier(ENCHANTMENT_ITERATOR_HURT, user.getHeldItem());
        }
    }

    public static void applyArthropodEnchantments(EntityLiving user, Entity target)
    {
        ENCHANTMENT_ITERATOR_DAMAGE.user = user;
        ENCHANTMENT_ITERATOR_DAMAGE.target = target;

        if (user != null)
        {
            applyEnchantmentModifierArray(ENCHANTMENT_ITERATOR_DAMAGE, user.getInventory());
        }

        if (user != null) // && user.isPlayer())
        {
            applyEnchantmentModifier(ENCHANTMENT_ITERATOR_DAMAGE, user.getHeldItem());
        }
    }

    /**
     * Returns the Knockback modifier of the enchantment on the players held item.
     */
    public static int getKnockbackModifier(EntityLiving player)
    {
        return getEnchantmentLevel(Enchantment.KNOCKBACK, player.getHeldItem());
    }

    /**
     * Returns the fire aspect modifier of the players held item.
     */
    public static int getFireAspectModifier(EntityLiving player)
    {
        return getEnchantmentLevel(Enchantment.FIRE_ASPECT, player.getHeldItem());
    }

//    /**
//     * Returns the 'Water Breathing' modifier of enchantments on player equipped armors.
//     */
//    public static int getRespiration(Entity player)
//    {
//        return getMaxEnchantmentLevel(Enchantment.respiration.effectId, player.getInventory());
//    }

//    /**
//     * Returns the level of the Depth Strider enchantment.
//     */
//    public static int getDepthStriderModifier(Entity player)
//    {
//        return getMaxEnchantmentLevel(Enchantment.depthStrider.effectId, player.getInventory());
//    }

    /**
     * Return the extra efficiency of tools based on enchantments on equipped player item.
     */
    public static int getEfficiencyModifier(EntityLiving player)
    {
        return getEnchantmentLevel(Enchantment.EFFICIENCY, player.getHeldItem());
    }

    /**
     * Returns the silk touch status of enchantments on current equipped item of player.
     */
    public static boolean getSilkTouchModifier(EntityLiving player)
    {
        return getEnchantmentLevel(Enchantment.SILK_TOUCH, player.getHeldItem()) > 0;
    }

    /**
     * Returns the fortune enchantment modifier of the current equipped item of player.
     */
    public static int getFortuneModifier(EntityLiving player)
    {
        return getEnchantmentLevel(Enchantment.FORTUNE, player.getHeldItem());
    }

    /**
     * Returns the level of the 'Luck Of The Sea' enchantment.
     */
    public static int getLuckOfSeaModifier(EntityLiving player)
    {
        return getEnchantmentLevel(Enchantment.LUCK_OF_THE_SEA, player.getHeldItem());
    }

    /**
     * Returns the level of the 'Lure' enchantment on the players held item.
     */
    public static int getLureModifier(EntityLiving player)
    {
        return getEnchantmentLevel(Enchantment.LURE, player.getHeldItem());
    }

    /**
     * Returns the looting enchantment modifier of the current equipped item of player.
     */
    public static int getLootingModifier(EntityLiving player)
    {
        return getEnchantmentLevel(Enchantment.LOOTING, player.getHeldItem());
    }

//    /**
//     * Returns the aqua affinity status of enchantments on current equipped item of player.
//     */
//    public static boolean getAquaAffinityModifier(EntityLivingBase player)
//    {
//        return getMaxEnchantmentLevel(Enchantment.aquaAffinity.effectId, player.getInventory()) > 0;
//    }

    public static ItemStack getEnchantedItem(Enchantment p_92099_0_, EntityLiving p_92099_1_)
    {
        for (ItemStack itemstack : p_92099_1_.getInventory())
        {
            if (itemstack != null && getEnchantmentLevel(p_92099_0_, itemstack) > 0)
            {
                return itemstack;
            }
        }

        return null;
    }

    /**
     * Returns the enchantability of itemstack, using a separate calculation for each enchantNum (0, 1 or 2), cutting to
     * the max enchantability power of the table, which is locked to a max of 15.
     */
    public static int calcItemStackEnchantability(Random rand, int enchantNum, int power, ItemStack stack)
    {
        Item item = stack.getItem();
        int i = item.getItemEnchantability();

        if (i <= 0)
        {
            return 0;
        }
        else
        {
            if (power > 15)
            {
                power = 15;
            }

            int j = rand.roll(8) + (power >> 1) + rand.zrange(power + 1);
            return enchantNum == 0 ? Math.max(j / 3, 1) : (enchantNum == 1 ? j * 2 / 3 + 1 : Math.max(j, power * 2));
        }
    }

    /**
     * Adds a random enchantment to the specified item. Args: random, itemStack, enchantabilityLevel
     */
    public static ItemStack addRandomEnchantment(Random p_77504_0_, ItemStack p_77504_1_, int p_77504_2_)
    {
        List<RngEnchantment> list = buildEnchantmentList(p_77504_0_, p_77504_1_, p_77504_2_);
        boolean flag = p_77504_1_.getItem() == Items.book;

        if (flag)
        {
            p_77504_1_.setItem(Items.enchanted_book);
        }

        if (list != null)
        {
            for (RngEnchantment enchantmentdata : list)
            {
                if (flag)
                {
                    Items.enchanted_book.addEnchantment(p_77504_1_, enchantmentdata);
                }
                else
                {
                    p_77504_1_.addEnchantment(enchantmentdata.enchantment, enchantmentdata.level);
                }
            }
        }

        return p_77504_1_;
    }

    public static List<RngEnchantment> buildEnchantmentList(Random randomIn, ItemStack itemStackIn, int level)
    {
        Item item = itemStackIn.getItem();
        int i = item.getItemEnchantability();

        if (i <= 0)
        {
            return null;
        }
        else
        {
            i = i / 2;
            i = randomIn.roll((i >> 1) + 1) + randomIn.zrange((i >> 1) + 1);
            int j = i + level;
            float f = (randomIn.floatv() + randomIn.floatv() - 1.0F) * 0.15F;
            int k = (int)((float)j * (1.0F + f) + 0.5F);

            if (k < 1)
            {
                k = 1;
            }

            List<RngEnchantment> list = null;
            Map<Enchantment, RngEnchantment> map = mapEnchantmentData(k, itemStackIn);

            if (map != null && !map.isEmpty())
            {
                RngEnchantment enchantmentdata = (RngEnchantment)new WeightedList(map.values()).pick(randomIn);

                if (enchantmentdata != null)
                {
                    list = Lists.<RngEnchantment>newArrayList();
                    list.add(enchantmentdata);

                    for (int l = k; randomIn.zrange(50) <= l; l >>= 1)
                    {
                        Iterator<Enchantment> iterator = map.keySet().iterator();

                        while (iterator.hasNext())
                        {
                        	Enchantment ench = iterator.next();
                            boolean flag = true;

                            for (RngEnchantment enchantmentdata1 : list)
                            {
                                if (!enchantmentdata1.enchantment.canApplyTogether(ench))
                                {
                                    flag = false;
                                    break;
                                }
                            }

                            if (!flag)
                            {
                                iterator.remove();
                            }
                        }

                        if (!map.isEmpty())
                        {
                            RngEnchantment enchantmentdata2 = (RngEnchantment)new WeightedList(map.values()).pick(randomIn);
                            list.add(enchantmentdata2);
                        }
                    }
                }
            }

            return list;
        }
    }

    public static Map<Enchantment, RngEnchantment> mapEnchantmentData(int p_77505_0_, ItemStack p_77505_1_)
    {
        Item item = p_77505_1_.getItem();
        Map<Enchantment, RngEnchantment> map = null;
        boolean flag = p_77505_1_.getItem() == Items.book;

        for (Enchantment enchantment : Enchantment.values())
        {
            if (enchantment != null && (enchantment.getType().canEnchantItem(item) || flag))
            {
                for (int i = enchantment.getMinLevel(); i <= enchantment.getMaxLevel(); ++i)
                {
                    if (p_77505_0_ >= enchantment.getMinEnchantability(i) && p_77505_0_ <= enchantment.getMaxEnchantability(i))
                    {
                        if (map == null)
                        {
                            map = Maps.<Enchantment, RngEnchantment>newEnumMap(Enchantment.class);
                        }

                        map.put(enchantment, new RngEnchantment(enchantment, i));
                    }
                }
            }
        }

        return map;
    }

    static final class DamageIterator implements EnchantmentHelper.IModifier
    {
        public EntityLiving user;
        public Entity target;

        private DamageIterator()
        {
        }

        public void calculateModifier(Enchantment enchantmentIn, int enchantmentLevel)
        {
            enchantmentIn.onEntityDamaged(this.user, this.target, enchantmentLevel);
        }
    }

    static final class HurtIterator implements EnchantmentHelper.IModifier
    {
        public EntityLiving user;
        public Entity attacker;

        private HurtIterator()
        {
        }

        public void calculateModifier(Enchantment enchantmentIn, int enchantmentLevel)
        {
            enchantmentIn.onUserHurt(this.user, this.attacker, enchantmentLevel);
        }
    }

    interface IModifier
    {
        void calculateModifier(Enchantment enchantmentIn, int enchantmentLevel);
    }

    static final class ModifierDamage implements EnchantmentHelper.IModifier
    {
        public int damageModifier;
        public DamageSource source;

        private ModifierDamage()
        {
        }

        public void calculateModifier(Enchantment enchantmentIn, int enchantmentLevel)
        {
            this.damageModifier += enchantmentIn.calcDamageReduction(enchantmentLevel, this.source);
        }
    }

    static final class ModifierLiving implements EnchantmentHelper.IModifier
    {
        public int livingModifier;

        private ModifierLiving()
        {
        }

        public void calculateModifier(Enchantment enchantmentIn, int enchantmentLevel)
        {
            this.livingModifier += enchantmentIn.calcAdditionalDamage(enchantmentLevel);
        }
    }
}
