package electroblob.wizardry.inventory;

import electroblob.wizardry.Settings;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.block.BlockBookshelf;
import electroblob.wizardry.registry.WizardryItems;
import electroblob.wizardry.tileentity.TileEntityBookshelf;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Items;
import net.minecraft.inventory.Container;
import net.minecraft.inventory.IInventory;
import net.minecraft.inventory.Slot;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;

import java.util.HashSet;
import java.util.Set;
import java.util.function.Supplier;

public class ContainerBookshelf extends Container {

	private static final Set<Item> validItems = new HashSet<>();

	/** The bookshelf tile entity associated with this container. */
	public TileEntityBookshelf tileentity;

	public ContainerBookshelf(IInventory inventory, TileEntityBookshelf tileentity){

		this.tileentity = tileentity;

		for(int y = 0; y < 2; y++){
			for(int x = 0; x < BlockBookshelf.SLOT_COUNT / 2; x++){
				this.addSlotToContainer(new SlotBookshelf(tileentity, x + BlockBookshelf.SLOT_COUNT / 2 * y, 35 + x * 18, 17 + y * 18));
			}
		}

		for(int x = 0; x < 9; x++){
			this.addSlotToContainer(new Slot(inventory, x, 8 + x * 18, 124));
		}

		for(int y = 0; y < 3; y++){
			for(int x = 0; x < 9; x++){
				this.addSlotToContainer(new Slot(inventory, 9 + x + y * 9, 8 + x * 18, 66 + y * 18));
			}
		}

	}

	/** Called from individual slots when their item is changed or removed. */
	public void onSlotChanged(){
		this.tileentity.sync();
	}

	@Override
	public boolean canInteractWith(EntityPlayer player){
		return this.tileentity.isUsableByPlayer(player);
	}

	@Override
	public ItemStack transferStackInSlot(EntityPlayer player, int clickedSlotId){

		ItemStack remainder = ItemStack.EMPTY;
		Slot slot = this.inventorySlots.get(clickedSlotId);

		if(slot != null && slot.getHasStack()){

			ItemStack stack = slot.getStack(); // The stack that was there originally
			remainder = stack.copy(); // A copy of that stack

			// Bookshelf -> inventory
			if(clickedSlotId < BlockBookshelf.SLOT_COUNT){
				// Tries to move the stack into the player's inventory. If this fails...
				if(!this.mergeItemStack(stack, BlockBookshelf.SLOT_COUNT, this.inventorySlots.size(), true)){
					return ItemStack.EMPTY; // ...nothing else happens.
				}
			}
			// Inventory -> bookshelf
			else{

				int minSlotId = 0;
				int maxSlotId = BlockBookshelf.SLOT_COUNT - 1;

				if(!isBook(stack)) return ItemStack.EMPTY;

				if(!this.mergeItemStack(stack, minSlotId, maxSlotId + 1, false)){
					return ItemStack.EMPTY;
				}
			}

			if(stack.getCount() == 0){
				slot.putStack(ItemStack.EMPTY);
			}else{
				slot.onSlotChanged();
			}

			if(stack.getCount() == remainder.getCount()){
				return ItemStack.EMPTY;
			}

			slot.onTake(player, stack);
		}

		return remainder;
	}

	/** Returns true if the given stack counts as a book and can be placed in a bookshelf, false if not. */
	public static boolean isBook(ItemStack stack){
		return validItems.contains(stack.getItem()) || Settings.containsMetaItem(Wizardry.settings.bookItems, stack);
	}

	/**
	 * Adds the given item to the set of items that can be put in a bookshelf. This method should be called from the
	 * {@code init()} phase.
	 * @param item The item to register
	 * @see BlockBookshelf#registerBookModelTexture(Supplier, ResourceLocation)
	 */
	public static void registerBookItem(Item item){
		validItems.add(item);
	}

	/** Called from {@link Wizardry#init(FMLInitializationEvent)} to register the default book items. */
	public static void initDefaultBookItems(){
		registerBookItem(Items.BOOK);
		registerBookItem(Items.WRITTEN_BOOK);
		registerBookItem(Items.WRITABLE_BOOK);
		registerBookItem(Items.ENCHANTED_BOOK);
		registerBookItem(WizardryItems.spell_book);
		registerBookItem(WizardryItems.arcane_tome);
		registerBookItem(WizardryItems.wizard_handbook);
		registerBookItem(WizardryItems.ruined_spell_book);
		registerBookItem(WizardryItems.scroll);
		registerBookItem(WizardryItems.blank_scroll);
		registerBookItem(WizardryItems.identification_scroll);
		// Not using the map in WandHelper for consistency with BlockBookshelf (again, addons must add theirs manually)
		registerBookItem(WizardryItems.storage_upgrade);
		registerBookItem(WizardryItems.siphon_upgrade);
		registerBookItem(WizardryItems.condenser_upgrade);
		registerBookItem(WizardryItems.range_upgrade);
		registerBookItem(WizardryItems.duration_upgrade);
		registerBookItem(WizardryItems.cooldown_upgrade);
		registerBookItem(WizardryItems.blast_upgrade);
		registerBookItem(WizardryItems.attunement_upgrade);
		registerBookItem(WizardryItems.melee_upgrade);
	}

	public class SlotBookshelf extends Slot {

		public SlotBookshelf(IInventory inventory, int index, int x, int y){
			super(inventory, index, x, y);
		}

		@Override
		public void putStack(ItemStack stack){
			boolean statusChanged = this.getStack().isEmpty() != stack.isEmpty()
					|| BlockBookshelf.getBookItems().indexOf(this.getStack().getItem())
					!= BlockBookshelf.getBookItems().indexOf(stack.getItem());
			super.putStack(stack);
			if(statusChanged) ContainerBookshelf.this.onSlotChanged();
		}

		@Override
		public ItemStack onTake(EntityPlayer player, ItemStack stack){
			ItemStack result = super.onTake(player, stack);
			ContainerBookshelf.this.onSlotChanged();
			return result;
		}

		@Override
		public boolean isItemValid(ItemStack stack){
			return isBook(stack);
		}
	}

}
