package com.bergerkiller.bukkit.common.inventory;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.bukkit.Material;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import com.bergerkiller.bukkit.common.reflection.classes.RecipeRef;
import com.bergerkiller.bukkit.common.utils.BlockUtil;
import com.bergerkiller.bukkit.common.utils.ItemUtil;
import com.bergerkiller.bukkit.common.utils.LogicUtil;
import com.bergerkiller.bukkit.common.utils.MaterialUtil;
import com.bergerkiller.bukkit.common.utils.MathUtil;
public class CraftRecipe {
private final ItemStack[] input;
private final ItemStack[] output;
private CraftRecipe(Collection<ItemStack> input, ItemStack output) {
// Convert the input
List<ItemStack> newinput = new ArrayList<ItemStack>(input.size());
boolean create;
for (ItemStack item : input) {
if (LogicUtil.nullOrEmpty(item)) {
continue;
}
item = item.clone();
if (item.getDurability() == Short.MAX_VALUE) {
item.setDurability((short) -1);
}
create = true;
for (ItemStack newitem : newinput) {
if (ItemUtil.equalsIgnoreAmount(item, newitem)) {
ItemUtil.addAmount(newitem, 1);
create = false;
break;
}
}
if (create) {
item.setAmount(1);
newinput.add(item);
}
}
this.input = newinput.toArray(new ItemStack[0]);
// Convert the output
List<ItemStack> newoutput = new ArrayList<ItemStack>(1);
newoutput.add(output.clone());
// Deal with special cases that demand an additional item (added elsewhere)
for (ItemStack stack : newinput) {
if (BlockUtil.isType(stack, Material.LAVA_BUCKET, Material.WATER_BUCKET, Material.MILK_BUCKET)) {
newoutput.add(new ItemStack(Material.BUCKET, stack.getAmount()));
}
}
this.output = newoutput.toArray(new ItemStack[0]);
}
/**
* Gets the input item at the index specified
*
* @param index of the item
* @return input Item
*/
public ItemStack getInput(int index) {
return this.input[index];
}
/**
* Gets all the input items
*
* @return input Items
*/
public ItemStack[] getInput() {
return this.input;
}
/**
* Gets all the output items
*
* @return output Items
*/
public ItemStack[] getOutput() {
return this.output;
}
/**
* Gets the total amount of items, this adds all the amounts of all the items together<br>
* <b>This is not the length of the Input item array!</b>
*
* @return Input item amount
*/
public int getInputSize() {
int count = 0;
for (ItemStack item : this.input) {
count += item.getAmount();
}
return count;
}
/**
* Gets the total amount of items, this adds all the amounts of all the items together<br>
* <b>This is not the length of the Output item array!</b>
*
* @return Output item amount
*/
public int getOutputSize() {
int count = 0;
for (ItemStack item : this.output) {
count += item.getAmount();
}
return count;
}
/**
* Checks whether the input items of this recipe are contained within an Inventory
*
* @param inventory to check
* @return True if the items are available, False if not
*/
public boolean containsInput(Inventory inventory) {
for (ItemStack item : this.input) {
if (ItemUtil.getItemCount(inventory, MaterialUtil.getTypeId(item), MaterialUtil.getRawData(item)) < item.getAmount()) {
return false;
}
}
return true;
}
/**
* Performs this recipe multiple times in the inventory specified
*
* @param inventory to craft in
* @param itemlimit the max amount of resulting items
* @return the amount of resulting items that were crafted
*/
public int craftItems(Inventory inventory, int itemlimit) {
int lim = MathUtil.floor((double) itemlimit / (double) this.output[0].getAmount());
return this.craft(inventory, lim) * this.output[0].getAmount();
}
/**
* Performs this recipe once in the inventory specified
*
* @param inventory to craft in
* @return True if crafting occurred, False if not
*/
public boolean craft(Inventory inventory) {
return craft(inventory, 1) == 1;
}
/**
* Performs this recipe multiple times in the inventory specified
*
* @param inventory to craft in
* @param limit the amount of times it can craft
* @return the amount of times it crafted
*/
public int craft(Inventory inventory, int limit) {
// Before cloning everything, check whether we can craft at all
if (!this.containsInput(inventory)) {
return 0;
}
// Create a (temporary) clone of the inventory to work with
final ItemStack[] items = inventory.getContents();
final int size = items.length;
final Inventory inventoryClone = new InventoryBaseImpl(items, true);
int amount, i;
// Craft items until the limit is reached, or crafting is impossible
// Below is the craftloop label, which is used to break out of crafting
craftloop:
for (amount = 0; amount < limit; amount++) {
// input item check
if (!this.containsInput(inventoryClone)) {
break;
}
// remove ingredients from inventory
for (ItemStack item : this.input) {
ItemUtil.removeItems(inventoryClone, item);
}
// add resulting items to inventory
for (ItemStack item : this.output) {
ItemStack cloned = ItemUtil.cloneItem(item);
ItemUtil.transfer(cloned, inventoryClone, Integer.MAX_VALUE);
// Could not add result (full), unsuccessful
if (!LogicUtil.nullOrEmpty(cloned)) {
break craftloop;
}
}
// Crafting was successful, transfer items over
// Be sure NOT to produce new ItemStack instances!
for (i = 0; i < size; i++) {
ItemStack newItem = inventoryClone.getItem(i);
if (LogicUtil.nullOrEmpty(newItem)) {
items[i] = null;
} else if (items[i] == null) {
items[i] = newItem.clone();
} else {
// Transfer info and amount
ItemUtil.transferInfo(newItem, items[i]);
items[i].setAmount(newItem.getAmount());
}
}
}
// Update input inventory with the new items
inventory.setContents(items);
return amount;
}
/**
* Creates a new Craft Recipe from an IRecipe instance.
* This method is not recommended to be used.
*
* @param recipe to use
* @return the CraftRecipe, or null on failure
*/
public static CraftRecipe create(Object recipe) {
final ItemStack output = RecipeRef.getOutput(recipe);
if (RecipeRef.SHAPED_TEMPLATE.isInstance(recipe)) {
return create(RecipeRef.shapedInput.get(recipe), output);
} else if (RecipeRef.SHAPELESS_TEMPLATE.isInstance(recipe)) {
return create(RecipeRef.shapelessInput.get(recipe), output);
} else {
return null;
}
}
public static CraftRecipe create(Collection<ItemStack> input, ItemStack output) {
if (LogicUtil.nullOrEmpty(input) || LogicUtil.nullOrEmpty(output)) {
return null;
} else {
CraftRecipe rval = new CraftRecipe(input, output);
// Check that input and output are not causing a loop
// For example Sandstone has an infinite crafting loop going on
// (You can craft 4 Sandstone using 4 Sandstone...yeah)
if (rval.input.length == 1 && rval.output.length == 1 && MaterialUtil.getTypeId(rval.input[0]) == MaterialUtil.getTypeId(rval.output[0])) {
return null;
}
return rval;
}
}
}