001package net.minecraftforge.oredict;
002
003import java.util.ArrayList;
004import java.util.HashMap;
005import java.util.Map;
006import java.util.Map.Entry;
007
008import net.minecraft.block.Block;
009import net.minecraft.item.crafting.IRecipe;
010import net.minecraft.inventory.InventoryCrafting;
011import net.minecraft.item.Item;
012import net.minecraft.item.ItemStack;
013import net.minecraft.item.crafting.ShapedRecipes;
014import net.minecraft.world.World;
015
016public class ShapedOreRecipe implements IRecipe 
017{
018    //Added in for future ease of change, but hard coded for now.
019    private static final int MAX_CRAFT_GRID_WIDTH = 3;
020    private static final int MAX_CRAFT_GRID_HEIGHT = 3;
021    
022    private ItemStack output = null;
023    private Object[] input = null;
024    private int width = 0;
025    private int height = 0;
026    private boolean mirrored = true;
027
028    public ShapedOreRecipe(Block     result, Object... recipe){ this(new ItemStack(result), recipe); }
029    public ShapedOreRecipe(Item      result, Object... recipe){ this(new ItemStack(result), recipe); }
030    public ShapedOreRecipe(ItemStack result, Object... recipe)
031    {
032        output = result.copy();
033
034        String shape = "";
035        int idx = 0;
036
037        if (recipe[idx] instanceof Boolean)
038        {
039            mirrored = (Boolean)recipe[idx];
040            if (recipe[idx+1] instanceof Object[])
041            {
042                recipe = (Object[])recipe[idx+1];
043            }
044            else
045            {
046                idx = 1;
047            }
048        }
049
050        if (recipe[idx] instanceof String[])
051        {
052            String[] parts = ((String[])recipe[idx++]);
053
054            for (String s : parts)
055            {
056                width = s.length();
057                shape += s;
058            }
059            
060            height = parts.length;
061        }
062        else
063        {
064            while (recipe[idx] instanceof String)
065            {
066                String s = (String)recipe[idx++];
067                shape += s;
068                width = s.length();
069                height++;
070            }
071        }
072
073        if (width * height != shape.length())
074        {
075            String ret = "Invalid shaped ore recipe: ";
076            for (Object tmp :  recipe)
077            {
078                ret += tmp + ", ";
079            }
080            ret += output;
081            throw new RuntimeException(ret);
082        }
083
084        HashMap<Character, Object> itemMap = new HashMap<Character, Object>();
085
086        for (; idx < recipe.length; idx += 2)
087        {
088            Character chr = (Character)recipe[idx];
089            Object in = recipe[idx + 1];
090
091            if (in instanceof ItemStack)
092            {
093                itemMap.put(chr, ((ItemStack)in).copy());
094            }
095            else if (in instanceof Item)
096            {
097                itemMap.put(chr, new ItemStack((Item)in));
098            }
099            else if (in instanceof Block)
100            {
101                itemMap.put(chr, new ItemStack((Block)in, 1, -1));
102            }
103            else if (in instanceof String)
104            {
105                itemMap.put(chr, OreDictionary.getOres((String)in));
106            }
107            else
108            {
109                String ret = "Invalid shaped ore recipe: ";
110                for (Object tmp :  recipe)
111                {
112                    ret += tmp + ", ";
113                }
114                ret += output;
115                throw new RuntimeException(ret);
116            }
117        }
118
119        input = new Object[width * height];
120        int x = 0;
121        for (char chr : shape.toCharArray())
122        {
123            input[x++] = itemMap.get(chr);   
124        }
125    }
126
127    ShapedOreRecipe(ShapedRecipes recipe, Map<ItemStack, String> replacements)
128    {
129        output = recipe.getRecipeOutput();
130        width = recipe.recipeWidth;
131        height = recipe.recipeHeight;
132
133        input = new Object[recipe.recipeItems.length];
134
135        for(int i = 0; i < input.length; i++)
136        {
137            ItemStack ingred = recipe.recipeItems[i];
138
139            if(ingred == null) continue;
140
141            input[i] = recipe.recipeItems[i];
142
143            for(Entry<ItemStack, String> replace : replacements.entrySet())
144            {
145                if(OreDictionary.itemMatches(replace.getKey(), ingred, true))
146                {
147                    input[i] = OreDictionary.getOres(replace.getValue());
148                    break;
149                }
150            }
151        }
152    }
153
154    @Override
155    public ItemStack getCraftingResult(InventoryCrafting var1){ return output.copy(); }
156
157    @Override
158    public int getRecipeSize(){ return input.length; }
159
160    @Override
161    public ItemStack getRecipeOutput(){ return output; }
162
163    @Override
164    public boolean matches(InventoryCrafting inv, World world)
165    {        
166        for (int x = 0; x <= MAX_CRAFT_GRID_WIDTH - width; x++)
167        {
168            for (int y = 0; y <= MAX_CRAFT_GRID_HEIGHT - height; ++y)
169            {
170                if (checkMatch(inv, x, y, true))
171                {
172                    return true;
173                }
174    
175                if (mirrored && checkMatch(inv, x, y, false))
176                {
177                    return true;
178                }
179            }
180        }
181    
182        return false;
183    }
184    
185    private boolean checkMatch(InventoryCrafting inv, int startX, int startY, boolean mirror)
186    {
187        for (int x = 0; x < MAX_CRAFT_GRID_WIDTH; x++)
188        {
189            for (int y = 0; y < MAX_CRAFT_GRID_HEIGHT; y++)
190            {
191                int subX = x - startX;
192                int subY = y - startY;
193                Object target = null;
194
195                if (subX >= 0 && subY >= 0 && subX < width && subY < height)
196                {
197                    if (mirror)
198                    {
199                        target = input[width - subX - 1 + subY * width];
200                    }
201                    else
202                    {
203                        target = input[subX + subY * width];
204                    }
205                }
206
207                ItemStack slot = inv.getStackInRowAndColumn(x, y);
208                
209                if (target instanceof ItemStack)
210                {
211                    if (!checkItemEquals((ItemStack)target, slot))
212                    {
213                        return false;
214                    }
215                }
216                else if (target instanceof ArrayList)
217                {
218                    boolean matched = false;
219                    
220                    for (ItemStack item : (ArrayList<ItemStack>)target)
221                    {
222                        matched = matched || checkItemEquals(item, slot);
223                    }
224                    
225                    if (!matched)
226                    {
227                        return false;
228                    }
229                }
230                else if (target == null && slot != null)
231                {
232                    return false;
233                }
234            }
235        }
236
237        return true;
238    }
239
240    private boolean checkItemEquals(ItemStack target, ItemStack input)
241    {
242        if (input == null && target != null || input != null && target == null)
243        {
244            return false;
245        }
246        return (target.itemID == input.itemID && (target.getItemDamage() == -1 || target.getItemDamage() == input.getItemDamage()));
247    }
248
249    public ShapedOreRecipe setMirrored(boolean mirror)
250    {
251        mirrored = mirror;
252        return this;
253    }
254}