package electroblob.wizardry.enchantment;

import com.google.common.collect.Iterables;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.registry.WizardryEnchantments;
import electroblob.wizardry.spell.FreezingWeapon;
import electroblob.wizardry.spell.ImbueWeapon;
import net.minecraft.enchantment.Enchantment;
import net.minecraft.enchantment.EnchantmentHelper;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.entity.projectile.EntityArrow;
import net.minecraft.init.Items;
import net.minecraft.inventory.ContainerChest;
import net.minecraft.inventory.Slot;
import net.minecraft.item.ItemEnchantedBook;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTBase;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraftforge.event.entity.EntityJoinWorldEvent;
import net.minecraftforge.event.entity.item.ItemTossEvent;
import net.minecraftforge.event.entity.living.LivingDropsEvent;
import net.minecraftforge.event.entity.player.PlayerContainerEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;

import java.util.Iterator;

/**
 * Interface for temporary enchantments that last for a certain duration ('imbuements'). This interface allows
 * {@link EnchantmentMagicSword} and {@link EnchantmentTimed} to both be treated as instances of a single type, rather
 * than having to deal with each of them separately, which would be inefficient and cumbersome (the former of those
 * classes cannot extend the latter because they both need to extend different subclasses of
 * {@link net.minecraft.enchantment.Enchantment}).
 * 
 * @since Wizardry 1.2
 */
@Mod.EventBusSubscriber
public interface Imbuement {

	// This interface has no abstract methods, these handlers are just here for the sake of keeping things organised.

	@SubscribeEvent
	public static void onLivingDropsEvent(LivingDropsEvent event){
		// Instantly disenchants an imbued weapon if it is dropped when the player dies.
		for(EntityItem item : event.getDrops()){
			removeImbuements(item.getItem());
		}
	}

	@SubscribeEvent
	public static void onItemTossEvent(ItemTossEvent event){
		// Instantly disenchants an imbued weapon if it is thrown on the ground.
		removeImbuements(event.getEntityItem().getItem());
	}

	/** Removes all imbuements from the given itemstack. */
	static void removeImbuements(ItemStack stack){
		if(stack.isItemEnchanted()){
			// No need to check what enchantments the item has, since remove() does nothing if the element does not exist
			NBTTagList enchantmentList = stack.getItem() == Items.ENCHANTED_BOOK ?
					ItemEnchantedBook.getEnchantments(stack) : stack.getEnchantmentTagList();
			// Check all enchantments of the item
			Iterator<NBTBase> enchantmentIt = enchantmentList.iterator();
			while(enchantmentIt.hasNext()){
				NBTTagCompound enchantmentTag = (NBTTagCompound) enchantmentIt.next();
				Enchantment enchantment = Enchantment.getEnchantmentByID(enchantmentTag.getShort("id"));
				// If the item contains a magic weapon enchantment, remove it from the item
				if(enchantment instanceof Imbuement){
					enchantmentIt.remove();
				}
			}
		}
	}

	@SubscribeEvent
	public static void onPlayerOpenContainerEvent(PlayerContainerEvent event){
		// Brute-force fix to stop enchanted books in dungeon chests from having imbuements on them.
		if(event.getContainer() instanceof ContainerChest){
			// Still not sure if it's better to set stacks in slots or modify the itemstack list directly, but I would
			// imagine it's the former.
			for(Slot slot : event.getContainer().inventorySlots){
				ItemStack slotStack = slot.getStack();
				if(slotStack.getItem() instanceof ItemEnchantedBook){
					// We don't care about the level of the enchantments
					NBTTagList enchantmentList = ItemEnchantedBook.getEnchantments(slotStack);
					// Removes all imbuements
					if(Iterables.removeIf(enchantmentList, tag -> {
						NBTTagCompound enchantmentTag = (NBTTagCompound) tag;
						return Enchantment.getEnchantmentByID(enchantmentTag.getShort("id"))
								instanceof Imbuement;
					})){
						// If any imbuements were removed, inform about the removal of the enchantment(s), or
						// delete the book entirely if there are none left.
						if(enchantmentList.isEmpty()){
							slot.putStack(ItemStack.EMPTY);
							Wizardry.logger.info("Deleted enchanted book with illegal enchantments");
						}else{
							// Inform about enchantment removal
							Wizardry.logger.info("Removed illegal enchantments from enchanted book");
						}
					}
				}
			}
		}
	}

	@SubscribeEvent
	public static void onEntityJoinWorld(EntityJoinWorldEvent event){
		// Rather long-winded (but necessary) way of getting an arrow just after it has been fired, checking if the bow
		// that fired it has the imbuement enchantment, and applying extra damage accordingly.
		if(!event.getEntity().world.isRemote && event.getEntity() instanceof EntityArrow){

			EntityArrow arrow = (EntityArrow)event.getEntity();

			if(arrow.shootingEntity instanceof EntityLivingBase){

				EntityLivingBase archer = (EntityLivingBase)arrow.shootingEntity;

				ItemStack bow = archer.getHeldItemMainhand();

				if(!ImbueWeapon.isBow(bow.getItem())){
					bow = archer.getHeldItemOffhand();
					if(!ImbueWeapon.isBow(bow.getItem())) return;
				}

				// Taken directly from ItemBow, so it works exactly the same as the power enchantment.
				int level = EnchantmentHelper.getEnchantmentLevel(WizardryEnchantments.magic_bow, bow);

				if(level > 0){
					arrow.setDamage(arrow.getDamage() + (double)level * 0.5D + 0.5D);
				}

				if(EnchantmentHelper.getEnchantmentLevel(WizardryEnchantments.flaming_weapon, bow) > 0){
					// Again, this is exactly what happens in ItemBow (flame is flame; level does nothing).
					arrow.setFire(100);
				}

				level = EnchantmentHelper.getEnchantmentLevel(WizardryEnchantments.freezing_weapon, bow);

				if(level > 0){
					if(arrow.getEntityData() != null){
						arrow.getEntityData().setInteger(FreezingWeapon.FREEZING_ARROW_NBT_KEY, level);
					}
				}
			}
		}
	}

}
