/FileExtraction/Java_unseen/Wind-Development_WindSpigot/WindSpigot-Server/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java
package org.bukkit.craftbukkit.inventory;

import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;

import org.apache.commons.lang.Validate;
import org.bukkit.Material;
import org.bukkit.entity.HumanEntity;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;

import net.minecraft.server.IHopper;
import net.minecraft.server.IInventory;
import net.minecraft.server.InventoryCrafting;
import net.minecraft.server.InventoryEnderChest;
import net.minecraft.server.InventoryMerchant;
import net.minecraft.server.PlayerInventory;
import net.minecraft.server.TileEntityBeacon;
import net.minecraft.server.TileEntityBrewingStand;
import net.minecraft.server.TileEntityDispenser;
import net.minecraft.server.TileEntityDropper;
import net.minecraft.server.TileEntityFurnace;

public class CraftInventory implements Inventory {
	protected final IInventory inventory;

	public CraftInventory(IInventory inventory) {
		this.inventory = inventory;
	}

	public IInventory getInventory() {
		return inventory;
	}

	@Override
	public int getSize() {
		return getInventory().getSize();
	}

	@Override
	public String getName() {
		return getInventory().getName();
	}

	@Override
	public ItemStack getItem(int index) {
		net.minecraft.server.ItemStack item = getInventory().getItem(index);
		return item == null ? null : CraftItemStack.asCraftMirror(item);
	}

	@Override
	public ItemStack[] getContents() {
		ItemStack[] items = new ItemStack[getSize()];
		net.minecraft.server.ItemStack[] mcItems = getInventory().getContents();

		int size = Math.min(items.length, mcItems.length);
		for (int i = 0; i < size; i++) {
			items[i] = mcItems[i] == null ? null : CraftItemStack.asCraftMirror(mcItems[i]);
		}
		return items;
	}

	@Override
	public void setContents(ItemStack[] items) {
		if (getInventory().getContents().length < items.length) {
			throw new IllegalArgumentException(
					"Invalid inventory size; expected " + getInventory().getContents().length + " or less");
		}

		net.minecraft.server.ItemStack[] mcItems = getInventory().getContents();

		for (int i = 0; i < mcItems.length; i++) {
			if (i >= items.length) {
				mcItems[i] = null;
			} else {
				mcItems[i] = CraftItemStack.asNMSCopy(items[i]);
			}
		}
	}

	@Override
	public void setItem(int index, ItemStack item) {
		getInventory().setItem(index,
				((item == null || item.getTypeId() == 0) ? null : CraftItemStack.asNMSCopy(item)));
	}

	@Override
	public boolean contains(int materialId) {
		for (ItemStack item : getContents()) {
			if (item != null && item.getTypeId() == materialId) {
				return true;
			}
		}
		return false;
	}

	@Override
	public boolean contains(Material material) {
		Validate.notNull(material, "Material cannot be null");
		return contains(material.getId());
	}

	@Override
	public boolean contains(ItemStack item) {
		if (item == null) {
			return false;
		}
		for (ItemStack i : getContents()) {
			if (item.equals(i)) {
				return true;
			}
		}
		return false;
	}

	@Override
	public boolean contains(int materialId, int amount) {
		if (amount <= 0) {
			return true;
		}
		for (ItemStack item : getContents()) {
			if (item != null && item.getTypeId() == materialId) {
				if ((amount -= item.getAmount()) <= 0) {
					return true;
				}
			}
		}
		return false;
	}

	@Override
	public boolean contains(Material material, int amount) {
		Validate.notNull(material, "Material cannot be null");
		return contains(material.getId(), amount);
	}

	@Override
	public boolean contains(ItemStack item, int amount) {
		if (item == null) {
			return false;
		}
		if (amount <= 0) {
			return true;
		}
		for (ItemStack i : getContents()) {
			if (item.equals(i) && --amount <= 0) {
				return true;
			}
		}
		return false;
	}

	@Override
	public boolean containsAtLeast(ItemStack item, int amount) {
		if (item == null) {
			return false;
		}
		if (amount <= 0) {
			return true;
		}
		for (ItemStack i : getContents()) {
			if (item.isSimilar(i) && (amount -= i.getAmount()) <= 0) {
				return true;
			}
		}
		return false;
	}

	@Override
	public HashMap<Integer, ItemStack> all(int materialId) {
		HashMap<Integer, ItemStack> slots = new HashMap<Integer, ItemStack>();

		ItemStack[] inventory = getContents();
		for (int i = 0; i < inventory.length; i++) {
			ItemStack item = inventory[i];
			if (item != null && item.getTypeId() == materialId) {
				slots.put(i, item);
			}
		}
		return slots;
	}

	@Override
	public HashMap<Integer, ItemStack> all(Material material) {
		Validate.notNull(material, "Material cannot be null");
		return all(material.getId());
	}

	@Override
	public HashMap<Integer, ItemStack> all(ItemStack item) {
		HashMap<Integer, ItemStack> slots = new HashMap<Integer, ItemStack>();
		if (item != null) {
			ItemStack[] inventory = getContents();
			for (int i = 0; i < inventory.length; i++) {
				if (item.equals(inventory[i])) {
					slots.put(i, inventory[i]);
				}
			}
		}
		return slots;
	}

	@Override
	public int first(int materialId) {
		ItemStack[] inventory = getContents();
		for (int i = 0; i < inventory.length; i++) {
			ItemStack item = inventory[i];
			if (item != null && item.getTypeId() == materialId) {
				return i;
			}
		}
		return -1;
	}

	@Override
	public int first(Material material) {
		Validate.notNull(material, "Material cannot be null");
		return first(material.getId());
	}

	@Override
	public int first(ItemStack item) {
		return first(item, true);
	}

	private int first(ItemStack item, boolean withAmount) {
		if (item == null) {
			return -1;
		}
		ItemStack[] inventory = getContents();
		for (int i = 0; i < inventory.length; i++) {
			if (inventory[i] == null) {
				continue;
			}

			if (withAmount ? item.equals(inventory[i]) : item.isSimilar(inventory[i])) {
				return i;
			}
		}
		return -1;
	}

	@Override
	public int firstEmpty() {
		ItemStack[] inventory = getContents();
		for (int i = 0; i < inventory.length; i++) {
			if (inventory[i] == null) {
				return i;
			}
		}
		return -1;
	}

	public int firstPartial(int materialId) {
		ItemStack[] inventory = getContents();
		for (int i = 0; i < inventory.length; i++) {
			ItemStack item = inventory[i];
			if (item != null && item.getTypeId() == materialId && item.getAmount() < item.getMaxStackSize()) {
				return i;
			}
		}
		return -1;
	}

	public int firstPartial(Material material) {
		Validate.notNull(material, "Material cannot be null");
		return firstPartial(material.getId());
	}

	private int firstPartial(ItemStack item) {
		ItemStack[] inventory = getContents();
		ItemStack filteredItem = CraftItemStack.asCraftCopy(item);
		if (item == null) {
			return -1;
		}
		for (int i = 0; i < inventory.length; i++) {
			ItemStack cItem = inventory[i];
			if (cItem != null && cItem.getAmount() < cItem.getMaxStackSize() && cItem.isSimilar(filteredItem)) {
				return i;
			}
		}
		return -1;
	}

	@Override
	public HashMap<Integer, ItemStack> addItem(ItemStack... items) {
		Validate.noNullElements(items, "Item cannot be null");
		HashMap<Integer, ItemStack> leftover = new HashMap<Integer, ItemStack>();

		/*
		 * TODO: some optimization - Create a 'firstPartial' with a 'fromIndex' - Record
		 * the lastPartial per Material - Cache firstEmpty result
		 */

		for (int i = 0; i < items.length; i++) {
			ItemStack item = items[i];
			while (true) {
				// Do we already have a stack of it?
				int firstPartial = firstPartial(item);

				// Drat! no partial stack
				if (firstPartial == -1) {
					// Find a free spot!
					int firstFree = firstEmpty();

					if (firstFree == -1) {
						// No space at all!
						leftover.put(i, item);
						break;
					} else // More than a single stack!
					if (item.getAmount() > getMaxItemStack()) {
						CraftItemStack stack = CraftItemStack.asCraftCopy(item);
						stack.setAmount(getMaxItemStack());
						setItem(firstFree, stack);
						item.setAmount(item.getAmount() - getMaxItemStack());
					} else {
						// Just store it
						setItem(firstFree, item);
						break;
					}
				} else {
					// So, apparently it might only partially fit, well lets do just that
					ItemStack partialItem = getItem(firstPartial);

					int amount = item.getAmount();
					int partialAmount = partialItem.getAmount();
					int maxAmount = partialItem.getMaxStackSize();

					// Check if it fully fits
					if (amount + partialAmount <= maxAmount) {
						partialItem.setAmount(amount + partialAmount);
						// To make sure the packet is sent to the client
						setItem(firstPartial, partialItem);
						break;
					}

					// It fits partially
					partialItem.setAmount(maxAmount);
					// To make sure the packet is sent to the client
					setItem(firstPartial, partialItem);
					item.setAmount(amount + partialAmount - maxAmount);
				}
			}
		}
		return leftover;
	}

	@Override
	public HashMap<Integer, ItemStack> removeItem(ItemStack... items) {
		Validate.notNull(items, "Items cannot be null");
		HashMap<Integer, ItemStack> leftover = new HashMap<Integer, ItemStack>();

		// TODO: optimization

		for (int i = 0; i < items.length; i++) {
			ItemStack item = items[i];
			int toDelete = item.getAmount();

			while (true) {
				int first = first(item, false);

				// Drat! we don't have this type in the inventory
				if (first == -1) {
					item.setAmount(toDelete);
					leftover.put(i, item);
					break;
				} else {
					ItemStack itemStack = getItem(first);
					int amount = itemStack.getAmount();

					if (amount <= toDelete) {
						toDelete -= amount;
						// clear the slot, all used up
						clear(first);
					} else {
						// split the stack and store
						itemStack.setAmount(amount - toDelete);
						setItem(first, itemStack);
						toDelete = 0;
					}
				}

				// Bail when done
				if (toDelete <= 0) {
					break;
				}
			}
		}
		return leftover;
	}

	private int getMaxItemStack() {
		return getInventory().getMaxStackSize();
	}

	@Override
	public void remove(int materialId) {
		ItemStack[] items = getContents();
		for (int i = 0; i < items.length; i++) {
			if (items[i] != null && items[i].getTypeId() == materialId) {
				clear(i);
			}
		}
	}

	@Override
	public void remove(Material material) {
		Validate.notNull(material, "Material cannot be null");
		remove(material.getId());
	}

	@Override
	public void remove(ItemStack item) {
		ItemStack[] items = getContents();
		for (int i = 0; i < items.length; i++) {
			if (items[i] != null && items[i].equals(item)) {
				clear(i);
			}
		}
	}

	@Override
	public void clear(int index) {
		setItem(index, null);
	}

	@Override
	public void clear() {
		for (int i = 0; i < getSize(); i++) {
			clear(i);
		}
	}

	@Override
	public ListIterator<ItemStack> iterator() {
		return new InventoryIterator(this);
	}

	@Override
	public ListIterator<ItemStack> iterator(int index) {
		if (index < 0) {
			index += getSize() + 1; // ie, with -1, previous() will return the last element
		}
		return new InventoryIterator(this, index);
	}

	@Override
	public List<HumanEntity> getViewers() {
		return this.inventory.getViewers();
	}

	@Override
	public String getTitle() {
		return inventory.getName();
	}

	@Override
	public InventoryType getType() {
		// Thanks to Droppers extending Dispensers, order is important.
		if (inventory instanceof InventoryCrafting) {
			return inventory.getSize() >= 9 ? InventoryType.WORKBENCH : InventoryType.CRAFTING;
		} else if (inventory instanceof PlayerInventory) {
			return InventoryType.PLAYER;
		} else if (inventory instanceof TileEntityDropper) {
			return InventoryType.DROPPER;
		} else if (inventory instanceof TileEntityDispenser) {
			return InventoryType.DISPENSER;
		} else if (inventory instanceof TileEntityFurnace) {
			return InventoryType.FURNACE;
		} else if (this instanceof CraftInventoryEnchanting) {
			return InventoryType.ENCHANTING;
		} else if (inventory instanceof TileEntityBrewingStand) {
			return InventoryType.BREWING;
		} else if (inventory instanceof CraftInventoryCustom.MinecraftInventory) {
			return ((CraftInventoryCustom.MinecraftInventory) inventory).getType();
		} else if (inventory instanceof InventoryEnderChest) {
			return InventoryType.ENDER_CHEST;
		} else if (inventory instanceof InventoryMerchant) {
			return InventoryType.MERCHANT;
		} else if (inventory instanceof TileEntityBeacon) {
			return InventoryType.BEACON;
		} else if (this instanceof CraftInventoryAnvil) {
			return InventoryType.ANVIL;
		} else if (inventory instanceof IHopper) {
			return InventoryType.HOPPER;
		} else {
			return InventoryType.CHEST;
		}
	}

	@Override
	public InventoryHolder getHolder() {
		return inventory.getOwner();
	}

	@Override
	public int getMaxStackSize() {
		return inventory.getMaxStackSize();
	}

	@Override
	public void setMaxStackSize(int size) {
		inventory.setMaxStackSize(size);
	}

	@Override
	public int hashCode() {
		return inventory.hashCode();
	}

	@Override
	public boolean equals(final Object obj) {
		return obj instanceof CraftInventory && ((CraftInventory) obj).inventory.equals(this.inventory);
	}
}
