package common.enchantment;

import java.util.Map;
import common.collect.Maps;
import common.entity.DamageSource;
import common.entity.Entity;
import common.entity.types.EntityLiving;
import common.item.ItemStack;
import common.item.tool.ItemArmor;
import common.item.tool.ItemAxe;
import common.item.tool.ItemShears;
import common.rng.Random;
import common.util.Displayable;
import common.util.ExtMath;
import common.util.Identifyable;
import common.vars.Vars;

public enum Enchantment implements Displayable, Identifyable
{   
    PROTECTION("protection", 10, EnchantmentType.ARMOR, "Schutz") {
    	public int getMinEnchantability(int enchantmentLevel)
        {
            return 1 + (enchantmentLevel - 1) * 11;
        }

        public int getMaxEnchantability(int enchantmentLevel)
        {
            return this.getMinEnchantability(enchantmentLevel) + 20;
        }

        public int getMaxLevel()
        {
            return 4;
        }

        public int calcDamageReduction(int level, DamageSource source)
        {
            if (source == DamageSource.outOfWorld)
            {
                return 0;
            }
            else
            {
                float f = (float)(6 + level * level) / 3.0F;
                return ExtMath.floorf(f * 0.75F);
            }
        }
    },
    FIRE_PROTECTION("fire_protection", 5, EnchantmentType.ARMOR, "Feuerschutz") {
    	public int getMinEnchantability(int enchantmentLevel)
        {
            return 10 + (enchantmentLevel - 1) * 8;
        }

        public int getMaxEnchantability(int enchantmentLevel)
        {
            return this.getMinEnchantability(enchantmentLevel) + 12;
        }

        public int getMaxLevel()
        {
            return 4;
        }

        public int calcDamageReduction(int level, DamageSource source)
        {
            if (source == DamageSource.outOfWorld)
            {
                return 0;
            }
            else
            {
                float f = (float)(6 + level * level) / 3.0F;
                return source.isFireDamage() ? ExtMath.floorf(f * 1.25F) : 0;
            }
        }
    },
    FEATHER_FALLING("feather_falling", 5, EnchantmentType.ARMOR_FEET, "Federfall") {
    	public int getMinEnchantability(int enchantmentLevel)
        {
            return 5 + (enchantmentLevel - 1) * 6;
        }

        public int getMaxEnchantability(int enchantmentLevel)
        {
            return this.getMinEnchantability(enchantmentLevel) + 10;
        }

        public int getMaxLevel()
        {
            return 4;
        }

        public int calcDamageReduction(int level, DamageSource source)
        {
            if (source == DamageSource.outOfWorld)
            {
                return 0;
            }
            else
            {
                float f = (float)(6 + level * level) / 3.0F;
                return source == DamageSource.fall ? ExtMath.floorf(f * 2.5F) : 0;
            }
        }
    },
    BLAST_PROTECTION("blast_protection", 2, EnchantmentType.ARMOR, "Explosionsschutz") {
    	public int getMinEnchantability(int enchantmentLevel)
        {
            return 5 + (enchantmentLevel - 1) * 8;
        }

        public int getMaxEnchantability(int enchantmentLevel)
        {
            return this.getMinEnchantability(enchantmentLevel) + 12;
        }

        public int getMaxLevel()
        {
            return 4;
        }

        public int calcDamageReduction(int level, DamageSource source)
        {
            if (source == DamageSource.outOfWorld)
            {
                return 0;
            }
            else
            {
                float f = (float)(6 + level * level) / 3.0F;
                return source.isExplosion() ? ExtMath.floorf(f * 1.5F) : 0;
            }
        }
    },
    PROJECTILE_PROTECTION("projectile_protection", 5, EnchantmentType.ARMOR, "Schusssicher") {
    	public int getMinEnchantability(int enchantmentLevel)
        {
            return 3 + (enchantmentLevel - 1) * 6;
        }

        public int getMaxEnchantability(int enchantmentLevel)
        {
            return this.getMinEnchantability(enchantmentLevel) + 15;
        }

        public int getMaxLevel()
        {
            return 4;
        }

        public int calcDamageReduction(int level, DamageSource source)
        {
            if (source == DamageSource.outOfWorld)
            {
                return 0;
            }
            else
            {
                float f = (float)(6 + level * level) / 3.0F;
                return source.isProjectile() ? ExtMath.floorf(f * 1.5F) : 0;
            }
        }
    },
    THORNS("thorns", 1, EnchantmentType.ARMOR_TORSO, "Dornen") {
    	public int getMinEnchantability(int enchantmentLevel)
        {
            return 10 + 20 * (enchantmentLevel - 1);
        }

        public int getMaxEnchantability(int enchantmentLevel)
        {
            return super.getMinEnchantability(enchantmentLevel) + 50;
        }

        public int getMaxLevel()
        {
            return 3;
        }

        public boolean canApply(ItemStack stack)
        {
            return stack.getItem() instanceof ItemArmor ? true : super.canApply(stack);
        }

        public void onUserHurt(EntityLiving user, Entity attacker, int level)
        {
            Random random = user.getRNG();
            ItemStack itemstack = EnchantmentHelper.getEnchantedItem(Enchantment.THORNS, user);

            if ((user.worldObj.client || Vars.damageThorns) && isThornsHurting(level, random))
            {
                if (attacker != null)
                {
                    attacker.attackEntityFrom(DamageSource.causeThornsDamage(user), getThornsDamage(level, random));
//                    attacker.playSound("damage.thorns", 0.5F, 1.0F);
                }

                if (itemstack != null)
                {
                    itemstack.damageItem(3, user);
                }
            }
            else if (itemstack != null)
            {
                itemstack.damageItem(1, user);
            }
        }
    },
    SHARPNESS("sharpness", 10, EnchantmentType.WEAPON, "Schärfe") {
        public int getMinEnchantability(int enchantmentLevel)
        {
            return 1 + (enchantmentLevel - 1) * 11;
        }

        public int getMaxEnchantability(int enchantmentLevel)
        {
            return this.getMinEnchantability(enchantmentLevel) + 20;
        }

        public int getMaxLevel()
        {
            return 5;
        }

        public int calcAdditionalDamage(int level)
        {
            return (int)(/*this.damageType == 0 ? */ (float)level * 1.25F); // : (this.damageType == 1 && creatureType == CreatureType.UNDEAD ? (float)level * 2.5F : (this.damageType == 2 && creatureType == CreatureType.ARTHROPOD ? (float)level * 2.5F : 0.0F)));
        }

        public boolean canApply(ItemStack stack)
        {
            return stack.getItem() instanceof ItemAxe ? true : super.canApply(stack);
        }

//        /**
//         * Called whenever a mob is damaged with an item that has this enchantment on it.
//         */
//        public void onEntityDamaged(EntityLivingBase user, Entity target, int level)
//        {
//            if (target instanceof EntityLivingBase)
//            {
//                EntityLivingBase entitylivingbase = (EntityLivingBase)target;
    //
//                if (this.damageType == 2 && entitylivingbase.getCreatureType() == CreatureType.ARTHROPOD)
//                {
//                    int i = 20 + user.getRNG().zrange(10 * level);
//                    entitylivingbase.addEffect(new PotionEffect(Potion.moveSlowdown.id, i, 3));
//                }
//            }
//        }
    },
    KNOCKBACK("knockback", 5, EnchantmentType.WEAPON, "Rückstoß") {
        public int getMinEnchantability(int enchantmentLevel)
        {
            return 5 + 20 * (enchantmentLevel - 1);
        }

        public int getMaxEnchantability(int enchantmentLevel)
        {
            return super.getMinEnchantability(enchantmentLevel) + 50;
        }

        public int getMaxLevel()
        {
            return 2;
        }
    },
    FIRE_ASPECT("fire_aspect", 2, EnchantmentType.WEAPON, "Verbrennung") {
        public int getMinEnchantability(int enchantmentLevel)
        {
            return 10 + 20 * (enchantmentLevel - 1);
        }

        public int getMaxEnchantability(int enchantmentLevel)
        {
            return super.getMinEnchantability(enchantmentLevel) + 50;
        }

        public int getMaxLevel()
        {
            return 2;
        }
    },
    LOOTING("looting", 2, EnchantmentType.WEAPON, "Plünderung") {
        public int getMinEnchantability(int enchantmentLevel)
        {
            return 15 + (enchantmentLevel - 1) * 9;
        }

        public int getMaxEnchantability(int enchantmentLevel)
        {
            return super.getMinEnchantability(enchantmentLevel) + 50;
        }

        public int getMaxLevel()
        {
            return 3;
        }
    },
    EFFICIENCY("efficiency", 10, EnchantmentType.DIGGER, "Effizienz") {
        public int getMinEnchantability(int enchantmentLevel)
        {
            return 1 + 10 * (enchantmentLevel - 1);
        }

        public int getMaxEnchantability(int enchantmentLevel)
        {
            return super.getMinEnchantability(enchantmentLevel) + 50;
        }

        public int getMaxLevel()
        {
            return 5;
        }

        public boolean canApply(ItemStack stack)
        {
            return stack.getItem() instanceof ItemShears ? true : super.canApply(stack);
        }
    },
    SILK_TOUCH("silk_touch", 1, EnchantmentType.DIGGER, "Behutsamkeit") {
        public int getMinEnchantability(int enchantmentLevel)
        {
            return 15;
        }

        public int getMaxEnchantability(int enchantmentLevel)
        {
            return super.getMinEnchantability(enchantmentLevel) + 50;
        }

        public int getMaxLevel()
        {
            return 1;
        }

        public boolean canApplyTogether(Enchantment ench)
        {
            return super.canApplyTogether(ench) && ench != FORTUNE;
        }

        public boolean canApply(ItemStack stack)
        {
            return stack.getItem() instanceof ItemShears ? true : super.canApply(stack);
        }
    },
    UNBREAKING("unbreaking", 5, EnchantmentType.BREAKABLE, "Haltbarkeit") {
        public int getMinEnchantability(int enchantmentLevel)
        {
            return 5 + (enchantmentLevel - 1) * 8;
        }

        public int getMaxEnchantability(int enchantmentLevel)
        {
            return super.getMinEnchantability(enchantmentLevel) + 50;
        }

        public int getMaxLevel()
        {
            return 3;
        }

        public boolean canApply(ItemStack stack)
        {
            return stack.isItemStackDamageable() ? true : super.canApply(stack);
        }
    },
    FORTUNE("fortune", 2, EnchantmentType.DIGGER, "Glück") {
        public int getMinEnchantability(int enchantmentLevel)
        {
            return 15 + (enchantmentLevel - 1) * 9;
        }

        public int getMaxEnchantability(int enchantmentLevel)
        {
            return super.getMinEnchantability(enchantmentLevel) + 50;
        }

        public int getMaxLevel()
        {
            return 3;
        }

        public boolean canApplyTogether(Enchantment ench)
        {
            return super.canApplyTogether(ench) && ench != SILK_TOUCH;
        }
    },
    POWER("power", 10, EnchantmentType.BOW, "Stärke") {
        public int getMinEnchantability(int enchantmentLevel)
        {
            return 1 + (enchantmentLevel - 1) * 10;
        }

        public int getMaxEnchantability(int enchantmentLevel)
        {
            return this.getMinEnchantability(enchantmentLevel) + 15;
        }

        public int getMaxLevel()
        {
            return 5;
        }
    },
    PUNCH("punch", 2, EnchantmentType.BOW, "Schlag") {
        public int getMinEnchantability(int enchantmentLevel)
        {
            return 12 + (enchantmentLevel - 1) * 20;
        }

        public int getMaxEnchantability(int enchantmentLevel)
        {
            return this.getMinEnchantability(enchantmentLevel) + 25;
        }

        public int getMaxLevel()
        {
            return 2;
        }
    },
    FLAME("flame", 2, EnchantmentType.BOW, "Flamme") {
        public int getMinEnchantability(int enchantmentLevel)
        {
            return 20;
        }

        public int getMaxEnchantability(int enchantmentLevel)
        {
            return 50;
        }

        public int getMaxLevel()
        {
            return 1;
        }
    },
    INFINITY("infinity", 1, EnchantmentType.BOW, "Unendlich") {
    	public int getMinEnchantability(int enchantmentLevel)
        {
            return 20;
        }

        public int getMaxEnchantability(int enchantmentLevel)
        {
            return 50;
        }

        public int getMaxLevel()
        {
            return 1;
        }
    },
    LUCK_OF_THE_SEA("luck_of_the_sea", 2, EnchantmentType.FISHING_ROD, "Glück des Meeres") {
        public int getMinEnchantability(int enchantmentLevel)
        {
            return 15 + (enchantmentLevel - 1) * 9;
        }

        public int getMaxEnchantability(int enchantmentLevel)
        {
            return super.getMinEnchantability(enchantmentLevel) + 50;
        }

        public int getMaxLevel()
        {
            return 3;
        }
    },
    LURE("lure", 2, EnchantmentType.FISHING_ROD, "Köder") {
        public int getMinEnchantability(int enchantmentLevel)
        {
            return 15 + (enchantmentLevel - 1) * 9;
        }

        public int getMaxEnchantability(int enchantmentLevel)
        {
            return super.getMinEnchantability(enchantmentLevel) + 50;
        }

        public int getMaxLevel()
        {
            return 3;
        }
    },
    DRAINING("draining", 1, EnchantmentType.WEAPON, "Seelenentzug") {
    	public int getMinEnchantability(int enchantmentLevel) {
    		return 2 + (enchantmentLevel - 1) * 11;
    	}

    	public int getMaxEnchantability(int enchantmentLevel) {
    		return this.getMinEnchantability(enchantmentLevel) + 20;
    	}

    	public int getMaxLevel() {
    		return 16;
    	}

    	public boolean canApply(ItemStack stack) {
    		return stack.getItem() instanceof ItemAxe ? true : super.canApply(stack);
    	}

    	public void onEntityDamaged(EntityLiving user, Entity target, int level) {
    		if(target instanceof EntityLiving) {
    			EntityLiving entity = (EntityLiving)target;
    			entity.attackEntityFrom(DamageSource.causeIndirectMagicDamage(user, user), (int)((float)level * 1.25F));
    			user.heal((int)((entity.arePotionsInverted() ? 0.2f : 1.0f) * (float)level));
    		}
    	}
    };
    
    private static final String[] LEVELS = new String[] {"I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X"};
    private static final Map<String, Enchantment> LOOKUP = Maps.<String, Enchantment>newHashMap();
    
    private final String name;
    private final int weight;
    private final EnchantmentType type;
    private final String display;
    
    static {
    	for(Enchantment ench : values()) {
            if(LOOKUP.containsKey(ench.name))
                throw new IllegalArgumentException("Verzauberung " + ench.name + " ist bereits registriert");
            LOOKUP.put(ench.name, ench);
    	}
    }

    public static Enchantment getEnchantment(String name)
    {
        return LOOKUP.get(name);
    }

    private Enchantment(String name, int weight, EnchantmentType type, String display)
    {
        this.name = name;
        this.weight = weight;
        this.type = type;
        this.display = display;
    }

    public int getWeight()
    {
        return this.weight;
    }

    public int getMinLevel()
    {
        return 1;
    }

    public int getMaxLevel()
    {
        return 1;
    }

    public int getMinEnchantability(int enchantmentLevel)
    {
        return 1 + enchantmentLevel * 10;
    }

    public int getMaxEnchantability(int enchantmentLevel)
    {
        return this.getMinEnchantability(enchantmentLevel) + 5;
    }

    public int calcDamageReduction(int level, DamageSource source)
    {
        return 0;
    }

    public int calcAdditionalDamage(int level)
    {
        return 0;
    }

    public boolean canApplyTogether(Enchantment ench)
    {
        return this != ench;
    }
    
    public final String getName() {
    	return this.name;
    }

    public String getDisplay()
    {
        return this.display;
    }

    public EnchantmentType getType() {
        return this.type;
    }
    
    public String getFormattedName(int level)
    {
        return this.getDisplay() + " " + ((level >= 1 && level <= LEVELS.length) ? LEVELS[level - 1] : "" + level);
    }

    public boolean canApply(ItemStack stack)
    {
        return this.type.canEnchantItem(stack.getItem());
    }

    public void onEntityDamaged(EntityLiving user, Entity target, int level)
    {
    }

    public void onUserHurt(EntityLiving user, Entity attacker, int level)
    {
    }

    public static int getFireTimeForEntity(Entity entity, int duration)
    {
        int i = EnchantmentHelper.getMaxEnchantmentLevel(Enchantment.FIRE_PROTECTION, entity.getInventory());

        if (i > 0)
        {
            duration -= ExtMath.floorf((float)duration * (float)i * 0.15F);
        }

        return duration;
    }

    public static double getKnockbackFactor(Entity entity, double strength)
    {
        int i = EnchantmentHelper.getMaxEnchantmentLevel(Enchantment.BLAST_PROTECTION, entity.getInventory());

        if (i > 0)
        {
            strength -= (double)ExtMath.floord(strength * (double)((float)i * 0.15F));
        }

        return strength;
    }

    public static boolean negateDamage(ItemStack stack, int level, Random rand)
    {
        return stack.getItem() instanceof ItemArmor && rand.floatv() < 0.6F ? false : rand.rarity(level + 1);
    }

    public static boolean isThornsHurting(int level, Random rand)
    {
        return level <= 0 ? false : rand.floatv() < 0.15F * (float)level;
    }

    public static int getThornsDamage(int level, Random rand)
    {
        return level > 10 ? level - 10 : rand.roll(4);
    }
}
