#include <Block/SpriteBlock/BasicBlock/LogBlock/LogBlock.hpp>
#include <Block/SpriteBlock/Mushroom/Mushroom.hpp>
#include <ItemRecipe/ItemRecipe.hpp>
#include <ItemRecipeRegistry/ItemRecipeRegistry.hpp>
#include <ItemRegistry/ItemRegistry.hpp>
#include <BlockRegistry/BlockRegistry.hpp>
#include <ItemStack/ItemStack.hpp>
#include <Milestone/BrickByBrickMilestone/BrickByBrickMilestone.hpp>
#include <Milestone/ElectronicsMilestone/ElectronicsMilestone.hpp>
#include <Milestone/HydraulicsMilestone/HydraulicsMilestone.hpp>
#include <Milestone/MetalWorkingMilestone/MetalWorkingMilestone.hpp>
#include <Milestone/NavigationMilestone/NavigationMilestone.hpp>
#include <Milestone/StoneAgeMilestone/StoneAgeMilestone.hpp>
#include <Milestone/CarpentryMilestone/CarpentryMilestone.hpp>
#include <Milestone/BronzeAgeMilestone/BronzeAgeMilestone.hpp>

ItemRecipeRegistry itemRecipeRegistry;

void ItemRecipeRegistry::shutdown()
{
    for (auto& recipe : recipes)
    {
        delete recipe;
    }
}

void ItemRecipeRegistry::registerAllRecipes()
{
    // Planks can be made out of any logs.
    blockRegistry.forEachBlock(
        [this](Block &block)
        {
            if (!block.isPrefixed("cactus") && isInstance<LogBlock>(block))
            {
                registerRecipe(
                    ItemRecipe::Level::Manual,
                    blockRegistry.getBlockByName("main:planks").toItemStack(4),

                    block.toItemStack()
                );
            }
        }
    );

    registerRecipe(
        ItemRecipe::Level::Manual,
        ItemStack("main:chisel"),

        ItemStack("main:stick", 4),
        blockRegistry.getBlockByName("main:pebble").toItemStack(1)
    )
        ->withAchievement(StoneAgeMilestone::ID, StoneAgeMilestone::ConstructChisel);

    registerRecipe(
        ItemRecipe::Level::Manual,
        ItemStack("main:stick", 8),

        blockRegistry.getBlockByName("main:planks").toItemStack(1)
    );

    registerRecipe(
        ItemRecipe::Level::Manual,
        blockRegistry.getBlockByName("main:woodenPole").toItemStack(4),

        blockRegistry.getBlockByName("main:planks").toItemStack(1)
    );

    registerRecipe(
        ItemRecipe::Level::Manual,
        ItemStack("main:basicAxe"),

        ItemStack("main:stick", 8),
        blockRegistry.getBlockByName("main:stone").toItemStack(1)
    )
        ->withAchievement(CarpentryMilestone::ID, CarpentryMilestone::BasicAxe);

    registerRecipe(
        ItemRecipe::Level::Manual,
        ItemStack("main:screwdriver"),

        ItemStack("main:stick", 3),
        ItemStack("main:ironIngot", 2)
    );

    registerRecipe(
        ItemRecipe::Level::Manual,
        blockRegistry.getBlockByName("main:torch").toItemStack(8),

        ItemStack("main:charcoal", 1),
        ItemStack("main:stick", 8)
    )
        ->withAchievement(CarpentryMilestone::ID, CarpentryMilestone::Torches);

    registerRecipe(
        ItemRecipe::Level::Manual,
        blockRegistry.getBlockByName("main:constructTable").toItemStack(1),

        blockRegistry.getBlockByName("main:planks").toItemStack(12)
    );

    registerRecipe(
        ItemRecipe::Level::Table,
        blockRegistry.getBlockByName("main:stoneEmerald").toItemStack(1),

        ItemStack("main:emerald", 2)
    );

    // Pillars can be made from their base material.
    blockRegistry.forEachBlock(
        [this](Block &block)
        {
            if (block.isSuffixed("Pillar"))
            {
                registerRecipe(
                    ItemRecipe::Level::Table,
                    block.toItemStack(1),

                    blockRegistry.getBlockByName(block.name.substr(0, block.name.size() - strlen("Pillar"))).toItemStack(1)
                );
            }
        }
    );

    registerRecipe(
        ItemRecipe::Level::Table,
        ItemStack("main:compass"),

        ItemStack("main:ironIngot", 1),
        ItemStack("main:nickelIngot", 1)
    );

    registerRecipe(
        ItemRecipe::Level::Table,
        ItemStack("main:carvedMap"),

        blockRegistry.getBlockByName("main:planks").toItemStack(1),
        ItemStack("main:stick", 8)
    );

    registerRecipe(
        ItemRecipe::Level::Table,
        ItemStack("main:paperMap"),

        ItemStack("main:paperSheet", 1),
        ItemStack("main:compass", 1),
        ItemStack("main:greenDye", 4)
    );

    registerRecipe(
        ItemRecipe::Level::Table,
        blockRegistry.getBlockByName("main:chain").toItemStack(4),

        ItemStack("main:ironIngot", 1)
    );

    registerRecipe(
        ItemRecipe::Level::Table,
        ItemStack("main:tinCopperBundle"),

        ItemStack("main:tinIngot"),
        ItemStack("main:copperIngot")
    );

    registerRecipe(
        ItemRecipe::Level::Table,
        ItemStack("main:brassCopperBundle"),

        ItemStack("main:brassIngot"),
        ItemStack("main:copperIngot")
    );

    registerRecipe(
        ItemRecipe::Level::Table,
        ItemStack("main:stoneAxe"),

        blockRegistry.getBlockByName("main:stone").toItemStack(3),
        ItemStack("main:stick", 8)
    )
        ->withAchievement(CarpentryMilestone::ID, CarpentryMilestone::StoneAxe);

    registerRecipe(
        ItemRecipe::Level::Table,
        ItemStack("main:bronzeAxe"),

        ItemStack("main:bronzeIngot", 3),
        ItemStack("main:stick", 8)
    )
        ->withAchievement(CarpentryMilestone::ID, CarpentryMilestone::BronzeAxe);

    registerRecipe(
        ItemRecipe::Level::Table,
        ItemStack("main:ironAxe"),

        ItemStack("main:ironIngot", 3),
        ItemStack("main:stick", 8)
    )
        ->withAchievement(CarpentryMilestone::ID, CarpentryMilestone::IronAxe);

    registerRecipe(
        ItemRecipe::Level::Table,
        ItemStack("main:steelAxe"),

        ItemStack("main:steelIngot", 3),
        ItemStack("main:stick", 8)
    )
        ->withAchievement(CarpentryMilestone::ID, CarpentryMilestone::SteelAxe);

    registerRecipe(
        ItemRecipe::Level::Table,
        ItemStack("main:diamondAxe"),

        ItemStack("main:diamond", 3),
        ItemStack("main:stick", 8)
    );

    registerRecipe(
        ItemRecipe::Level::Table,
        blockRegistry.getBlockByName("main:crate").toItemStack(),

        blockRegistry.getBlockByName("main:planks").toItemStack(6),
        ItemStack("main:stick", 12)
    );

    registerRecipe(
        ItemRecipe::Level::Table,
        ItemStack("main:rake"),

        ItemStack("main:ironIngot", 8),
        ItemStack("main:stick", 8)
    );

    registerRecipe(
        ItemRecipe::Level::Table,
        ItemStack("main:stonePickaxe"),

        blockRegistry.getBlockByName("main:stone").toItemStack(3),
        ItemStack("main:stick", 8)
    )
        ->withAchievement(BronzeAgeMilestone::ID, BronzeAgeMilestone::StonePickaxe);

    registerRecipe(
        ItemRecipe::Level::Table,
        ItemStack("main:bronzePickaxe"),

        ItemStack("main:bronzeIngot", 3),
        ItemStack("main:stick", 8)
    )
        ->withAchievement(BronzeAgeMilestone::ID, BronzeAgeMilestone::BronzePickaxe);

    registerRecipe(
        ItemRecipe::Level::Table,
        ItemStack("main:ironPickaxe"),

        ItemStack("main:ironIngot", 3),
        ItemStack("main:stick", 8)
    )
        ->withAchievement(MetalWorkingMilestone::ID, MetalWorkingMilestone::IronPickaxe);

    registerRecipe(
        ItemRecipe::Level::Table,
        ItemStack("main:steelPickaxe"),

        ItemStack("main:steelIngot", 3),
        ItemStack("main:stick", 8)
    )
        ->withAchievement(MetalWorkingMilestone::ID, MetalWorkingMilestone::SteelPickaxe);

    registerRecipe(
        ItemRecipe::Level::Table,
        ItemStack("main:diamondPickaxe"),

        ItemStack("main:diamond", 3),
        ItemStack("main:stick", 8)
    );

    registerRecipe(
        ItemRecipe::Level::Table,
        blockRegistry.getBlockByName("main:stonePot").toItemStack(),

        blockRegistry.getBlockByName("main:stone").toItemStack(12),
        ItemStack("main:stick", 16)
    );

    registerRecipe(
        ItemRecipe::Level::Table,
        blockRegistry.getBlockByName("main:ironPot").toItemStack(),

        ItemStack("main:ironIngot", 12),
        ItemStack("main:stick", 16)
    );

    registerRecipe(
        ItemRecipe::Level::Table,
        ItemStack("main:capsuleSmallTin"),

        ItemStack("main:tinIngot", 8)
    )
        ->withAchievement(HydraulicsMilestone::ID, HydraulicsMilestone::MakeSmallTinCapsule);

    registerRecipe(
        ItemRecipe::Level::Table,
        ItemStack("main:capsuleLargeTin"),

        ItemStack("main:capsuleSmallTin"),
        ItemStack("main:tinIngot", 24)
    )
        ->withAchievement(HydraulicsMilestone::ID, HydraulicsMilestone::MakeLargeTinCapsule);

    // Leather can be made out of any mushrooms.
    blockRegistry.forEachBlock(
        [this](Block &block)
        {
            if (isInstance<Mushroom>(block))
            {
                registerRecipe(
                    ItemRecipe::Level::Manual,
                    ItemStack("main:mushroomLeather", 1),

                    block.toItemStack(16)
                );
            }
        }
    );

    registerRecipe(
        ItemRecipe::Level::Table,
        ItemStack("main:runningShoes", 1),

        ItemStack("main:mushroomLeather", 16),
        ItemStack("main:rubber", 4)
    );

    registerRecipe(
        ItemRecipe::Level::Table,
        ItemStack("main:backpack", 1),

        ItemStack("main:mushroomLeather", 16),
        ItemStack("main:rubber", 8)
    );

    registerRecipe(
        ItemRecipe::Level::EngTable,
        ItemStack("main:paperSheet", 1),

        ItemStack("main:woodPulp", 8)
    );

    registerRecipe(
        ItemRecipe::Level::EngTable,
        ItemStack("main:capsuleSmallLead"),

        ItemStack("main:capsuleSmallTin"),
        ItemStack("main:leadIngot", 8)
    );

    registerRecipe(
        ItemRecipe::Level::EngTable,
        ItemStack("main:capsuleLargeLead"),

        ItemStack("main:capsuleSmallLead"),
        ItemStack("main:leadIngot", 24)
    );

    registerRecipe(
        ItemRecipe::Level::EngTable,
        ItemStack("main:capsuleLargeLead"),

        ItemStack("main:capsuleLargeTin"),
        ItemStack("main:leadIngot", 32)
    );

    registerRecipe(
        ItemRecipe::Level::Table,
        blockRegistry.getBlockByName("main:woodenBarrel").toItemStack(),

        blockRegistry.getBlockByName("main:planks").toItemStack(12),
        ItemStack("main:ironIngot", 2)
    )
        ->withAchievement(HydraulicsMilestone::ID, HydraulicsMilestone::MakeWoodenBarrel);

    registerRecipe(
        ItemRecipe::Level::Table,
        blockRegistry.getBlockByName("main:dynamo").toItemStack(),

        ItemStack("main:tinIngot", 6),
        ItemStack("main:nickelIngot", 1),
        ItemStack("main:coil", 1)
    );

    registerRecipe(
        ItemRecipe::Level::Table,
        blockRegistry.getBlockByName("main:tinCasing").toItemStack(3),

        ItemStack("main:tinIngot", 3),
        ItemStack("main:ironIngot", 1)
    )
        ->withAchievement(ElectronicsMilestone::ID, ElectronicsMilestone::MakeTinCasing);

    registerRecipe(
        ItemRecipe::Level::Table,
        blockRegistry.getBlockByName("main:steelCasing").toItemStack(3),

        ItemStack("main:steelIngot", 3),
        ItemStack("main:nickelIngot", 1)
    );

    registerRecipe(
        ItemRecipe::Level::EngTable,
        blockRegistry.getBlockByName("main:leadCasing").toItemStack(3),

        ItemStack("main:leadIngot", 3),
        ItemStack("main:silverIngot", 1)
    );

    // Bricks can be made out of their corresponding block in 2:1 proportions.
    blockRegistry.forEachBlock(
        [this](Block &block)
        {
            if (block.isDyedBlock())
            {
                // Don't generate recipies for every single variant of dyed bricks.
                return;
            }

            auto bricksName = block.name + "Bricks";
            if (!blockRegistry.isValidBlock(bricksName))
            {
                // No bricks for this block.
                return;
            }

            registerRecipe(
                ItemRecipe::Level::Table,
                blockRegistry.getBlockByName(bricksName).toItemStack(2),

                block.toItemStack(1)
            );
        }
    );

    registerRecipe(
        ItemRecipe::Level::Table,
        blockRegistry.getBlockByName("main:grindingBowl").toItemStack(),

        blockRegistry.getBlockByName("main:clay").toItemStack(8),
        ItemStack("main:ironIngot", 3)
    );

    registerRecipe(
        ItemRecipe::Level::Table,
        blockRegistry.getBlockByName("main:fluidPipe").toItemStack(16),

        ItemStack("main:ironIngot", 3)
    )
        ->withAchievement(HydraulicsMilestone::ID, HydraulicsMilestone::MakeFluidPipe);

    registerRecipe(
        ItemRecipe::Level::Table,
        blockRegistry.getBlockByName("main:copperWire").toItemStack(16),

        ItemStack("main:copperIngot", 3)
    )
        ->withAchievement(ElectronicsMilestone::ID, ElectronicsMilestone::MakeCopperWire);

    registerRecipe(
        ItemRecipe::Level::Table,
        blockRegistry.getBlockByName("main:pump").toItemStack(),

        blockRegistry.getBlockByName("main:fluidPipe").toItemStack(1),
        ItemStack("main:tinIngot", 6),
        ItemStack("main:ironIngot", 2),
        blockRegistry.getBlockByName("main:copperWire").toItemStack(3)
    )
        ->withAchievement(HydraulicsMilestone::ID, HydraulicsMilestone::MakePump);

    registerRecipe(
        ItemRecipe::Level::EngTable,
        blockRegistry.getBlockByName("main:dataCable").toItemStack(2),

        ItemStack("main:rubber", 1),
        blockRegistry.getBlockByName("main:copperWire").toItemStack(4)
    );

    registerRecipe(
        ItemRecipe::Level::Manual,
        ItemStack("main:brick", 4),

        blockRegistry.getBlockByName("main:clay").toItemStack()
    )
        ->withAchievement(BrickByBrickMilestone::ID, BrickByBrickMilestone::MakeBrickItem);

    registerRecipe(
        ItemRecipe::Level::Manual,
        blockRegistry.getBlockByName("main:bricks").toItemStack(3),

        ItemStack("main:brick", 2)
    );

    registerRecipe(
        ItemRecipe::Level::Table,
        blockRegistry.getBlockByName("main:steamGenerator").toItemStack(),

        ItemStack("main:brick", 32),
        ItemStack("main:ironIngot", 4),
        ItemStack("main:battery"),
        blockRegistry.getBlockByName("main:glass").toItemStack(3),
        blockRegistry.getBlockByName("main:copperWire").toItemStack(2),
        blockRegistry.getBlockByName("main:fluidPipe").toItemStack(2)
    );

    registerRecipe(
        ItemRecipe::Level::Table,
        ItemStack("main:battery"),

        ItemStack("main:tinIngot", 6),
        blockRegistry.getBlockByName("main:copperWire").toItemStack(2),
        ItemStack("main:nickelIngot", 1),
        ItemStack("main:ironIngot", 1)
    )
        ->withAchievement(ElectronicsMilestone::ID, ElectronicsMilestone::MakeBattery);

    registerRecipe(
        ItemRecipe::Level::Table,
        blockRegistry.getBlockByName("main:chemicalReactor").toItemStack(),

        blockRegistry.getBlockByName("main:tinCasing").toItemStack(),
        blockRegistry.getBlockByName("main:glass").toItemStack(3)
    )
        ->withAchievement(ElectronicsMilestone::ID, ElectronicsMilestone::MakeChemicalReactor);

    registerRecipe(
        ItemRecipe::Level::Table,
        ItemStack("main:coil"),

        ItemStack("main:ironIngot", 1),
        blockRegistry.getBlockByName("main:copperWire").toItemStack(3)
    )
        ->withAchievement(ElectronicsMilestone::ID, ElectronicsMilestone::MakeCoil);

    registerRecipe(
        ItemRecipe::Level::Table,
        blockRegistry.getBlockByName("main:engineeringTable").toItemStack(),

        ItemStack("main:silverIngot", 2),
        ItemStack("main:steelIngot", 6),
        ItemStack("main:battery", 1),
        blockRegistry.getBlockByName("main:copperWire").toItemStack(3)
    );

    // Stairs can be made out of their corresponding block in 5:4 proportions.
    blockRegistry.forEachBlock(
        [this](Block &block)
        {
            if (block.isDyedBlock())
            {
                // Don't generate recipies for every single variant of dyed stairs.
                return;
            }

            auto stairsName = block.name + "Stairs";
            if (!blockRegistry.isValidBlock(stairsName))
            {
                // No stairs for this block.
                return;
            }

            registerRecipe(
                ItemRecipe::Level::Table,
                blockRegistry.getBlockByName(stairsName).toItemStack(5),

                block.toItemStack(4)
            );
        }
    );

    registerRecipe(
        ItemRecipe::Level::Table,
        blockRegistry.getBlockByName("main:oakDoor").toItemStack(),

        blockRegistry.getBlockByName("main:planks").toItemStack(8)
    );

    registerRecipe(
        ItemRecipe::Level::EngTable,
        blockRegistry.getBlockByName("main:accumulator").toItemStack(),

        ItemStack("main:bronzeIngot", 6),
        ItemStack("main:battery", 3),
        blockRegistry.getBlockByName("main:copperWire").toItemStack(2)
    );

    registerRecipe(
        ItemRecipe::Level::EngTable,
        ItemStack("main:siliconSheet"),

        ItemStack("main:siliconIngot", 2)
    );

    registerRecipe(
        ItemRecipe::Level::EngTable,
        blockRegistry.getBlockByName("main:solarPanel").toItemStack(),

        ItemStack("main:steelIngot", 8),
        ItemStack("main:siliconSheet", 4),
        ItemStack("main:copperIngot", 1),
        blockRegistry.getBlockByName("main:glass").toItemStack()
    );

    registerRecipe(
        ItemRecipe::Level::EngTable,
        blockRegistry.getBlockByName("main:steelBarrel").toItemStack(),

        blockRegistry.getBlockByName("main:woodenBarrel").toItemStack(),
        ItemStack("main:steelIngot", 12)
    );

    registerRecipe(
        ItemRecipe::Level::EngTable,
        blockRegistry.getBlockByName("main:leadBarrel").toItemStack(),

        blockRegistry.getBlockByName("main:steelBarrel").toItemStack(),
        ItemStack("main:leadIngot", 12)
    );

    registerRecipe(
        ItemRecipe::Level::EngTable,
        blockRegistry.getBlockByName("main:oilRefinery").toItemStack(),

        ItemStack("main:steelIngot", 6),
        blockRegistry.getBlockByName("main:fluidPipe").toItemStack(8),
        ItemStack("main:coil", 10),
        blockRegistry.getBlockByName("main:copperWire").toItemStack(1),
        ItemStack("main:battery", 1),
        blockRegistry.getBlockByName("main:glass").toItemStack(2)
    );

    registerRecipe(
        ItemRecipe::Level::EngTable,
        blockRegistry.getBlockByName("main:combustionGenerator").toItemStack(),

        ItemStack("main:steelIngot", 6),
        ItemStack("main:goldIngot", 4),
        ItemStack("main:battery"),
        blockRegistry.getBlockByName("main:glass").toItemStack(3),
        blockRegistry.getBlockByName("main:copperWire").toItemStack(2),
        blockRegistry.getBlockByName("main:fluidPipe").toItemStack(2)
    );

    registerRecipe(
        ItemRecipe::Level::EngTable,
        blockRegistry.getBlockByName("main:macerator").toItemStack(),

        blockRegistry.getBlockByName("main:steelCasing").toItemStack(1),
        ItemStack("main:battery"),
        ItemStack("main:goldIngot", 4),
        ItemStack("main:leadIngot", 2)
    );

    registerRecipe(
        ItemRecipe::Level::EngTable,
        blockRegistry.getBlockByName("main:electricFurnace").toItemStack(),

        blockRegistry.getBlockByName("main:tinCasing").toItemStack(1),
        blockRegistry.getBlockByName("main:glass").toItemStack(4),
        ItemStack("main:battery"),
        ItemStack("main:coil", 20)
    );

    registerRecipe(
        ItemRecipe::Level::EngTable,
        ItemStack("main:speedUpgrade"),

        ItemStack("main:tinIngot", 6),
        ItemStack("main:silverIngot", 4),
        ItemStack("main:goldIngot", 4),
        ItemStack("main:brassIngot", 1),
        ItemStack("main:bronzeIngot", 1)
    );

    registerRecipe(
        ItemRecipe::Level::EngTable,
        ItemStack("main:efficiencyUpgrade"),

        ItemStack("main:tinIngot", 6),
        ItemStack("main:steelIngot", 4),
        ItemStack("main:silverIngot", 4)
    );

    registerRecipe(
        ItemRecipe::Level::EngTable,
        blockRegistry.getBlockByName("main:electricLamp").toItemStack(),

        blockRegistry.getBlockByName("main:glass").toItemStack(6),
        ItemStack("main:ironIngot", 1),
        blockRegistry.getBlockByName("main:copperWire").toItemStack(1)
    );

    registerRecipe(
        ItemRecipe::Level::EngTable,
        ItemStack("main:electricDrill"),

        ItemStack("main:diamond", 32),
        ItemStack("main:battery", 2),
        ItemStack("main:steelIngot", 8),
        blockRegistry.getBlockByName("main:copperWire").toItemStack(8)
    );

    registerRecipe(
        ItemRecipe::Level::EngTable,
        blockRegistry.getBlockByName("main:indicatorLights").toItemStack(1),

        blockRegistry.getBlockByName("main:tinCasing").toItemStack(1),
        blockRegistry.getBlockByName("main:glass").toItemStack(1),
        ItemStack("main:goldDust", 2)
    );

    registerRecipe(
        ItemRecipe::Level::EngTable,
        blockRegistry.getBlockByName("main:conveyorBelt").toItemStack(16),

        blockRegistry.getBlockByName("main:planks").toItemStack(1),
        ItemStack("main:steelIngot", 3),
        ItemStack("main:rubber", 4)
    );

    registerRecipe(
        ItemRecipe::Level::EngTable,
        blockRegistry.getBlockByName("main:beltMotor").toItemStack(1),

        blockRegistry.getBlockByName("main:conveyorBelt").toItemStack(1),
        ItemStack("main:nickelIngot", 2),
        ItemStack("main:coil", 1),
        ItemStack("main:battery", 1)
    );

    registerRecipe(
        ItemRecipe::Level::EngTable,
        ItemStack("main:steelClaws"),

        ItemStack("main:steelIngot", 3)
    );

    registerRecipe(
        ItemRecipe::Level::EngTable,
        blockRegistry.getBlockByName("main:itemExtractor").toItemStack(),

        blockRegistry.getBlockByName("main:beltMotor").toItemStack(),
        ItemStack("main:steelClaws"),
        blockRegistry.getBlockByName("main:tinCasing").toItemStack()
    );

    registerRecipe(
        ItemRecipe::Level::EngTable,
        blockRegistry.getBlockByName("main:incinerator").toItemStack(),

        blockRegistry.getBlockByName("main:steelCasing").toItemStack(),
        ItemStack("main:ironIngot", 4),
        ItemStack("main:coil", 10)
    );

    registerRecipe(
        ItemRecipe::Level::EngTable,
        ItemStack("main:laser"),

        ItemStack("main:emerald"),
        blockRegistry.getBlockByName("main:electricLamp").toItemStack()
    );

    registerRecipe(
        ItemRecipe::Level::EngTable,
        blockRegistry.getBlockByName("main:miningRig").toItemStack(),

        ItemStack("main:laser"),
        blockRegistry.getBlockByName("main:pump").toItemStack(),
        blockRegistry.getBlockByName("main:steelCasing").toItemStack()
    );

    registerRecipe(
        ItemRecipe::Level::EngTable,
        blockRegistry.getBlockByName("main:beltJunction").toItemStack(),

        blockRegistry.getBlockByName("main:conveyorBelt").toItemStack(3),
        ItemStack("main:steelClaws"),
        blockRegistry.getBlockByName("main:tinCasing").toItemStack()
    );

    registerRecipe(
        ItemRecipe::Level::EngTable,
        blockRegistry.getBlockByName("main:centrifuge").toItemStack(),

        ItemStack("main:steelIngot", 16),
        blockRegistry.getBlockByName("main:steelCasing").toItemStack()
    );

    registerRecipe(
        ItemRecipe::Level::EngTable,
        blockRegistry.getBlockByName("main:uraniumRefinery").toItemStack(),

        ItemStack("main:coil", 8),
        ItemStack("main:nickelIngot", 16),
        ItemStack("main:battery"),
        blockRegistry.getBlockByName("main:leadCasing").toItemStack()
    );

    registerRecipe(
        ItemRecipe::Level::EngTable,
        blockRegistry.getBlockByName("main:nuclearReactor").toItemStack(),

        blockRegistry.getBlockByName("main:leadCasing").toItemStack(),
        blockRegistry.getBlockByName("main:temperedGlass").toItemStack(3),
        ItemStack("main:battery", 5)
    );

    registerRecipe(
        ItemRecipe::Level::EngTable,
        ItemStack("main:heatSink"),

        ItemStack("main:copperIngot", 8),
        ItemStack("main:steelIngot", 1)
    );

    registerRecipe(
        ItemRecipe::Level::EngTable,
        ItemStack("main:turbine"),

        ItemStack("main:steelIngot", 4),
        ItemStack("main:coil", 1)
    );

    registerRecipe(
        ItemRecipe::Level::EngTable,
        ItemStack("main:alternator"),

        ItemStack("main:turbine", 1),
        ItemStack("main:nickelIngot", 2),
        ItemStack("main:steelIngot", 8)
    );

    registerRecipe(
        ItemRecipe::Level::EngTable,
        ItemStack("main:uraniumFissionCell"),

        ItemStack("main:alternator", 3),
        ItemStack("main:leadIngot", 8),
        ItemStack("main:goldIngot", 8),
        blockRegistry.getBlockByName("main:pump").toItemStack(1)
    );

    registerRecipe(
        ItemRecipe::Level::EngTable,
        ItemStack("main:plutoniumCaptureCell"),

        ItemStack("main:steelIngot", 64),
        ItemStack("main:leadIngot", 128),
        ItemStack("main:silverIngot", 16),
        ItemStack("main:diamond", 8)
    );

    registerRecipe(
        ItemRecipe::Level::EngTable,
        ItemStack("main:plutoniumFissionCell"),

        ItemStack("main:alternator", 15),
        ItemStack("main:leadIngot", 32),
        ItemStack("main:goldIngot", 8),
        ItemStack("main:silverIngot", 16),
        blockRegistry.getBlockByName("main:pump").toItemStack(1)
    );

    registerRecipe(
        ItemRecipe::Level::EngTable,
        ItemStack("main:fan1"),

        ItemStack("main:turbine", 1),
        ItemStack("main:rubber", 8)
    );

    registerRecipe(
        ItemRecipe::Level::EngTable,
        blockRegistry.getBlockByName("main:substation").toItemStack(),

        blockRegistry.getBlockByName("main:accumulator").toItemStack(8),
        blockRegistry.getBlockByName("main:steelCasing").toItemStack()
    );

    registerRecipe(
        ItemRecipe::Level::EngTable,
        blockRegistry.getBlockByName("main:blockPlacer").toItemStack(),

        blockRegistry.getBlockByName("main:conveyorBelt").toItemStack(),
        blockRegistry.getBlockByName("main:steelCasing").toItemStack(),
        ItemStack("main:steelClaws")
    );

    registerRecipe(
        ItemRecipe::Level::EngTable,
        blockRegistry.getBlockByName("main:itemDeployer").toItemStack(),

        blockRegistry.getBlockByName("main:conveyorBelt").toItemStack(),
        blockRegistry.getBlockByName("main:steelCasing").toItemStack(),
        blockRegistry.getBlockByName("main:copperWire").toItemStack(),
        ItemStack("main:steelClaws")
    );

    registerRecipe(
        ItemRecipe::Level::EngTable,
        ItemStack("main:quartzFork", 32),

        ItemStack("main:quartz", 1)
    );

    registerRecipe(
        ItemRecipe::Level::EngTable,
        blockRegistry.getBlockByName("main:quartzClock").toItemStack(),

        blockRegistry.getBlockByName("main:steelCasing").toItemStack(),
        blockRegistry.getBlockByName("main:copperWire").toItemStack(),
        blockRegistry.getBlockByName("main:dataCable").toItemStack(),
        ItemStack("main:quartzFork", 4)
    );

    registerRecipe(
        ItemRecipe::Level::EngTable,
        blockRegistry.getBlockByName("main:blockBreaker").toItemStack(),

        blockRegistry.getBlockByName("main:beltMotor").toItemStack(),
        blockRegistry.getBlockByName("main:steelCasing").toItemStack(),
        ItemStack("main:coil"),
        ItemStack("main:diamond", 8)
    );

    registerRecipe(
        ItemRecipe::Level::EngTable,
        blockRegistry.getBlockByName("main:logicBox").toItemStack(8),

        blockRegistry.getBlockByName("main:steelCasing").toItemStack(8),
        ItemStack("main:siliconSheet"),
        ItemStack("main:goldIngot", 2)
    );

    registerRecipe(
        ItemRecipe::Level::EngTable,
        ItemStack("main:jetpack", 1),

        ItemStack("main:coil", 16),
        ItemStack("main:goldIngot", 4),
        ItemStack("main:steelIngot", 16)
    );

    registerRecipe(
        ItemRecipe::Level::EngTable,
        ItemStack("main:memoryChip", 1),

        ItemStack("main:siliconSheet", 3),
        ItemStack("main:goldIngot", 1),
        ItemStack("main:rubber", 1)
    );
}

std::vector<ItemRecipe*> ItemRecipeRegistry::search(ItemRecipe::Level maxLevel, std::string query)
{
    std::vector<ItemRecipe*> results;

    std::vector<ItemRecipe*>::iterator it;
    for (it=recipes.begin(); it!=recipes.end(); ++it)
    {
        if (static_cast<int>((*it)->identity.level) <= static_cast<int>(maxLevel) && (*it)->matchesQuery(query))
        {
            results.push_back(*it);
        }
    }

    return results;
}

ItemRecipe* ItemRecipeRegistry::getRecipeByID(uint32_t id)
{
    if (id >= (uint32_t) recipes.size())
    {
        return nullptr;
    }

    return recipes[id];
}

int ItemRecipeRegistry::getTotalRecipes()
{
    return (int) recipes.size();
}