package com.hea3ven.tools.commonutils.item.crafting;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import net.minecraft.block.Block;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.IRecipe;

import net.minecraftforge.oredict.OreDictionary;
import net.minecraftforge.oredict.ShapedOreRecipe;
import net.minecraftforge.oredict.ShapelessOreRecipe;

public class RecipeBuilder<T extends RecipeBuilder> {
	private boolean shaped = true;
	private ItemStack output;
	private String outputIngredient;
	private int outputSize = 1;
	private String[] ingredients;

	public IRecipe build() {
		if (ingredients == null)
			throw new IllegalStateException("No ingredients were defined");

		ItemStack result = (outputIngredient != null) ? parseStack(outputIngredient) : output.copy();
		result.stackSize = outputSize;
		if (shaped) {
			Map<Character, Boolean> mappings = new HashMap<>();
			List<Object> processedIngredients = new ArrayList<>();
			int i = ingredients.length - 1;
			while (i >= 0) {
				Object ingredient = parseIngredient(ingredients[i]);
				if (ingredient == null)
					break;
				if (ingredients[i - 1].length() != 1)
					throw new IllegalStateException("An ingredient mapping is more than 1 character long");

				char mapping = ingredients[i - 1].charAt(0);
				mappings.put(mapping, false);
				processedIngredients.add(0, ingredient);
				processedIngredients.add(0, mapping);

				i -= 2;
			}
			for (; i >= 0; i--) {
				for (char c : ingredients[i].toCharArray()) {
					if (c == ' ')
						continue;
					if (!mappings.containsKey(c))
						throw new IllegalStateException("Missing maping in the recipe");
					mappings.put(c, true);
				}
				processedIngredients.add(0, ingredients[i]);
			}
			if (mappings.containsValue(false))
				throw new IllegalStateException("Extra mapping in the recipe");

			return new ShapedOreRecipe(result, processedIngredients.toArray());
		} else {
			List<Object> processedIngredients = new ArrayList<>();
			for (String ingredient : ingredients) {
				processedIngredients.add(parseIngredient(ingredient));
			}
			return new ShapelessOreRecipe(result, processedIngredients.toArray());
		}
	}

	protected Object parseIngredient(String ingredient) {
		if (ingredient.indexOf(':') != -1)
			return ingredient;
		ItemStack stack = parseStack(ingredient);
		if (stack != null)
			return stack;
		if (isOreDict(ingredient))
			return ingredient;
		return null;
	}

	private ItemStack parseStack(String ingredient) {
		Block block = Block.getBlockFromName(ingredient);
		if (block != null)
			return new ItemStack(block);
		Item item = Item.getByNameOrId(ingredient);
		if (item != null)
			return new ItemStack(item);
		return null;
	}

	protected boolean isOreDict(String ingredient) {
		return OreDictionary.doesOreNameExist(ingredient);
	}

	@SuppressWarnings("unchecked")
	public T shaped(boolean shaped) {
		this.shaped = shaped;
		return (T) this;
	}

	@SuppressWarnings("unchecked")
	public T output(String ingredient) {
		outputIngredient = ingredient;
		return (T) this;
	}

	@SuppressWarnings("unchecked")
	public T output(ItemStack stack) {
		output = stack;
		return (T) this;
	}

	public T output(Item item) {
		return output(new ItemStack(item));
	}

	public T output(Block block) {
		return output(new ItemStack(block));
	}

	@SuppressWarnings("unchecked")
	public T outputAmount(int size) {
		outputSize = size;
		return (T) this;
	}

	@SuppressWarnings("unchecked")
	public T ingredients(String... ingredients) {
		this.ingredients = ingredients;
		return (T) this;
	}
}

@@ -145,16 +145,80 @@
 til.Map;
+%0Aimport java.util.regex.Matcher;%0Aimport java.util.regex.Pattern;
 %0A%0Aimport
@@ -568,16 +568,113 @@
 lder%3E %7B%0A
+%09private static final Pattern itemPattern = Pattern.compile(%22((%5C%5Cd+)x)?(%5C%5Cw*:%5C%5Cw*)(@(%5C%5Cd+))?%22);%0A%0A
 %09private
@@ -1036,16 +1036,46 @@
 copy();%0A
+%09%09if (result.stackSize == 1)%0A%09
 %09%09result
@@ -1099,16 +1099,16 @@
 utSize;%0A
-
 %09%09if (sh
@@ -2503,67 +2503,8 @@
 ) %7B%0A
-%09%09if (ingredient.indexOf(':') != -1)%0A%09%09%09return ingredient;%0A
 %09%09It
@@ -2811,73 +2811,289 @@
 k);%0A
-%09%09Item item = Item.getByNameOrId(ingredient);%0A%09%09if (item != null)
+%0A%09%09Matcher match = itemPattern.matcher(ingredient);%0A%09%09if (match.matches()) %7B%0A%09%09%09Item item = Item.getByNameOrId(match.group(3));%0A%09%09%09int size = match.group(2) != null ? Integer.parseInt(match.group(2)) : 1;%0A%09%09%09int meta = match.group(5) != null ? Integer.parseInt(match.group(5)) : 0;
 %0A%09%09%09
@@ -3117,18 +3117,34 @@
 ack(item
-);
+, size, meta);%0A%09%09%7D
 %0A%09%09retur

Add the ability to parse item descriptions to the RecipeBuilder