package untouchedwagons.minecraft.mcrc2.crafting;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraftforge.fluids.FluidStack;
import untouchedwagons.minecraft.mcrc2.MinecraftResourceCalculatorMod;
import untouchedwagons.minecraft.mcrc2.api.recipes.RecipeWrapper;
import untouchedwagons.minecraft.mcrc2.api.recipes.ingredients.GenericTool;
import untouchedwagons.minecraft.mcrc2.exceptions.InfiniteRecursionException;
import untouchedwagons.minecraft.mcrc2.registry.GameRegistry;
import untouchedwagons.minecraft.mcrc2.registry.MinecraftItem;
import java.util.*;
public class CraftingTree implements ICraftingTree {
private Map<String, Integer> tools_in_use;
private long start_time;
private String result;
private Integer amount;
private Boolean excess;
private RecipeWrapper recipe;
private Integer recipe_count;
private Map<String, Integer> excess_items;
private GameRegistry registry;
private Map<Item, String> item_id_lookup;
private Map<String, Integer> selected_recipes;
private List<ICraftingTree> ingredients;
public CraftingTree(GameRegistry game_registry,
Map<String, Integer> excess_items,
Map<String, Integer> tools_in_use,
long start_time) {
this.registry = game_registry;
this.item_id_lookup = this.registry.getItemIdReverseLookup();
this.selected_recipes = game_registry.getSelectedRecipes();
this.excess_items = excess_items;
this.tools_in_use = tools_in_use;
this.start_time = start_time;
this.ingredients = new ArrayList<ICraftingTree>();
this.excess = false;
}
public void craft(String item, Integer amount) throws InfiniteRecursionException {
this.result = item;
this.amount = amount;
// If 5 seconds have passed since starting, we bail since we're probably stuck
// in an infinite loop
if(System.currentTimeMillis() - this.start_time > 5000) {
throw new InfiniteRecursionException();
}
MinecraftItem item_obj = this.registry.getItems().get(item);
List<RecipeWrapper> recipes = item_obj.getRecipes();
// No need to go any further if there's no recipes
if ((this.recipe_count = recipes.size()) == 0) {
return;
}
Integer recipe_pos = this.selected_recipes.get(this.result);
// If the recipe position is out of bounds or the user has not
// changed the default recipe
if (recipe_pos == null ||
recipe_pos < 0 ||
recipe_pos >= this.recipe_count) {
recipe_pos = 0;
}
this.recipe = recipes.get(recipe_pos);
/* TODO
* This can be improved a bit. If 6 items are required and there's
* 2 excess and the item is made in batches of 4, 8 will be crafted
* when the excess can be used.
*/
// If there is sufficient excess, use it
Integer excess_count;
if ((excess_count = this.excess_items.get(this.result)) != null &&
excess_count > amount)
{
this.excess = true;
this.excess_items.put(this.result, excess_count - amount);
return;
}
// Calculate the number of sets of items we need
Integer bundles = (int)Math.ceil(amount / this.recipe.getResult().getAmount());
Integer excess = (this.recipe.getResult().getAmount() * bundles) - amount;
// If there'll be leftover we'll add it to the list
if (excess > 0) {
this.handleExcess(this.result, excess);
}
// Take care of any by-products made by the recipe
// For example, an empty bucket when making cake
for (Map.Entry<ItemStack, Integer> by_product : this.recipe.getByProducts().entrySet())
{
String by_product_name = this.item_id_lookup.get(by_product.getKey().getItem());
if (by_product.getKey().getHasSubtypes()) {
by_product_name += String.format(":%d", by_product.getKey().getItemDamage());
}
this.handleExcess(by_product_name, by_product.getValue());
}
for (Map.Entry<Object, Integer> ingredient : this.recipe.getIngredients().entrySet())
{
Integer items_required = ingredient.getValue() * bundles;
String ingredient_name = "";
ICraftingTree crafting_tree;
if (ingredient.getKey() instanceof ItemStack)
{
ItemStack ingredient_is = (ItemStack) ingredient.getKey();
ingredient_name = this.registry.getItemIdReverseLookup().get(ingredient_is.getItem());
if (ingredient_is.getHasSubtypes()) {
ingredient_name += String.format(":%d", ingredient_is.getItemDamage());
}
// If the ingredient is a by-product we'll only need the ingredient count for all the crafting
// steps since the ingredient can be reused
if (this.recipe.getByProducts().get(ingredient.getKey()) == ingredient.getValue()) {
items_required = ingredient.getValue();
}
}
else if (ingredient.getKey() instanceof List)
{
ingredient_name = this.registry.getOredictName((List) ingredient.getKey());
}
else if (ingredient.getKey() instanceof FluidStack)
{
ingredient_name = "Forge:" + ((FluidStack)ingredient.getKey()).getUnlocalizedName();
}
else if (ingredient.getKey() instanceof GenericTool)
{
GenericTool tool = (GenericTool) ingredient.getKey();
ingredient_name = tool.getId();
Integer new_tool_durability = tool.getDurability();
Integer used_tool_durability;
Integer temp_bundles = bundles;
// If the tool has already been made and has more than enough durability we'll use it first
if ((used_tool_durability = this.tools_in_use.get(ingredient_name)) != null)
{
// If there's not enough or just enough durability we'll use up the tool first
if (used_tool_durability <= temp_bundles)
{
temp_bundles -= used_tool_durability;
this.tools_in_use.remove(ingredient_name);
crafting_tree = new UsedToolCraftingTree();
crafting_tree.craft(ingredient_name, 1);
this.ingredients.add(crafting_tree);
}
// If we still need to make more
if (temp_bundles > 0)
{
items_required = (int)Math.ceil(temp_bundles / new_tool_durability);
used_tool_durability = (items_required * new_tool_durability) - temp_bundles;
this.tools_in_use.put(ingredient_name, used_tool_durability);
}
else
{
continue;
}
}
}
else
{
if (MinecraftResourceCalculatorMod.do_logging) {
MinecraftResourceCalculatorMod.error_logger.println(
String.format("Found unknown ingredient type. Expected: ItemStack, List or FluidStack, got: %s", ingredient.getKey().getClass())
);
}
continue;
}
crafting_tree = new CraftingTree(this.registry, this.excess_items, this.tools_in_use, this.start_time);
crafting_tree.craft(ingredient_name, items_required);
this.ingredients.add(crafting_tree);
}
}
protected void handleExcess(String item, Integer amount)
{
Integer current_excess = this.excess_items.get(item);
if (current_excess == null) {
this.excess_items.put(item, amount);
} else {
this.excess_items.put(item, current_excess + amount);
}
}
public String getResult() {
return this.result;
}
public Integer getAmount() {
return this.amount;
}
public boolean isExcess() {
return this.excess;
}
public List<ICraftingTree> getIngredients() {
return this.ingredients;
}
public RecipeWrapper getRecipe() {
return this.recipe;
}
public Integer getRecipeCount() {
return this.recipe_count;
}
}