@Mod(modid = References.MOD_ID, name = References.MOD_NAME, version = References.MOD_VERSION, dependencies="required-after:Forge@10.13.0.1180;after:TConstruct")
public class OresPlus {
	
	@SidedProxy(clientSide="tw.oresplus.client.ClientProxy", serverSide="tw.oresplus.core.ServerProxy") 
	public static IProxy proxy;
	
	@Instance(References.MOD_ID)
	public static OresPlus instance;

    public static OreLog log;
    
    public static ConfigMain config = new ConfigMain();
    
    public static String regenKeyOre = "DEFAULT";
    public static String regenKeyOil = "DEFAULT";
    public static String regenKeyRubberTree = "DEFAULT";
    public static String regenKeyBeehives = "DEFAULT";

    public static boolean angryPigmen = true;
    public static boolean debugMode = false;
    public static boolean difficultAlloys = false;
    public static boolean easyMineralSmelt = true;
    public static boolean iridiumPlateRecipe = true;
    public static boolean logRegenerations = false;
    
    public static WorldGenCore worldGen = new WorldGenCore();
    public static OreEventHandler eventHandler = new OreEventHandler();
    public static BucketHandler bucketHandler = new BucketHandler();
    public static TickHandler tickHandler = new TickHandler();
    public static IMCHandler imcHandler = new IMCHandler();
    public static ItemMapHelper itemMapHelper;
    
    @EventHandler
    public void preInit(FMLPreInitializationEvent event) {
    	log.init();
    	
		RecipeManager.init();

		ConfigCore.setBaseDir(event.getModConfigurationDirectory());
		config.init(event);
		
		config.load();
		
	    angryPigmen = config.getBoolean("angryPigmen", angryPigmen, "set to false to prevent zombie pigmen from attacking when mining nether ores");
	    debugMode = config.getBoolean("debugMode", debugMode, "set to true to enable finer debug logging");
	    difficultAlloys = config.getBoolean("difficultAlloys", difficultAlloys, "enable true to set brass & bronze alloy recipes to output only 2 dusts");
	    easyMineralSmelt = config.getBoolean("easyMineralSmelt", easyMineralSmelt, "disable to prevent Mineral ores smelting directly into metal");
	    iridiumPlateRecipe = config.getBoolean("iridiumPlateRecipe", iridiumPlateRecipe, "enables an ore dictionary-enabled recipe for iridium plate");
	    logRegenerations = config.getBoolean("logRegenerations", logRegenerations, "enable to log all regenerations that occur");

	    regenKeyOre = config.getString(ConfigCore.CAT_REGEN, "regenKey", regenKeyOre, "change this to regenerate ores");
	    regenKeyOil = config.getString(ConfigCore.CAT_REGEN, "regenKeyOil", regenKeyOil, "change this to regenerate buildcraft oil wells");
	    regenKeyRubberTree = config.getString(ConfigCore.CAT_REGEN, "regenKeyRubberTree", regenKeyRubberTree, "change this to regenerate IC2 rubber trees");
	    regenKeyBeehives = config.getString(ConfigCore.CAT_REGEN, "regenKeyBeehives", regenKeyBeehives, "change this to regenerate Forestry beehives");
		
		Ores.manager = new OreManager();
		
    	BlockManager.init();
    	ItemManager.init();
    	FluidManager.init();
    	GasManager.init();

    	// register aluminium->aluminum ore dictionary 
    	OreDictionary.registerOre("oreAluminum", MetallicOres.Aluminium.ore.source);
    	OreDictionary.registerOre("oreNetherAluminum", MetallicOres.Aluminium.netherOre.source);
    	OreDictionary.registerOre("blockAluminum", MetallicOres.Aluminium.block.source);
    	OreDictionary.registerOre("ingotAluminum", MetallicOres.Aluminium.ingot.source);
    	OreDictionary.registerOre("nuggetAluminum", MetallicOres.Aluminium.nugget.source);
    	OreDictionary.registerOre("crushedAluminum", MetallicOres.Aluminium.crushedOre.source);
    	OreDictionary.registerOre("crushedPurifiedAluminum", MetallicOres.Aluminium.purifiedCrushedOre.source);
    	OreDictionary.registerOre("dustAluminum", MetallicOres.Aluminium.dust.source);
    	OreDictionary.registerOre("dustTinyAluminum", MetallicOres.Aluminium.tinyDust.source);
    	OreDictionary.registerOre("clusterAluminum", MetallicOres.Aluminium.cluster.source);
    	OreDictionary.registerOre("dustDirtyAluminum", MetallicOres.Aluminium.dirtyDust.source);
    	OreDictionary.registerOre("clumpAluminum", MetallicOres.Aluminium.clump.source);
    	OreDictionary.registerOre("shardAluminum", MetallicOres.Aluminium.shard.source);
    	OreDictionary.registerOre("crystalAluminum", MetallicOres.Aluminium.crystal.source);
    	// register mercury-> quicksilver ore dictionary
    	OreDictionary.registerOre("quicksilver", Items.itemMercury.item.source);
    	
    	//Register Ore Generators
    	log.info("Registering Ore Generators");
    	for (OreGenerators oreGen : OreGenerators.values()) {
    		oreGen.registerGenerator();
    	}
    	for (OreGeneratorsNether oreGen : OreGeneratorsNether.values()) {
    		oreGen.registerGenerator();
    	}
    	for (OreGeneratorsEnd oreGen : OreGeneratorsEnd.values()) {
    		oreGen.registerGenerator();
    	}
    	
		config.save();
		
    	for (Helpers helper : Helpers.values()) {
    		helper.preInit();
    	}
    }
    
    @EventHandler
    public void init(FMLInitializationEvent event) {
    	RecipeManager.initRecipes();
    	
    	GameRegistry.registerTileEntity(TileEntityGrinder.class, "TileEntityGrinder");
    	GameRegistry.registerTileEntity(TileEntityCracker.class, "TileEntityCracker");
    	
    	MinecraftForge.EVENT_BUS.register(eventHandler);
    	MinecraftForge.EVENT_BUS.register(bucketHandler);
    	MinecraftForge.ORE_GEN_BUS.register(eventHandler);
    	FMLCommonHandler.instance().bus().register(tickHandler);
    	
    	NetworkRegistry.INSTANCE.registerGuiHandler(instance, new GuiHandler());
    	
    	itemMapHelper = new ItemMapHelper();
    	
    	//OresTrigger.registerTriggers();
    	//ActionManager.registerTriggerProvider(new TriggerProvider());
    	
    	VillagerRegistry.instance().registerVillageTradeHandler(VillagerTradeHandler.VILLAGER_BLACKSMITH, new VillagerTradeHandler(VillagerTradeHandler.VILLAGER_BLACKSMITH));

    	NetHandler.init();
    	
    	for (Helpers helper : Helpers.values()) {
    		helper.init();
    	}
    }
    
    @EventHandler
    public void postInit(FMLPostInitializationEvent event) {
    	if (debugMode) {
    		Ores.grinderRecipes.debug();
    	}
    	
    	//RecipeManager.replaceRecipeResults();
    	
    	OreChestLoot.registerChestLoot();
    	
    	for (Helpers helper : Helpers.values()) {
    		helper.postInit();
    	}
    }
    
    @EventHandler
	public void recieveIMC(FMLInterModComms.IMCEvent event) {
    	imcHandler.recieveIMC(event);
    }
    
    @EventHandler
    public void handleMissingMaps(FMLMissingMappingsEvent event) {
    	itemMapHelper.handleMissingMaps(event);
    }
    
    @EventHandler
    public void handRemaps(FMLModIdMappingEvent event) {
    	BlockManager.handleRemaps(event);
    }
    
} public class Ores {
	private static Ores instance = new Ores();
	
	public static IOreManager manager;
	
	public static IOreFluidManager fluidManager;
	
	public static IOreRecipeManager grinderRecipes;
	public static IOreFluidRecipeManager crackerRecipes;

	private Ores () {}
	
	private class BlockInfo {
		public String ownerMod;
		public Block block;
		
		public BlockInfo(String owner, Block newBlock) {
			this.ownerMod = owner;
			this.block = newBlock;
		}

	}
	
	
} public class BlockCore extends net.minecraft.block.Block {
	public BlockCore(Material material, String blockName, boolean isMeta) {
		super(material);
		this.setBlockName(blockName); // name
		this.setBlockTextureName(References.MOD_ID +":" + blockName); // texture
		if (isMeta)
			GameRegistry.registerBlock(this, ItemBlockWithMetadata.class, blockName);
		else
			GameRegistry.registerBlock(this, blockName);
	}

	public BlockCore(Material material, String blockName) {
		this(material, blockName, false);
	}
} public class BlockOre extends BlockCore {
	private OreDrops drops;
	private int xpDropLow;
	private int xpDropHigh;
	private boolean _isNether;
	
	public BlockOre(OreClass ore, boolean isNether) {
		super (Material.rock, ore.name);
		this.drops = ore.drops;
		this.setCreativeTab(CreativeTabs.tabBlock);
		OreDictionary.registerOre(ore.name, this);
		this.setHardness(3.0F);
		this.setHarvestLevel("pickaxe", ore.harvestLevel);
		this.xpDropLow = ore.xpDropLow;
		this.xpDropHigh = ore.xpDropHigh;
		this._isNether = isNether;
		Ores.manager.registerOre(ore.name, this);
	}
	
	public BlockOre(OreClass ore) {
		this(ore, false);
	}

	private Random rand = new Random();
    @Override
    public int getExpDrop(IBlockAccess var1, int var2, int var3) {
    	if (this.xpDropLow == 0 && this.xpDropHigh == 0)
    		return 0;
    	else
    		return this.xpDropLow + rand.nextInt(this.xpDropHigh - this.xpDropLow + 1);
    }
    
	@Override
    public ArrayList<ItemStack> getDrops(World world, int x, int y, int z, int metadata, int fortune) {
		ArrayList rList = new ArrayList();
		switch (this.drops)	{
		case AMETHYST:
			rList.add(GemstoneOres.Amethyst.gem.newStack(this.fortuneHelper(world, 1, fortune)));
			break;
		case APATITE:
			rList.add(GemstoneOres.Apatite.gem.newStack(this.fortuneHelper(world, 1 + world.rand.nextInt(4), fortune)));
			break;
		case BITUMEN:
			rList.add(Items.itemBitumen.item.newStack(this.fortuneHelper(world, 2 + world.rand.nextInt(4), fortune)));
			break;
		case CERTUSQUARTZ:
			Item certusQuartz = Helpers.AppliedEnergistics.getItem("item.ItemMultiMaterial");
			if (Helpers.AppliedEnergistics.isLoaded() && certusQuartz != null) {
				OreItemStack cqStack = world.rand.nextFloat() > 0.92F ? 
					new OreItemStack(new ItemStack(certusQuartz, 1, 1)) :
					new OreItemStack(new ItemStack(certusQuartz, 1, 0));
				rList.add(cqStack.newStack(fortuneHelper(world, 1 + world.rand.nextInt(2), fortune)));
			}
			else {
				rList.add(new ItemStack(this.getItemDropped(metadata, world.rand, fortune), 1));
			}
			break;
		case CINNABAR: // cinnabar = 1 cinnabar & 1/4 redstone (gregstech)
			rList.add(MineralOres.Cinnabar.dust.newStack(this.fortuneHelper(world, 1, fortune)));
			if (world.rand.nextInt(4) == 0)	{
				rList.add(new ItemStack(net.minecraft.init.Items.redstone, this.fortuneHelper(world, 1, fortune)));
			}
			break;
		case IRIDIUM: // iridium = 1 (half fortune) iridium (gregstech)
			rList.add(Items.gemIridium.item.newStack(this.fortuneHelper(world, 1, fortune)));
			break;
		case MAGNESIUM: // magnesium = 2-5 magnesium (metallurgy)
			rList.add(Items.dustMagnesium.item.newStack(this.fortuneHelper(world, 2 + world.rand.nextInt(4), fortune)));
			break;
		case MALACHITE:
			rList.add(GemstoneOres.Malachite.gem.newStack(this.fortuneHelper(world, 1, fortune)));
			break;
		case NIKOLITE: // nikolite = 4-5 nikolite (rp2)
			rList.add(DustyOres.Nikolite.dust.newStack(this.fortuneHelper(world, 4 + world.rand.nextInt(2), fortune)));
			break;
		case OLIVINE:
			rList.add(Items.gemOlivine.item.newStack(this.fortuneHelper(world, 1, fortune)));
			break;
		case PERIDOT:
			rList.add(GemstoneOres.Peridot.gem.newStack(this.fortuneHelper(world, 1, fortune)));
			break;
		case PHOSPHORITE: // phosphorite = 2-5 phosphorite (metallurgy)
			rList.add(DustyOres.Phosphorite.dust.newStack(this.fortuneHelper(world, 2 + world.rand.nextInt(4), fortune)));
			break;
		case POTASH: // potash = 2-5 potash (metallurgy)
			rList.add(DustyOres.Potash.dust.newStack(this.fortuneHelper(world, 2 + world.rand.nextInt(4), fortune)));
			break;
		case PYRITE: // pyrite = 1 pyrite (gregstech)
			rList.add(MineralOres.Pyrite.dust.newStack(this.fortuneHelper(world, 1, fortune)));
			break;
		case RUBY: // ruby = 1 ruby (twstuff/rp2) | 1 ruby & 1/32 red garnet (gregstech)
			rList.add(GemstoneOres.Ruby.gem.newStack(this.fortuneHelper(world, 1, fortune)));
			if (world.rand.nextInt(32) == 0) {
				rList.add(Items.gemRedGarnet.item.newStack(this.fortuneHelper(world, 1, fortune)));
			}
			break;
		case SALTPETER: // saltpeter = 1-2 saltpeter (railcraft) | 2-5 saltpeter (metallurgy)
			rList.add(DustyOres.Saltpeter.dust.newStack(this.fortuneHelper(world, 1 + world.rand.nextInt(2), fortune)));
			break;
		case SAPPHIRE: // sapphire = 1 sapphire (twstuff/rp2) | 1 sapphiree & 1/64 green sapphire
			rList.add(GemstoneOres.Sapphire.gem.newStack(this.fortuneHelper(world, 1, fortune)));
			if (world.rand.nextInt(64) == 0) {
				rList.add(GemstoneOres.Peridot.gem.newStack(this.fortuneHelper(world, 1, fortune)));
			}
			break;
		case SODALITE: // sodalite = 6 sodalite & 1/4 aluminium (gregstech)
			rList.add(Items.dustSodalite.item.newStack(this.fortuneHelper(world, 6, fortune)));
			if (world.rand.nextInt(4) == 0)	{
				rList.add(MetallicOres.Aluminium.dust.newStack(this.fortuneHelper(world, 1, fortune)));
			}
			break;
		case SPHALERITE: // sphalerite = 1 sphalerite & 1/4 zinc & 1/32 yellow garnet
			rList.add(MineralOres.Sphalerite.dust.newStack(this.fortuneHelper(world, 1, fortune)));
			if (world.rand.nextInt(4) == 0)	{
				rList.add(MetallicOres.Zinc.dust.newStack(this.fortuneHelper(world, 1, fortune)));
			}
			if (world.rand.nextInt(32) == 0) {
				rList.add(Items.gemYellowGarnet.item.newStack(this.fortuneHelper(world, 1, fortune)));
			}
			break;
		case SULFUR: // sulfur = 2-5 sulfur (railcraft/metallurgy)
			rList.add(DustyOres.Sulfur.dust.newStack(this.fortuneHelper(world, 2 + world.rand.nextInt(4), fortune)));
			break;
		case TANZANITE:
			rList.add(GemstoneOres.Tanzanite.gem.newStack(this.fortuneHelper(world, 1, fortune)));
			break;
		case TOPAZ: // topaz = 1 topaz (twstuff)
			rList.add(GemstoneOres.Topaz.gem.newStack(this.fortuneHelper(world, 1, fortune)));
			break;
		default:
			rList.add(new ItemStack(this.getItemDropped(metadata, world.rand, fortune), 1));
		}
		return rList;
    }
	
	private int fortuneHelper(World world, int stackSize, int fortune) {
		switch (this.drops){
		case AMETHYST:
		case CERTUSQUARTZ:
		case CINNABAR:
		case PERIDOT:
		case IRIDIUM:
		case MALACHITE:
		case PYRITE:
		case RUBY:
		case SAPPHIRE:
		case SPHALERITE:
		case TANZANITE:
		case TOPAZ:
		case URANIUM:
			switch (fortune) {
			case 1:
				if (world.rand.nextInt(3) == 0)
					stackSize = stackSize * 2;
				break;
			case 2:
				switch (world.rand.nextInt(4)) {
				case 0:
					stackSize = stackSize * 2;
					break;
				case 1: 
					stackSize = stackSize * 3;
					break;
				}
				break;
			case 3:
				switch (world.rand.nextInt(5)) {
				case 0:
					stackSize = stackSize * 2;
					break;
				case 1:
					stackSize = stackSize * 3;
					break;
				case 2:
					stackSize = stackSize * 4;
					break;
				}
				break;
			}
			break;
		case APATITE:
		case BITUMEN:
		case MAGNESIUM:
		case NIKOLITE:
		case OLIVINE:
		case PHOSPHORITE:
		case POTASH:
		case SALTPETER:
		case SULFUR:
		case SODALITE:
			if (fortune >= 1)
				stackSize++;
			if (fortune >= 2)
				stackSize++;
			if (fortune == 3)
				stackSize++;
			break;
		default:
		}
		return stackSize;
	}

	@Override
    public void harvestBlock(World world, EntityPlayer player, int x, int y, int z, int meta) {
		super.harvestBlock(world, player, x, y, z, meta);
		if (OresPlus.angryPigmen & this._isNether)
			upsetThePigs(player, world, x, y, z);
	}

	private static void upsetThePigs(EntityPlayer player, World world, int x, int y, int z) {
		int range = 32;
		List entities = world.getEntitiesWithinAABB(net.minecraft.entity.monster.EntityPigZombie.class, AxisAlignedBB.getBoundingBox(x - range, y - range, z - range, x + range + 1, y  + range + 1, z + range + 1));
		
		for (Object entity : entities) {
			if (entity instanceof EntityPigZombie) {
				((EntityPigZombie)entity).attackEntityFrom(DamageSource.causePlayerDamage(player), 0);
			}
		}
	}
} public abstract class ConfigCore {
	public static final String CAT_ORES = "ores";
	public static final String CAT_ORE_GEN = "ore_generation";
	public static final String CAT_REGEN = "regeneration";
	private static final String CAT_CONFIG = "configFile";
	
	protected boolean configured = false;
	protected Configuration config;
	
	protected final int configVersion = 1;
	protected int configFileVersion;
	
	protected static File baseConfigDir;
	
	public static void setBaseDir(File modConfigDir) {
		baseConfigDir = new File(modConfigDir, References.MOD_ID + "/");
	}
	
	public void init(String configName, File configFile) {
		OresPlus.log.info("Initializing " + configName + " Configuration");
		
		this.config = new Configuration(configFile);
		
		this.config.load();
		
		this.config.addCustomCategoryComment(CAT_CONFIG, "Config file versioning");

		if (this.config.hasKey(CAT_CONFIG, "configVersion")) {
		    this.configFileVersion = getInt(CAT_CONFIG, "configVersion", configFileVersion, "Configuration File Version - Do not change, modifying this may break your game");
	    }
	    else {
	    	this.configFileVersion = 0;
	    }
	    OresPlus.log.info("Reading from v" + configFileVersion + " Configuration File");
	}
	
	public void save() {
		OresPlus.log.info("Saving Configuration");
		this.saveQuiet();
	}
	
	public void saveQuiet() {
		if (!configured) {
			OresPlus.log.info("Error - configuration not initialized!");
			return;		
		}
		
		if (configFileVersion != configVersion) {
			Property prop = config.get(CAT_CONFIG, "configVersion", configVersion);
			prop.comment = "Configuration File Version - Do not change, modifying this may break your game";
			prop.set(configVersion);
		}
		
		if (config.hasChanged())
			config.save();
	}
	
	private int getInt(String key, int defaultValue, String comment) {
		return getInt(Configuration.CATEGORY_GENERAL, key, defaultValue, comment);
	}

	private int getInt(String category, String key, int defaultValue, String comment) {
		Property prop = config.get(category, key, defaultValue);
		if (comment != "")
			prop.comment = comment;
		return prop.getInt(defaultValue);
	}

	public boolean getBoolean(String key, boolean defaultValue, String comment) {
		Property prop = config.get(Configuration.CATEGORY_GENERAL, key, defaultValue);
		if (comment != "")
			prop.comment = comment;
		return prop.getBoolean(defaultValue);
	}
	
	public boolean getBoolean(String key, boolean defaultValue) {
		return getBoolean(key, defaultValue, "");
	}
	
	public String getString(String catagory, String key, String defaultValue, String comment) {
		Property prop = config.get(catagory, key, defaultValue);
		if (comment != "")
			prop.comment = comment;
		return prop.getString();
	}
	
	public String getString(String key, String defaultValue, String comment) {
		return getString(Configuration.CATEGORY_GENERAL, key, defaultValue, comment);
	}

	public String getString(String key, String defaultValue) {
		return getString(Configuration.CATEGORY_GENERAL, key, defaultValue, "");
	}
	
	private String getOreGenCfgLine(OreGenClass oreConfig) {
		if (oreConfig == null)
			return null;
		return Boolean.toString(oreConfig.enabled) + ","
				+ oreConfig.density + ","
				+ Boolean.toString(oreConfig.doRegen) + ","
				+ oreConfig.regenKey;
	}
	
	public OreGenClass getOreGen(OreGenClass oreConfig) {
		if (oreConfig != null) {
			Property prop = config.get(CAT_ORE_GEN, oreConfig.name, getOreGenCfgLine(oreConfig));
			String cfg[] = prop.getString().split(",");
			boolean configChanged = false;
			switch (configFileVersion) {
			case 1:
				oreConfig.enabled = Boolean.parseBoolean(cfg[0]);
				oreConfig.density = Integer.parseInt(cfg[1]);
				oreConfig.doRegen = Boolean.parseBoolean(cfg[2]);
				oreConfig.regenKey = cfg[3];
				break;
			default:
				switch (cfg.length) {
				case 3: // v0.1.4 - 0.4.23
					oreConfig.enabled = Boolean.parseBoolean(cfg[0]);
					oreConfig.density = Integer.parseInt(cfg[1]);
					oreConfig.doRegen = Boolean.parseBoolean(cfg[2]);
					break;
				case 4: // current
					oreConfig.enabled = Boolean.parseBoolean(cfg[0]);
					oreConfig.density = Integer.parseInt(cfg[1]);
					oreConfig.doRegen = Boolean.parseBoolean(cfg[2]);
					oreConfig.regenKey = cfg[3];
					break;
				case 8: // v0.1.1 - v0.1.3
					oreConfig.enabled = Boolean.parseBoolean(cfg[1]);
					oreConfig.doRegen = Boolean.parseBoolean(cfg[7]);
					configChanged = true;
					break;
				default:
					OresPlus.log.info("Could not read config for ore " + oreConfig.name + ". Resetting to default");
					configChanged = true;
				}
			}
			if (configChanged)
				prop.set(getOreGenCfgLine(oreConfig));
		}
		return oreConfig;
	}
	
	private String getOreCfgLine(OreClass ore) {
		if (ore == null)
			return null;
		return Boolean.toString(ore.enabled) + ","
				+ ore.source.name();
	}
	
	public OreClass getOre(OreClass ore) {
		if (ore != null) {
			Property prop = config.get(CAT_ORES, ore.name, getOreCfgLine(ore));
			String cfg[] = prop.getString().split(",");
			
			boolean configChanged = false;
			switch (cfg.length) {
			case 2:
				ore.enabled = Boolean.parseBoolean(cfg[0]);
				ore.source = OreSources.valueOf(cfg[1]);
				break;
			case 3: // v0.1.1 - v0.1.3
				ore.enabled = Boolean.parseBoolean(cfg[0]);
				configChanged = true;
				break;
			default:
				OresPlus.log.info("Could not read config for ore " + ore.name + ". Resetting to default");
				configChanged = true;
			}
			if (configChanged)
				prop.set(getOreCfgLine(ore));
		}
		return ore;
	}

} public enum Helpers {
	AppliedEnergistics(new AppEngHelper()),
	BuildCraft(new BCHelper()),
	Forestry(new ForestryHelper()),
	IC2(new IC2Helper()),
	Mekanism(new MekanismHelper()),
	RailCraft(new RCHelper()),
	ThaumCraft(new TCHelper()),
	ThermalExpansion(new TEHelper()),
	Tinkers(new TinkersHelper()),
	Treecapitator(new TreecapitatorHelper()), 
	BigReactors(new BigReactorsHelper()), 
	MineFactoryReloaded(new MFRHelper());
	
	private OresHelper _oresHelper;
	
	private Helpers(OresHelper oresHelper) {
		_oresHelper = oresHelper;
	}
	
	public void preInit() {
		this._oresHelper.preInit();
	}
	
	public void init() {
		this._oresHelper.init();
	}
	
	public void postInit() {
		this._oresHelper.postInit();
	}
	
	public boolean isLoaded() {
		return this._oresHelper.isLoaded();
	}
	
	public void generate(World world, Random rand, int chunkX, int chunkZ) {
		this._oresHelper.generate(world, rand, chunkX, chunkZ);
	}
	
	public Block getBlock(String blockName) {
		return this._oresHelper.getBlock(blockName);
	}
	
	public Item getItem(String itemName) {
		return this._oresHelper.getItem(itemName);
	}
	
	public void registerRecipe(RecipeType recipeType, ItemStack input, ItemStack... outputs) {
		this.registerRecipe(recipeType, input, null, outputs);
	}
	
	public void registerRecipe(RecipeType recipeType, ItemStack input, NBTTagCompound metadata, ItemStack... outputs) {
		this._oresHelper.registerRecipe(recipeType, input, metadata, outputs);
	}

	public void registerGasRecipe(RecipeType recipeType,
			Object input, NBTTagCompound metadata, Object output) {
		this.registerGasRecipe(recipeType, input, metadata, output, null);
	}
	
	public void registerGasRecipe(RecipeType recipeType,
			Object input, NBTTagCompound metadata, Object output, Object secondaryOutput) {
		// TODO Auto-generated method stub
		this._oresHelper.registerGasRecipe(recipeType, input, metadata, output, secondaryOutput);
	}
	
} public class ItemCore extends Item{
	protected int itemId;
	
	public ItemCore(String itemName) {
		super();
		setUnlocalizedName(itemName);
		setTextureName(References.MOD_ID + ":" + itemName);
		Ores.manager.registerOreItem(itemName, this);
		GameRegistry.registerItem(this, itemName);
		OreDictionary.registerOre(itemName, this);
	}

	public ItemCore addOreDictionaryName(String name) {
		OreDictionary.registerOre(name, this);
		return this;
	}    
} public class OreItemStack implements IRecipeInput {
	public ItemStack source;
	
	public OreItemStack(ItemStack input) {
		if (input == null) {
			this.source = null;
		}
		else {
			this.source = input.copy();
			this.source.stackSize = 1;
		}
	}
	
	public OreItemStack(Item input) {
		this(new ItemStack(input, 1));
	}
	
	public OreItemStack(Item input, int damageValue) {
		this(new ItemStack(input, 1, damageValue));
	}
	
	public OreItemStack(Block input) {
		this(new ItemStack(input, 1));
	}
	
	public OreItemStack(Block input, int meta) {
		this(new ItemStack(input, 1, meta));
	}
	
	@Override
	public boolean matches(ItemStack subject) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public int getAmount() {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public List<ItemStack> getInputs() {
		// TODO Auto-generated method stub
		return null;
	}

	public ItemStack newStack() {
		return this.newStack(1);
	}
	
	public ItemStack newStack(int size) {
		if (this.source == null) {
			return null;
		}
		this.source.stackSize = size;
		return this.source.copy();
	}
	
	public Block getSourceBlock() {
		return Block.getBlockFromItem(this.getSourceItem());
	}
	
	public Item getSourceItem() {
		return this.source.getItem();
	}
	
} public class RecipeManager {
	private static ICraftingHandler neiHandler;
	private static ICraftingHandler tmiHandler;
	
	private static ArrayList<ItemStack> maceratorBlackList = new ArrayList();
	private static ArrayList<ItemStack> grinderBlackList = new ArrayList();
	private static ArrayList<ItemStack> rockCrusherBlackList = new ArrayList();
	private static ArrayList<ItemStack> pulverizerBlackList = new ArrayList();
	
	public static void init() {
		Ores.grinderRecipes = new GrinderRecipeManager();
		Ores.crackerRecipes = new CrackerRecipeManager();
		
		neiHandler = new NeiHandler();
		tmiHandler = new TmiHandler();
	}
	
	public static void initRecipes() {
		maceratorBlackList.add(new ItemStack(net.minecraft.init.Items.coal, 1, 0));
		maceratorBlackList.add(new ItemStack(net.minecraft.init.Items.coal, 1, 1));

		OreItemStack bucketEmpty = new OreItemStack(net.minecraft.init.Items.bucket);
		OreItemStack coal = new OreItemStack(net.minecraft.init.Items.coal, 0);
		OreItemStack charcoal = new OreItemStack(net.minecraft.init.Items.coal, 1);
		OreItemStack chest = new OreItemStack(net.minecraft.init.Blocks.chest);
	    OreItemStack fertalizer = new OreItemStack(Helpers.Forestry.getItem("fertilizerCompound"));
		OreItemStack flint = new OreItemStack(net.minecraft.init.Items.flint);
		OreItemStack furnace = new OreItemStack(net.minecraft.init.Blocks.furnace);
		OreItemStack glass = new OreItemStack(net.minecraft.init.Blocks.glass);
		OreItemStack glassPane = new OreItemStack(net.minecraft.init.Blocks.glass_pane);
		OreItemStack gunpowder = new OreItemStack(net.minecraft.init.Items.gunpowder);
		OreItemStack oreCoal = new OreItemStack(net.minecraft.init.Blocks.coal_ore);
		OreItemStack oreLapis = new OreItemStack(net.minecraft.init.Blocks.lapis_ore);
		OreItemStack piston = new OreItemStack(net.minecraft.init.Blocks.piston);
	    OreItemStack stick = new OreItemStack(net.minecraft.init.Items.stick);
	    OreItemStack stone = new OreItemStack(net.minecraft.init.Blocks.stone);
		OreItemStack tank = new OreItemStack(Helpers.BuildCraft.getBlock("tankBlock"));
		
		if (OresPlus.config.enableMachines) {
			// machine casing
			addShapedRecipe(Items.machineCasing.item.newStack(), "sss", "sis", "sss", 's', stone.newStack(), 'i', MetallicOres.Iron.ingot.newStack());
			// cracker recipe
			if (Helpers.BuildCraft.isLoaded() && tank.source.getItem() != null) {
				addShapedRecipe(BlockManager.cracker.newStack(), "t", "c", "F", 't', tank.newStack(), 'c', Items.machineCasing.item.newStack(), 'F', furnace.newStack());
			}
			else
				addShapedRecipe(BlockManager.cracker.newStack(), "ggg", "gcg", "gFg", 'g', glass.newStack(), 'c', Items.machineCasing.item.newStack(), 'F', furnace.newStack());
			
			// grinder recipe
			addShapedRecipe(BlockManager.grinder.newStack(), "fff", "fcf", "fFf", 'f', flint.newStack(), 'c', Items.machineCasing.item.newStack(), 'F', furnace.newStack());
			
			// centrifuge recipe
			// addShapedRecipe(Blocks.centrifuge.newStack(), "ipi" "ici", "iFi", 'i', MetallicOres.Iron.ingot.newStack(), 'p', glassPane.newStack(), 'c', Items.machineCasing.newStack(), 'F', furnace.newStack());
		}
		
		//gunpowder recipe
		addShapelessRecipe(gunpowder.newStack(4), "dustSaltpeter", "dustSaltpeter", "dustCoal", "dustSulfur");
		
		// misc grinder recipes
		addGrinderRecipe(charcoal.newStack(), Items.dustCharcoal.item.newStack());
		addGrinderRecipe(coal.newStack(), Items.dustCoal.item.newStack());
		addGrinderRecipe(GeneralOres.Uranium.ore.newStack(), Items.crushedUranium.item.newStack(2));
		
		// alloy recipes
		int amount = 4;
		if (OresPlus.difficultAlloys) {
			amount = 2;
		}
		addShapelessRecipe(MetallicOres.Brass.dust.newStack(amount), "dustCopper", "dustCopper", "dustCopper", "dustZinc");
		addShapelessRecipe(MetallicOres.Bronze.dust.newStack(amount), "dustCopper", "dustCopper", "dustCopper", "dustTin");
		addShapelessRecipe(MetallicOres.Electrum.dust.newStack(2), "dustGold", "dustSilver");
		
		// misc nether ore smelting recipes
		addSmelting(GeneralOres.NetherCoal.ore.newStack(), oreCoal.newStack(2), 0.0F);
		addSmelting(GeneralOres.NetherLapis.ore.newStack(), oreLapis.newStack(2), 0.0F);
		addSmelting(GeneralOres.NetherUranium.ore.newStack(), GeneralOres.Uranium.ore.newStack(2), 0.0F);
		
	    if (Helpers.Forestry.isLoaded()) {
	        addShapelessRecipe(fertalizer.newStack(8), new Object[] { DustyOres.Saltpeter.dustName, DustyOres.Phosphorite.dustName, DustyOres.Potash.dustName });
	        addShapelessRecipe(fertalizer.newStack(12), new Object[] { DustyOres.Saltpeter.dustName, DustyOres.Saltpeter.dustName, DustyOres.Phosphorite.dustName, DustyOres.Potash.dustName });
	        addShapelessRecipe(fertalizer.newStack(12), new Object[] { DustyOres.Saltpeter.dustName, DustyOres.Phosphorite.dustName, DustyOres.Phosphorite.dustName, DustyOres.Potash.dustName });
	        addShapelessRecipe(fertalizer.newStack(12), new Object[] { DustyOres.Saltpeter.dustName, DustyOres.Phosphorite.dustName, DustyOres.Potash.dustName, DustyOres.Potash.dustName });
	        addShapelessRecipe(fertalizer.newStack(12), new Object[] { DustyOres.Saltpeter.dustName, DustyOres.Phosphorite.dustName, DustyOres.Potash.dustName, Items.dustMagnesium.item.newStack() });
	        addShapelessRecipe(fertalizer.newStack(16), new Object[] { DustyOres.Saltpeter.dustName, DustyOres.Saltpeter.dustName, DustyOres.Phosphorite.dustName, DustyOres.Potash.dustName, Items.dustMagnesium.item.newStack() });
	        addShapelessRecipe(fertalizer.newStack(16), new Object[] { DustyOres.Saltpeter.dustName, DustyOres.Phosphorite.dustName, DustyOres.Phosphorite.dustName, DustyOres.Potash.dustName, Items.dustMagnesium.item.newStack() });
	        addShapelessRecipe(fertalizer.newStack(16), new Object[] { DustyOres.Saltpeter.dustName, DustyOres.Phosphorite.dustName, DustyOres.Potash.dustName, DustyOres.Potash.dustName, Items.dustMagnesium.item.newStack() });
	      }

	    addShapedRecipe(ItemManager.armorAdamantineHelmet.newStack(), "aaa", "a a", 'a', MetallicOres.Adamantine.ingotName );
	    addShapedRecipe(ItemManager.armorAdamantineChestplate.newStack(), "a a", "aaa", "aaa", 'a', MetallicOres.Adamantine.ingotName );
	    addShapedRecipe(ItemManager.armorAdamantineLeggings.newStack(), "aaa", "a a", "a a", 'a', MetallicOres.Adamantine.ingotName );
	    addShapedRecipe(ItemManager.armorAdamantineBoots.newStack(), "a a", "a a", 'a', MetallicOres.Adamantine.ingotName );
	    addShapedRecipe(ItemManager.armorColdironHelmet.newStack(), "ccc", "c c", 'c', MetallicOres.Coldiron.ingotName );
	    addShapedRecipe(ItemManager.armorColdironChestplate.newStack(), "c c", "ccc", "ccc", 'c', MetallicOres.Coldiron.ingotName );
	    addShapedRecipe(ItemManager.armorColdironLeggings.newStack(), "ccc", "c c", "c c", 'c', MetallicOres.Coldiron.ingotName );
	    addShapedRecipe(ItemManager.armorColdironBoots.newStack(), "c c", "c c", 'c', MetallicOres.Coldiron.ingotName );
	    addShapedRecipe(ItemManager.armorMithralHelmet.newStack(), "mmm", "m m", 'm', MetallicOres.Mithril.ingotName );
	    addShapedRecipe(ItemManager.armorMithralChestplate.newStack(), "m m", "mmm", "mmm", 'm', MetallicOres.Mithril.ingotName );
	    addShapedRecipe(ItemManager.armorMithralLeggings.newStack(), "mmm", "m m", "m m", 'm', MetallicOres.Mithril.ingotName );
	    addShapedRecipe(ItemManager.armorMithralBoots.newStack(), "m m", "m m", 'm', MetallicOres.Mithril.ingotName );

	    addShapedRecipe(ItemManager.toolAdamantineAxe.newStack(), " aa", " sa", " s ", 'a', MetallicOres.Adamantine.ingotName, 's', stick.newStack() );
	    addShapedRecipe(ItemManager.toolAdamantineHoe.newStack(), " aa", " s ", " s ", 'a', MetallicOres.Adamantine.ingotName, 's', stick.newStack() );
	    addShapedRecipe(ItemManager.toolAdamantinePickaxe.newStack(), "aaa", " s ", " s ", 'a', MetallicOres.Adamantine.ingotName, 's', stick.newStack() );
	    addShapedRecipe(ItemManager.toolAdamantineSpade.newStack(), "a", "s", "s", 'a', MetallicOres.Adamantine.ingotName, 's', stick.newStack());
	    addShapedRecipe(ItemManager.toolAdamantineSword.newStack(), "a", "a", "s", 'a', MetallicOres.Adamantine.ingotName, 's', stick.newStack() );
	    addShapedRecipe(ItemManager.toolColdironAxe.newStack(), " cc", " sc", " s ", 'c', MetallicOres.Coldiron.ingotName, 's', stick.newStack() );
	    addShapedRecipe(ItemManager.toolColdironHoe.newStack(), " cc", " s ", " s ", 'c', MetallicOres.Coldiron.ingotName, 's', stick.newStack() );
	    addShapedRecipe(ItemManager.toolColdironPickaxe.newStack(), "ccc", " s ", " s ", 'c', MetallicOres.Coldiron.ingotName, 's', stick.newStack() );
	    addShapedRecipe(ItemManager.toolColdironSpade.newStack(), "c", "s", "s", 'c', MetallicOres.Coldiron.ingotName, 's', stick.newStack());
	    addShapedRecipe(ItemManager.toolColdironSword.newStack(), "c", "c", "s", 'c', MetallicOres.Coldiron.ingotName, 's', stick.newStack() );
	    addShapedRecipe(ItemManager.toolMithralAxe.newStack(), " mm", " sm", " s ", 'm', MetallicOres.Mithril.ingotName, 's', stick.newStack() );
	    addShapedRecipe(ItemManager.toolMithralHoe.newStack(), " mm", " s ", " s ", 'm', MetallicOres.Mithril.ingotName, 's', stick.newStack() );
	    addShapedRecipe(ItemManager.toolMithralPickaxe.newStack(), "mmm", " s ", " s ", 'm', MetallicOres.Mithril.ingotName, 's', stick.newStack() );
	    addShapedRecipe(ItemManager.toolMithralSpade.newStack(), "m", "s", "s", 'm', MetallicOres.Mithril.ingotName, 's', stick.newStack());
	    addShapedRecipe(ItemManager.toolMithralSword.newStack(), "m", "m", "s", 'm', MetallicOres.Mithril.ingotName, 's', stick.newStack() );
	    
	    //Misc Dust Recipes
	    addShapedRecipe(Items.dustTitanium.item.newStack(), "ttt", "ttt", "ttt", 't', Items.dustTinyTitanium.item.newStack());
	    addShapedRecipe(Items.dustTungsten.item.newStack(), "ttt", "ttt", "ttt", 't', Items.dustTinyTungsten.item.newStack());
	    addShapelessRecipe(Items.dustTinyTitanium.item.newStack(9), Items.dustTitanium.item.newStack());
	    addShapelessRecipe(Items.dustTinyTungsten.item.newStack(9), Items.dustTungsten.item.newStack());
	    
		for (MetallicOres ore : MetallicOres.values()) {
			ore.registerRecipes();
		}
		
		for (GemstoneOres ore : GemstoneOres.values()) {
			ore.registerRecipes();
		}
		
		for (DustyOres ore : DustyOres.values()) {
			ore.registerRecipes();
		}
		
		for (MineralOres ore : MineralOres.values()) {
			ore.registerRecipes();
		}
		
		for (GeneralOres ore : GeneralOres.values()) {
			ore.registerRecipes();
		}
		
		for (Items item : Items.values()) {
			item.registerRecipes();
		}
		
		if (OresPlus.iridiumPlateRecipe && Helpers.IC2.isLoaded()) {
			OreItemStack iridiumPlate = new OreItemStack(Helpers.IC2.getItem("itemPartIridium"));
			OreItemStack alloyPlate = new OreItemStack(Helpers.IC2.getItem("itemPartAlloy"));
			addShapedRecipe(iridiumPlate.newStack(), "ipi", "pdp", "ipi", 'i', Items.gemIridium.toString(), 'p', alloyPlate.newStack(), 'd', GemstoneOres.Diamond.gem.newStack());
		}
		
		if (Helpers.BigReactors.isLoaded()) {
			OreItemStack graphiteBar = new OreItemStack(Helpers.BigReactors.getItem("BRIngot"), 2);
			OreItemStack fuelRod = new OreItemStack(Helpers.BigReactors.getItem("YelloriumFuelRod"));
			OreItemStack reactorCasing = new OreItemStack(Helpers.BigReactors.getBlock("BRReactorPart"));
			OreItemStack reactorController = new OreItemStack(Helpers.BigReactors.getBlock("BRReactorPart"), 1);
			OreItemStack reactorControlRod = new OreItemStack(Helpers.BigReactors.getBlock("BRReactorPart"), 2);
			OreItemStack reactorPowerTap = new OreItemStack(Helpers.BigReactors.getBlock("BRReactorPart"), 3);
			OreItemStack reactorAccessPort = new OreItemStack(Helpers.BigReactors.getBlock("BRReactorPart"), 4);
			OreItemStack reactorCoolantPort = new OreItemStack(Helpers.BigReactors.getBlock("BRReactorPart"), 5);
			OreItemStack reactorRedNetPort = new OreItemStack(Helpers.BigReactors.getBlock("BRReactorPart"), 6);
			//OreItemStack reactorComputerPort = new OreItemStack(Helpers.BigReactors.getBlock("BRReactorPart"), 7);
			OreItemStack reactorRedstonePort = new OreItemStack(Helpers.BigReactors.getBlock("BRReactorRedstonePort"));
			
			addShapedRecipe(fuelRod.newStack(), "igi", "iyi", "igi", 'i', MetallicOres.Iron.ingot.newStack(), 'g', graphiteBar.newStack(), 'y', MetallicOres.Yellorium.ingot.newStack());
			addShapedRecipe(reactorCasing.newStack(4), "igi", "gyg", "igi", 'i', MetallicOres.Iron.ingot.newStack(), 'g', graphiteBar.newStack(), 'y', MetallicOres.Yellorium.ingot.newStack());
			addShapedRecipe(reactorController.newStack(), "R R", "ydy", "RrR", 'R', reactorCasing.newStack(), 'y', MetallicOres.Yellorium.ingot.newStack(), 'd', GemstoneOres.Diamond.gem.newStack(), 'r', DustyOres.Redstone.dust.newStack());
			addShapedRecipe(reactorControlRod.newStack(), "RgR", "grg", "RyR", 'R', reactorCasing.newStack(), 'g', graphiteBar.newStack(), 'r', DustyOres.Redstone.dust.newStack(), 'y', MetallicOres.Yellorium.ingot.newStack());
			addShapedRecipe(reactorPowerTap.newStack(), "RrR", "r r", "RrR", 'R', reactorCasing.newStack(), 'r', DustyOres.Redstone.dust.newStack());
			addShapedRecipe(reactorAccessPort.newStack(), "R R", " c ", "RpR", 'R', reactorCasing.newStack(), 'c', chest.newStack(), 'p', piston.newStack());
			addShapedRecipe(reactorCoolantPort.newStack(), "R R", "ibi", "RpR", 'R', reactorCasing.newStack(), 'i', MetallicOres.Iron.ingot.newStack(), 'b', bucketEmpty.newStack(), 'p', piston.newStack());
			if (Helpers.MineFactoryReloaded.isLoaded()) {
				OreItemStack redNetCable = new OreItemStack(Helpers.MineFactoryReloaded.getItem("tile.mfr.cable.redstone"));
				addShapedRecipe(reactorRedNetPort.newStack(), "RrR", "rgr", "RrR", 'R', reactorCasing.newStack(), 'r', redNetCable.newStack(), 'g', MetallicOres.Gold.ingot.newStack());
			}
			//addShapedRecipe(reactorComputerPort.newStack());
			addShapedRecipe(reactorRedstonePort.newStack(), "RrR", "rgr", "RrR", 'R', reactorCasing.newStack(), 'r', DustyOres.Redstone.dust.newStack(), 'g', MetallicOres.Gold.ingot.newStack());
		}
		
		if (Helpers.AppliedEnergistics.isLoaded()) {
			OreItemStack certusQuartzDust = new OreItemStack(Helpers.AppliedEnergistics.getItem("item.ItemMultiMaterial"), 2);
			Helpers.IC2.registerRecipe(RecipeType.Macerator, GeneralOres.CertusQuartz.ore.newStack(), certusQuartzDust.newStack(2));
		}
		
		if (Helpers.Mekanism.isLoaded()) {
			Helpers.Mekanism.registerGasRecipe(RecipeType.ElectrolyticSeperator, new FluidStack(Fluids.CopperSulfide.fluid, 2), null, new GasStack(MetallicOres.Copper.cleanSlurry, 1), new GasStack(Gases.sulfurDioxideGas.gas, 1));
			Helpers.Mekanism.registerGasRecipe(RecipeType.ChemicalCrystalizer, new GasStack(Gases.slurryCleanAntimony.gas, 200), null, Items.crystalAntimony.item.newStack());
			Helpers.Mekanism.registerGasRecipe(RecipeType.ChemicalCrystalizer, new GasStack(Gases.slurryCleanTitanium.gas, 200), null, Items.crystalTitanium.item.newStack());
			Helpers.Mekanism.registerRecipe(RecipeType.ChemicalInjector, Items.crystalAntimony.item.newStack(), Items.shardAntimony.item.newStack());
			Helpers.Mekanism.registerRecipe(RecipeType.ChemicalInjector, Items.crystalTitanium.item.newStack(), Items.shardTitanium.item.newStack());
			Helpers.Mekanism.registerRecipe(RecipeType.PurificationChamber, Items.shardAntimony.item.newStack(), Items.clumpAntimony.item.newStack());
			Helpers.Mekanism.registerRecipe(RecipeType.PurificationChamber, Items.shardTitanium.item.newStack(), Items.clumpTitanium.item.newStack());
			Helpers.Mekanism.registerRecipe(RecipeType.Crusher, Items.clumpAntimony.item.newStack(), Items.dustDirtyAntimony.item.newStack());
			Helpers.Mekanism.registerRecipe(RecipeType.Crusher, Items.clumpTitanium.item.newStack(), Items.dustDirtyTitanium.item.newStack());
			Helpers.Mekanism.registerRecipe(RecipeType.EnrichmentChamber, Items.dustDirtyAntimony.item.newStack(), Items.dustAntimony.item.newStack());
			Helpers.Mekanism.registerRecipe(RecipeType.EnrichmentChamber, Items.dustDirtyTitanium.item.newStack(), Items.dustTitanium.item.newStack());
		}
	}

	public static void addShapelessRecipe(ItemStack output, Object... params) {
		CraftingManager.getInstance().getRecipeList().add(new ShapelessOreRecipe(output, params));
	}

	public static void addSmelting(ItemStack input, ItemStack output, Float xp) {
		GameRegistry.addSmelting(input, output, xp);	
	}
	
	public static void addShapedRecipe (ItemStack output, Object... params) {
		CraftingManager.getInstance().getRecipeList().add(new ShapedOreRecipe(output, params));
	}

	public static void addGrinderRecipe(ItemStack input, ItemStack output) {
		// Register Grinder Recipe
		Ores.grinderRecipes.add(input, output);
		OresPlus.log.debug("Registered OresPlus:Grinder recipe for " + input.getUnlocalizedName());
		// Register IC2 Macerator Recipe
		if (Helpers.IC2.isLoaded() && !isBlackListed(maceratorBlackList, input)) {
			Helpers.IC2.registerRecipe(RecipeType.Macerator, input, output);
			OresPlus.log.debug("Registered IC2:Macerator recipe for " + input.getUnlocalizedName());
		}
		// Register AE2 Grinder Recipe
		if (Helpers.AppliedEnergistics.isLoaded() && !isBlackListed(grinderBlackList, input)) {
			NBTTagCompound metadata = new NBTTagCompound();
			metadata.setInteger("cranks", 8);
			Helpers.AppliedEnergistics.registerRecipe(RecipeType.Grinder, input, metadata, output);
			OresPlus.log.debug("Registered AE2:Grinder recipe for " + input.getUnlocalizedName());
		}
		// Register Railcraft Rock Crusher Recipe
		if (Helpers.RailCraft.isLoaded() && !isBlackListed(rockCrusherBlackList , input)) {
			Helpers.RailCraft.registerRecipe(RecipeType.RockCrusher, input, output);
			OresPlus.log.debug("Registered RailCraft:RockCrusher recipe for " + input.getUnlocalizedName());
		}
	}
	
	public static void addUUMatterRecipe(ItemStack source, double cost) {
		if (Helpers.IC2.isLoaded() && source != null && cost != 0.0) {
			NBTTagCompound metadata = new NBTTagCompound();
			metadata.setDouble("cost", cost);
			Helpers.IC2.registerRecipe(RecipeType.Scanner, source, metadata, new ItemStack[] {});
		}
	}

	private static boolean isBlackListed(
			ArrayList<ItemStack> blacklist, ItemStack input) {
		for (ItemStack item : blacklist) {
			if (item.isItemEqual(input)) {
				return true;
			}
		}
		return false;
	}

	public static void replaceRecipeResults() {
		// replace smelting results
		OresPlus.log.info("Replacing smelting results");
		Collection smeltingResultsList = FurnaceRecipes.smelting().getSmeltingList().values();
		for (Object result : smeltingResultsList.toArray()) {
			if (result instanceof ItemStack) {
				Item item = ((ItemStack)result).getItem();
				UniqueIdentifier itemUid = GameRegistry.findUniqueIdentifierFor(item);
				if (itemUid.modId != "minecraft" && itemUid.modId != References.MOD_ID) {
					OresPlus.log.info("Recipe Result " + itemUid.modId + ":" + itemUid.name);
					ItemStack newItem = new ItemStack(Ores.manager.getOreItem(itemUid.name), ((ItemStack)result).stackSize);
					if (newItem != null) {
						((ItemStack)result).func_150996_a(newItem.getItem());
						net.minecraft.init.Items.apple.setDamage(newItem, net.minecraft.init.Items.apple.getDamage(((ItemStack)result)));
					}
				}
			}
		}
	}
	
	public static void hideItem(OreItemStack item) {
		neiHandler.hideItem(item.source);
		tmiHandler.hideItem(item.source);
	}
	
	public static void addCrucibleRecipe(String key, ItemStack result, String catalyst, AspectList tags) {
		if (Helpers.ThaumCraft.isLoaded()) {
			ThaumcraftApi.addCrucibleRecipe(key, result, catalyst, tags);
		}
	}
	
} public enum RecipeType {
	Centrifuge,
	ChemicalCrystalizer,
	ChemicalDissolver, 
	ChemicalInjector, 
	ChemicalWasher, 
	Crusher, 
	ElectrolyticSeperator,
	EnrichmentChamber, 
	Grinder,
	Macerator,
	OreWasher,
	PurificationChamber, 
	RockCrusher,
	Scanner;
}
import java.util.Random;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.item.ItemStack;
import net.minecraftforge.oredict.OreDictionary;
import scala.reflect.internal.Trees.This;
import thaumcraft.api.ThaumcraftApi;
import thaumcraft.api.aspects.Aspect;
import thaumcraft.api.aspects.AspectList;
import tw.oresplus.OresPlus;
import tw.oresplus.api.Ores;
import tw.oresplus.blocks.BlockCore;
import tw.oresplus.blocks.BlockOre;
import tw.oresplus.core.config.ConfigCore;
import tw.oresplus.core.helpers.Helpers;
import tw.oresplus.items.ItemCore;
import tw.oresplus.recipes.OreItemStack;
import tw.oresplus.recipes.RecipeManager;
import tw.oresplus.recipes.RecipeType;
package tw.oresplus.ores;



public enum GemstoneOres implements IOreList {
		Amethyst (2, OreDrops.AMETHYST, 1500.0D),
		Apatite (1, OreDrops.APATITE, 800.0D),
		Diamond (2, 3, 7, OreDrops.DIAMOND, 0.0D), // 5050.057512219353
		Emerald (2, 3, 7, OreDrops.EMERALD, 0.0D), // 55643.450773700926
		Malachite (2, 3, 7, OreDrops.MALACHITE, 3000.0D),
		Peridot (2, 3, 7, OreDrops.PERIDOT, 3000.0D),
		Ruby (2, 3, 7, OreDrops.RUBY, 3000.0D),
		Sapphire (2, 3, 7, OreDrops.SAPPHIRE, 3000.0D),
		Tanzanite(2, 3, 7, OreDrops.TANZANITE, 3000.0D),
		Topaz (2, 3, 7, OreDrops.TOPAZ, 3000.0D)
	;
		
	public String oreName;
	public String netherOreName;
	public String blockName;
	public String gemName;
	
	public OreItemStack ore;
	public OreItemStack netherOre;
	public OreItemStack block;
	public OreItemStack gem;
	
	public boolean enabled;
	
	private int _harvestLevel;
	private int _xpLow;
	private int _xpHigh;
	private OreDrops _drops;
	private OreSources _source;
	private double _uuCost;
	
	GemstoneOres (int harvestLevel, int xpLow, int xpHigh, OreDrops drops, double uuCost) {
		this.oreName = "ore" + this.toString();
		this.netherOreName = "oreNether" + this.toString();
		this.blockName = "block" + this.toString();
		this.gemName = "gem" + this.toString();
		
		this.enabled = true;
		
		this._harvestLevel = harvestLevel;
		this._xpLow = xpLow;
		this._xpHigh = xpHigh;
		this._drops = drops;
		this._source = OreSources.DEFAULT;
		this._uuCost = uuCost;
	}
	
	GemstoneOres (int harvestLevel, OreDrops drops, double uuCost) {
		this(harvestLevel, 0, 0, drops, uuCost);
	}
	
	private boolean isVanilla() {
		return (this == Diamond || this == Emerald);
	}

	@Override
	public OreClass getDefaultConfig() {
		return new OreClass(this.oreName, this.enabled, this._harvestLevel, this._xpLow, 
				this._xpHigh, this._drops, this._source);
	}

	@Override
	public OreClass getDefaultConfigNether() {
		return new OreClass(this.netherOreName, this.enabled, this._harvestLevel, this._xpLow,
				this._xpHigh, OreDrops.ORE, this._source);
	}

	@Override
	public void registerBlocks() {
		//Register Ore
		if (!this.isVanilla()) {
			this.ore = new OreItemStack(new BlockOre(this.getDefaultConfig()));
		}
		else if (this == Diamond) {
			this.ore = new OreItemStack(net.minecraft.init.Blocks.diamond_ore);
			Ores.manager.registerOre(this.oreName, net.minecraft.init.Blocks.diamond_ore);
		}
		else if (this == Emerald) {
			this.ore = new OreItemStack(net.minecraft.init.Blocks.emerald_ore);
			Ores.manager.registerOre(this.oreName, net.minecraft.init.Blocks.emerald_ore);
		}
		
		// Register Nether Ore
		this.netherOre = new OreItemStack(new BlockOre(this.getDefaultConfigNether(), true));
		
		// Register Storage Block
		if (!this.isVanilla()) {
			this.block = new OreItemStack(new BlockCore(Material.iron, this.blockName)
				.setCreativeTab(CreativeTabs.tabBlock)
				.setHardness(5.0F)
				.setResistance(10.0F)
			.	setStepSound(Block.soundTypeMetal));
			OreDictionary.registerOre(this.blockName, this.block.source);
		}
		else if (this == Diamond) {
			this.block = new OreItemStack(net.minecraft.init.Blocks.diamond_block);
		}
		else if (this == Emerald) {
			this.block = new OreItemStack(net.minecraft.init.Blocks.emerald_block);
		}
		
	}

	@Override
	public void registerItems() {
		// Register Gem
		if (!this.isVanilla()) {
			this.gem = new OreItemStack(new ItemCore(this.gemName).setCreativeTab(CreativeTabs.tabMaterials));
		}
		else if (this == Diamond) {
			this.gem = new OreItemStack(net.minecraft.init.Items.diamond);
		}
		else if (this == Emerald) {
			this.gem = new OreItemStack(net.minecraft.init.Items.emerald);
		}
	}

	@Override
	public void registerRecipes() {
		// register ore smelt & grinds
		switch (this) {
		case Diamond:
		case Emerald:
			RecipeManager.addGrinderRecipe(this.ore.newStack(), this.gem.newStack(2));