public class BlockPortableLadder extends Block {

	public BlockPortableLadder() {
		super(Material.IRON);
		setHardness(0.05f);

		IBlockState state = blockState.getBaseState();
		state = BlockProperties.setSide(state, EnumFacing.NORTH);
		state = BlockProperties.setHalf(state, EnumBlockHalf.BOTTOM);
		setDefaultState(state);
	}

	@Override
	protected BlockStateContainer createBlockState() {
		return new BlockStateContainer(this, BlockProperties.SIDE, BlockProperties.HALF);
	}

	@Override
	public IBlockState getStateFromMeta(int meta) {
		IBlockState state = getDefaultState();
		state = BlockProperties.setSide(state, EnumFacing.getHorizontal(meta & 0x3));
		state = BlockProperties.setHalf(state, EnumBlockHalf.values()[(meta >> 2) & 0x1]);
		return state;
	}

	@Override
	public int getMetaFromState(IBlockState state) {
		int meta = 0;
		meta |= BlockProperties.getSide(state).getHorizontalIndex();
		meta |= BlockProperties.getHalf(state).ordinal() << 2;
		return meta;
	}

	@Override
	public boolean isOpaqueCube(IBlockState state) {
		return false;
	}

	@Override
	public boolean isFullCube(IBlockState state) {
		return false;
	}

	@Override
	public boolean isLadder(IBlockState state, IBlockAccess world, BlockPos pos, EntityLivingBase entity) {
		return true;
	}

	@Override
	public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess world, BlockPos pos) {
		if (state.getBlock() == this) {
			boolean bottom = BlockProperties.getHalf(state) == EnumBlockHalf.BOTTOM;
			switch (BlockProperties.getSide(state)) {
				default:
				case NORTH:
					return new AxisAlignedBB(0.0f, 0.0f, bottom ? 0.25f : 0.0f, 1.0f, 1.0f, 0.5f);
				case EAST:
					return new AxisAlignedBB(0.5f, 0.0f, 0.0f, bottom ? 0.75f : 1.0f, 1.0f, 1.0f);
				case SOUTH:
					return new AxisAlignedBB(0.0f, 0.0f, 0.5f, 1.0f, 1.0f, bottom ? 0.75f : 1.0f);
				case WEST:
					return new AxisAlignedBB(bottom ? 0.25f : 0.0f, 0.0f, 0.0f, 0.5f, 1.0f, 1.0f);
			}
		}
		return null;
	}

	@Override
	public boolean canHarvestBlock(IBlockAccess world, BlockPos pos, EntityPlayer player) {
		return true;
	}

	@Override
	public void onBlockHarvested(World world, BlockPos pos, IBlockState state, EntityPlayer player) {
		BlockPos startPos = pos;
		if (BlockProperties.getHalf(state) == EnumBlockHalf.TOP)
			startPos = pos.down(2);
		else {
			IBlockState upState = world.getBlockState(pos.up());
			if (upState.getBlock() == this && BlockProperties.getHalf(upState) == EnumBlockHalf.TOP)
				startPos = pos.down();
		}

		EnumFacing face = BlockProperties.getSide(state);
		for (int i = 0; i < 3; i++) {
			BlockPos curPos = startPos.up(i);
			if (!curPos.equals(pos))
				world.setBlockToAir(curPos);
			world.setBlockToAir(curPos.offset(face));
		}
	}

	public static class ItemPortableLadder extends ItemBlock {

		public ItemPortableLadder(Block block) {
			super(block);
		}

		@Override
		public EnumActionResult onItemUse(ItemStack stack, EntityPlayer playerIn, World world, BlockPos pos,
				EnumHand hand, EnumFacing side, float hitX, float hitY, float hitZ) {
			if (world.isRemote) {
				return EnumActionResult.SUCCESS;
			} else if (side != EnumFacing.UP) {
				return EnumActionResult.PASS;
			} else {
				if (!world.getBlockState(pos).getBlock().isReplaceable(world, pos)) {
					pos = pos.up();
				}

				int i = MathHelper.floor_double((double) (playerIn.rotationYaw * 4.0F / 360.0F) + 0.5D) & 3;
				EnumFacing placeFacing = EnumFacing.getHorizontal(i);

				for (int height = 0; height < 3; height++) {
					BlockPos placePos1 = pos.up(height);
					IBlockState state1 = world.getBlockState(placePos1);
					if (!playerIn.canPlayerEdit(placePos1, side, stack) || !state1.getBlock()
							.isReplaceable(world, placePos1))
						return EnumActionResult.FAIL;
					BlockPos placePos2 = placePos1.offset(placeFacing);
					IBlockState state2 = world.getBlockState(placePos2);
					if (!playerIn.canPlayerEdit(placePos2, side, stack) || !state2.getBlock()
							.isReplaceable(world, placePos2))
						return EnumActionResult.FAIL;
				}

				for (int height = 0; height < 3; height++) {
					IBlockState state = ModBuildingBricks.portableLadder.getDefaultState();
					state = BlockProperties.setSide(state, placeFacing);
					if (height != 2)
						state = BlockProperties.setHalf(state, EnumBlockHalf.BOTTOM);
					else
						state = BlockProperties.setHalf(state, EnumBlockHalf.TOP);
					BlockPos placePos = pos.up(height);
					world.setBlockState(placePos, state, 3);
					state = BlockProperties.setSide(state, placeFacing.getOpposite());
					world.setBlockState(placePos.offset(placeFacing), state, 3);
				}
				--stack.stackSize;
				return EnumActionResult.SUCCESS;
			}
		}
	}

	@Override
	public EnumPushReaction getMobilityFlag(IBlockState state) {
		return EnumPushReaction.BLOCK;
	}
} public interface BlockBuildingBricks {

	MaterialBlockLogic getBlockLogic();

} public interface BlockMaterial {

	String getLocalizedName(Material mat);

	Material getMaterial(IBlockAccess world, BlockPos pos);
} public class BlockPlacementManager {

	private static BlockPlacementManager instance;

	public static BlockPlacementManager getInstance() {
		return instance;
	}

	private final ArrayList<PlacementOverrideEntry> handlerEntries = new ArrayList<>();
	private List<IBlockPlacementHandler> handlers;

	static {
		instance = new BlockPlacementManager();
		instance.add(20, new SlabCombinePlacementHandler());
		instance.add(20, new StepCombinePlacementHandler());
		instance.add(20, new CornerCombinePlacementHandler());
		instance.add(69, new SlabPlacementHandler());
		instance.add(70, new FirstFallbackPlacementHandler());
		instance.add(200, new FallbackPlacementHandler());
	}

	public void add(int priority, IBlockPlacementHandler handler) {
		handlerEntries.add(new PlacementOverrideEntry(priority, handler));
		handlerEntries.sort((e1, e2) -> Integer.compare(e1.priority, e2.priority));
		handlers = handlerEntries.stream().map(input -> input.handler).collect(Collectors.toList());
	}

	public List<IBlockPlacementHandler> getHandlers() {
		return handlers;
	}

	@SubscribeEvent
	public void onItemBlockCanPlaceBlockOnSide(ItemBlockCanPlaceBlockOnSideEvent event) {
		for (IBlockPlacementHandler handler : getHandlers()) {
			if (!handler.isHandled(event.getStack()))
				continue;

			event.setCanceled(true);
			if (handler.canPlaceBlockOnSide(event.getWorld(), event.getPos(), event.getSide(),
					event.getPlayer(), event.getStack()))
				event.setCanPlaceBlockOnSide(true);
		}
	}

	@SubscribeEvent
	public void onItemBlockUse(ItemBlockUseEvent event) {
		Material mat = MaterialRegistry.getMaterialForStack(event.getStack());
		IBlockState state = event.getWorld().getBlockState(event.getPlaceParams().pos);
		for (IBlockPlacementHandler handler : getHandlers()) {
			if (handler.isHandled(event.getStack())) {
				event.setActionResult(EnumActionResult.FAIL);
				EnumActionResult result =
						handler.place(event.getWorld(), event.getStack(), event.getPlayer(), mat, state,
								event.getPlaceParams());
				if (result != EnumActionResult.PASS) {
					event.setCanceled(true);
					event.setActionResult(result);
					break;
				}
			}
		}
	}

	private static class PlacementOverrideEntry {
		public int priority;
		public IBlockPlacementHandler handler;

		public PlacementOverrideEntry(int priority, IBlockPlacementHandler handler) {
			this.priority = priority;
			this.handler = handler;
		}
	}
} @SideOnly(Side.CLIENT)
public class ModelBakerBlockMaterial extends ModelBakerBase {

	public static Map<Material, TextureAtlasSprite> particleTextures = Maps.newHashMap();

	private Map<MaterialBlockType, RenderDefinition> renderDefinitions = Maps.newHashMap();

	public ModelBakerBlockMaterial() {
		super();

		renderDefinitions.put(MaterialBlockType.FULL, new RenderDefinitionSimple(MaterialBlockType.FULL));
		renderDefinitions.put(MaterialBlockType.STAIRS, new RenderDefinitionStairs());
		renderDefinitions.put(MaterialBlockType.SLAB, new RenderDefinitionSlab(MaterialBlockType.SLAB));
		renderDefinitions.put(MaterialBlockType.VERTICAL_SLAB,
				new RenderDefinitionSlab(MaterialBlockType.VERTICAL_SLAB));
		renderDefinitions.put(MaterialBlockType.STEP, new RenderDefinitionStep());
		renderDefinitions.put(MaterialBlockType.CORNER,
				new RenderDefinitionRotHalf(MaterialBlockType.CORNER));
		renderDefinitions.put(MaterialBlockType.WALL,
				new RenderDefinitionConnectible("wall_post", "wall_post_short", "wall_connection", "wall"));
		renderDefinitions.put(MaterialBlockType.FENCE,
				new RenderDefinitionConnectible("fence_post", null, "fence_connection", "fence"));
		renderDefinitions.put(MaterialBlockType.FENCE_GATE,
				new RenderDefinitionFenceGate(MaterialBlockType.FENCE_GATE));
		renderDefinitions.put(MaterialBlockType.PANE, new RenderDefinitionPane());
	}

	@Override
	public void onTextureStitchPreEvent(TextureStitchEvent.Pre event) {
		for (Material mat : MaterialRegistry.getAll()) {
			particleTextures.put(mat,
					event.getMap().registerSprite(new ResourceLocation(mat.getTextures().get("particle"))));
		}
	}

	@Override
	public void onModelBakeEvent(ModelBakeEvent event) {
		for (Cell<MaterialBlockType, StructureMaterial, Block> cell : MaterialBlockRegistry.instance.getBlocks()
				.cellSet()) {
			bakeBlockModels(event.getModelManager(), event.getModelRegistry(), cell.getRowKey(),
					cell.getColumnKey(), cell.getValue());
		}
	}

	private void bakeBlockModels(ModelManager modelManager,
			IRegistry<ModelResourceLocation, IBakedModel> modelRegistry,
			MaterialBlockType blockType, StructureMaterial structMat, Block block) {
		RenderDefinition renderDefinition = renderDefinitions.get(blockType);
		if (renderDefinition == null)
			return;

		ResourceLocation blockName = Block.REGISTRY.getNameForObject(block);

		// Register models in the registry
		ModelItemMaterialBlock itemBakedModel = new ModelItemMaterialBlock(modelManager.getMissingModel());
		modelRegistry.putObject(new ModelResourceLocation(blockName + "#inventory"), itemBakedModel);

		ModelMaterialBlock blockBakedModel = new ModelMaterialBlock();
		for (Object stateObj : block.getBlockState().getValidStates()) {
			ModelResourceLocation modelLoc =
					new ModelResourceLocation(blockName, getPropertyString((IBlockState) stateObj));
			modelRegistry.putObject(modelLoc, blockBakedModel);
		}
		blockBakedModel.setDelegate(modelManager.getMissingModel());

		// Generate the actual models
		for (Material mat : MaterialBlockRegistry.instance.getBlockMaterials(block)) {
			// Item model
			itemBakedModel.put(mat.getMaterialId(), renderDefinition.bakeItem(modelManager, mat));

			// Block models
			for (Object stateObj : block.getBlockState().getValidStates()) {
				IBlockState state = (IBlockState) stateObj;
				state = TileMaterial.setStateMaterial((IExtendedBlockState) state, mat);
				blockBakedModel.put(BlockStateUtil.getHashCode(state),
						renderDefinition.bake(modelManager, mat, state));
			}
		}
	}
} @SideOnly(Side.CLIENT)
public class ModelBakerItemMaterial extends ModelBakerBase {

	public static ModelBakerItemMaterial instance;
	private ResourceLocation modelLoc;
	private ModelResourceLocation targetModelLoc;
	private Vector3f translation;
	private Vector3f scale;

	public ModelBakerItemMaterial(String modelLocName, String targetModelLocName, Vector3f translation,
			Vector3f scale) {
		modelLoc = new ResourceLocation(modelLocName);
		targetModelLoc = new ModelResourceLocation(targetModelLocName);
		this.translation = translation;
		this.scale = scale;
	}

	@Override
	public void onModelBakeEvent(ModelBakeEvent event) {
		IModel baseItemModel = getModel(modelLoc);
		IBakedModel baseBakedItemModel = BakerUtil.bake(baseItemModel, DefaultVertexFormats.ITEM);
		ModelItemMaterial dynModel = new ModelItemMaterial(baseBakedItemModel);
		event.getModelRegistry().putObject(targetModelLoc, dynModel);

		for (Material material : MaterialRegistry.getAll()) {

			ItemStack stack = material.getFirstBlock().getStack();
			Item item = stack.getItem();
			List<String> variantNames;
			try {
				//noinspection unchecked
				variantNames =
						(List<String>) ReflectionHelper.findMethod(ModelBakery.class, event.getModelLoader(),
								new String[] {"getVariantNames", "func_177596_a"}, Item.class)
								.invoke(event.getModelLoader(), item);
			} catch (Exception e) {
				e.printStackTrace();
				continue;
			}
			ResourceLocation itemName = new ResourceLocation(
					variantNames.size() > 1 ? variantNames.get(stack.getMetadata()) : variantNames.get(0));
			ResourceLocation model =
					new ResourceLocation(itemName.getResourceDomain(), "item/" + itemName.getResourcePath());
			IModel itemModel = getModel(model, new ResourceLocation("minecraft", "block/cube_bottom_top"));
			itemModel = BakerUtil.retexture(material.getTextures(), itemModel);
			IModelState modelState = new TRSRTransformation(translation, null, scale, null);
			IBakedModel bakedItemModel = BakerUtil.bake(itemModel, modelState, DefaultVertexFormats.ITEM);

			itemModel = getModel(modelLoc);
			baseBakedItemModel = BakerUtil.bake(itemModel, DefaultVertexFormats.ITEM);

			dynModel.put(material, new ModelItemMaterial(baseBakedItemModel, bakedItemModel));
		}
	}
} public class GuiMaterialBag extends GuiContainer {
	public static final int ID = 1;
	private static final ResourceLocation BG_RESOURCE =
			new ResourceLocation("buildingbricks:textures/gui/container/material_bag.png");

	public GuiMaterialBag(Container container) {
		super(container);
	}

	@Override
	protected void drawGuiContainerForegroundLayer(int mouseX, int mouseY) {
		super.drawGuiContainerForegroundLayer(mouseX, mouseY);
		this.fontRendererObj.drawString(String.format("%.2f",
				((ItemHandlerMaterialBag) ((SlotItemHandler) inventorySlots.inventorySlots.get(0))
						.getItemHandler()).getCurrentVolume() / 1000f), 70, 64, 4210752);
	}

	@Override
	protected void drawGuiContainerBackgroundLayer(float partialTicks, int mouseX, int mouseY) {
		GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f);
		mc.getTextureManager().bindTexture(BG_RESOURCE);

		int k = (this.width - this.xSize) / 2;
		int l = (this.height - this.ySize) / 2;
		this.drawTexturedModalRect(k, l, 0, 0, this.xSize, this.ySize);
	}
} public class GuiTrowel extends GuiContainer {

	public static final int ID = 0;
	public static final ResourceLocation BG_RESOURCE =
			new ResourceLocation("buildingbricks:textures/gui/container/trowel.png");

	private EntityPlayer player;

	public GuiTrowel(EntityPlayer player, ItemStack trowel) {
		super(ModBuildingBricks.trowel.getContainer(player));

		this.player = player;
	}

	@Override
	protected void drawGuiContainerBackgroundLayer(float partialTicks, int mouseX, int mouseY) {
		GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f);
		mc.getTextureManager().bindTexture(BG_RESOURCE);

		int k = (this.width - this.xSize) / 2;
		int l = (this.height - this.ySize) / 2;
		this.drawTexturedModalRect(k, l, 0, 0, this.xSize, this.ySize);

		HeldEquipment equip = PlayerUtil.getHeldEquipment(player, ModBuildingBricks.trowel);
		if(equip == null)
			return;
		ItemStack stack = equip.getStack();
		Material mat = MaterialStack.get(stack);
		if (mat != null) {
			int i = mat.getBlockRotation().getIndex(ModBuildingBricks.trowel.getCurrentBlockType(stack));
			int xCol = i % 4;
			int yCol = i / 4;
			this.drawTexturedModalRect(k + 95 + 18 * xCol, l + 6 + 18 * yCol, 176, 0, 22, 22);
		}
	}
} @SideOnly(Side.CLIENT)
public class TrowelKeyBindings {
	public static Consumer<KeyInputEvent> getOnTrowelPrev() {
		return event -> {
			HeldEquipment equipment =
					PlayerUtil.getHeldEquipment(Minecraft.getMinecraft().thePlayer, ModBuildingBricks.trowel);
			if (equipment != null && equipment.getStack().getItem() != null) {
				TrowelRotateBlockTypeMessage.send(false, null);
			}
		};
	}

	public static Consumer<KeyInputEvent> getOnTrowelNext() {
		return event -> {
			HeldEquipment equipment =
					PlayerUtil.getHeldEquipment(Minecraft.getMinecraft().thePlayer, ModBuildingBricks.trowel);
			if (equipment != null && equipment.getStack().getItem() != null) {
				TrowelRotateBlockTypeMessage.send(true, null);
			}
		};
	}

	public static Function<MouseEvent, Boolean> getOnTrowelScroll() {
		return event -> {
			TrowelRotateBlockTypeMessage.send(event.getDwheel() < 0, null);
			return true;
		};
	}

	public static Consumer<KeyInputEvent> getOnTrowelSelect(final MaterialBlockType blockType) {
		return event -> TrowelRotateBlockTypeMessage.send(false, blockType);
	}
} public class CommandCreateMaterial extends CommandBase {
	@Override
	public String getCommandName() {
		return "material";
	}

	@Override
	public String getCommandUsage(ICommandSender sender) {
		return "buildingbricks.commands.material.usage";
	}

	@Override
	public int getRequiredPermissionLevel() {
		return 0;
	}

	@Override
	public void execute(MinecraftServer server, ICommandSender sender, String[] args)
			throws CommandException {
		EntityPlayer player = (EntityPlayer) sender;

		if (args.length < 1)
			throw new WrongUsageException("buildingbricks.commands.material.usage");

		if (Objects.equals(args[0], "generate")) {
			if (args.length < 2)
				throw new WrongUsageException("buildingbricks.commands.material.usage");

			String structMatName = args[1];
			StructureMaterial structMat = StructureMaterial.valueOf(structMatName);

			ItemStack blockStack = player.inventory.getStackInSlot(0);
			if (blockStack == null)
				return;

			Block block = ((ItemBlock) blockStack.getItem()).block;

			String id = Block.REGISTRY.getNameForObject(block).toString();
			if (args.length > 2)
				id = id.split(":")[0] + ":" + args[2];

			JsonObject mat = new JsonObject();
			mat.addProperty("name", blockStack.getDisplayName());
			mat.addProperty("id", id);
			mat.addProperty("type", structMat.getName());
			mat.addProperty("hardness", block.getBlockHardness(block.getDefaultState(), null, null));
			mat.addProperty("resistance", (block.getExplosionResistance(null) * 5f) / 3f);
			mat.add("textures", new JsonObject());

			JsonObject blocks = new JsonObject();
			for (int i = 0; i < MaterialBlockType.values().length; i++) {
				ItemStack stack = player.inventory.getStackInSlot(i);
				if (stack == null)
					continue;

				if (!stack.getHasSubtypes()) {
					blocks.addProperty(MaterialBlockType.values()[i].toString().toLowerCase(),
							Item.REGISTRY.getNameForObject(stack.getItem()).toString());
				} else {
					JsonObject blockInfoJson = new JsonObject();
					blockInfoJson.addProperty("id",
							Item.REGISTRY.getNameForObject(stack.getItem()).toString());
					blockInfoJson.addProperty("meta", stack.getMetadata());
					blocks.add(MaterialBlockType.values()[i].toString().toLowerCase(), blockInfoJson);
				}
			}
			mat.add("blocks", blocks);
			String matString = new GsonBuilder().setPrettyPrinting()
					.serializeNulls()
					.setFieldNamingPolicy(FieldNamingPolicy.IDENTITY)
					.create()
					.toJson(mat)
					.replace("  ", "\t");
			Path outputPath = Paths.get(Minecraft.getMinecraft().mcDataDir.getAbsolutePath(), "config",
					"BuildingBricks", "resources", "assets", id.split(":")[0], "materials");
			if (!Files.exists(outputPath))
				try {
					Files.createDirectories(outputPath);
				} catch (IOException e) {
					Throwables.propagate(e);
				}
			outputPath = outputPath.resolve(id.split(":")[1] + ".json");
			try (BufferedWriter stream = Files.newBufferedWriter(outputPath, StandardOpenOption.CREATE_NEW)) {
				stream.write(matString);
			} catch (FileAlreadyExistsException e) {
				player.addChatMessage(
						new TextComponentTranslation("buildingbricks.commands.material.errorFileExists"));
			} catch (IOException e) {
				Throwables.propagate(e);
			}
		}
	}
} public class EventHandlerShowMaterialTooltip {

	private static EventHandlerShowMaterialTooltip instance = new EventHandlerShowMaterialTooltip();

	public static EventHandlerShowMaterialTooltip getInstance() {
		return instance;
	}

	@SubscribeEvent
	public void onItemTooltipEvent(ItemTooltipEvent event) {
		Material mat = MaterialRegistry.getMaterialForStack(event.getItemStack());
		if (mat != null) {
			if (event.isShowAdvancedItemTooltips())
				event.getToolTip()
						.add(I18n.translateToLocalFormatted("buildingbricks.tooltip.material",
								mat.getLocalizedName(), mat.getMaterialId()));
		}
	}
} @SideOnly(Side.CLIENT)
public class EventHandlerTrowelOverlay {

	private ResourceLocation widgetsTexture = new ResourceLocation("textures/gui/widgets.png");

	@SubscribeEvent
	public void onRenderOverlay(RenderGameOverlayEvent.Post event) {
		if (event.getType() == ElementType.HOTBAR) {
			EntityPlayerSP player = Minecraft.getMinecraft().thePlayer;
			HeldEquipment equipment = PlayerUtil.getHeldEquipment(player, ModBuildingBricks.trowel);
			if (equipment != null && equipment.getStack().getItem() == ModBuildingBricks.trowel) {
				Material mat = ModBuildingBricks.trowel.getMaterial(equipment.getStack());
				if (mat != null) {
					MaterialBlockType type = ModBuildingBricks.trowel.getCurrentBlockType(equipment.getStack());
					renderTrowelOverlay(event.getResolution(), event.getPartialTicks(), mat, type);
				}
			}
		}
	}

	private void renderTrowelOverlay(ScaledResolution sr, float partialTicks, Material mat,
			MaterialBlockType type) {
		int xPos = sr.getScaledWidth() / 2 - 88 + 10 * 20;
		int yPos = sr.getScaledHeight() - 16 - 3;

		Minecraft mc = Minecraft.getMinecraft();
		mc.getTextureManager().bindTexture(widgetsTexture);
		mc.ingameGUI.drawTexturedModalRect(xPos - 3, yPos - 3, 0, 0, 21, 22);
		mc.ingameGUI.drawTexturedModalRect(xPos + 18, yPos - 3, 181, 0, 1, 22);

		ItemStack stack1 = mat.getBlock(type).getStack();
		if (stack1 != null && stack1.getItem() != null)
			renderItem(partialTicks, xPos, yPos, stack1);
	}

	private void renderItem(float partialTicks, int xPos, int yPos, ItemStack stack) {
		Minecraft mc = Minecraft.getMinecraft();

		GlStateManager.enableRescaleNormal();
		GlStateManager.enableBlend();
		GlStateManager.tryBlendFuncSeparate(770, 771, 1, 0);
		RenderHelper.enableGUIStandardItemLighting();

		float f1 = stack.animationsToGo - partialTicks;

		if (f1 > 0.0F) {
			GlStateManager.pushMatrix();
			float f2 = 1.0F + f1 / 5.0F;
			GlStateManager.translate(xPos + 8, yPos + 12, 0.0F);
			GlStateManager.scale(1.0F / f2, (f2 + 1.0F) / 2.0F, 1.0F);
			GlStateManager.translate(-(xPos + 8), -(yPos + 12), 0.0F);
		}

		mc.getRenderItem().renderItemAndEffectIntoGUI(stack, xPos, yPos);
		if (f1 > 0.0F) {
			GlStateManager.popMatrix();
		}

		mc.getRenderItem().renderItemOverlays(mc.fontRendererObj, stack, xPos, yPos);

		RenderHelper.disableStandardItemLighting();
		GlStateManager.disableRescaleNormal();
		GlStateManager.disableBlend();
	}
} public class ItemMaterialBag extends Item implements ItemMaterial {

	public static final int BAG_VOLUME = 3 * 9 * 64 * 1000;

	@Override
	public void setMaterial(ItemStack stack, Material mat) {
		if (mat == null) {
			if (stack.hasTagCompound())
				stack.getTagCompound().removeTag("material");
		} else {
			if (!stack.hasTagCompound())
				stack.setTagCompound(new NBTTagCompound());
			stack.getTagCompound().setString("material", mat.getMaterialId());
		}
	}

	@Override
	public Material getMaterial(ItemStack stack) {
		if (stack.getTagCompound() != null && stack.getTagCompound().hasKey("material", NBT.TAG_STRING))
			return MaterialRegistry.get(stack.getTagCompound().getString("material"));
		else
			return null;
	}

	public void setVolume(ItemStack stack, int volume) {
		if (volume == 0) {
			setMaterial(stack, null);
		}

		if (stack.getTagCompound() == null)
			stack.setTagCompound(new NBTTagCompound());

		stack.getTagCompound().setInteger("Volume", volume);
	}

	public int getVolume(ItemStack stack) {
		if (stack.getTagCompound() == null)
			return 0;
		if (!stack.getTagCompound().hasKey("Volume"))
			return 0;

		return stack.getTagCompound().getInteger("Volume");
	}

	public void initUuid(ItemStack stack) {
		if (stack.getTagCompound() == null)
			stack.setTagCompound(new NBTTagCompound());

		if (!stack.getTagCompound().hasKey("UUID"))
			stack.getTagCompound().setString("UUID", UUID.randomUUID().toString());
	}

	public boolean areSameStack(ItemStack stack1, ItemStack stack2) {
		NBTTagCompound nbt1 = stack1.getTagCompound();
		NBTTagCompound nbt2 = stack2.getTagCompound();
		return nbt1 != null && nbt2 != null && nbt1.getString("UUID").equals(nbt2.getString("UUID"));
	}

	@Override
	public int getDamage(ItemStack stack) {
		return BAG_VOLUME - getVolume(stack);
	}

	@Override
	public int getMaxDamage() {
		return BAG_VOLUME;
	}

	@Override
	public ActionResult<ItemStack> onItemRightClick(ItemStack stack, World worldIn, EntityPlayer player,
			EnumHand hand) {
		player.openGui(ModBuildingBricks.MODID, GuiMaterialBag.ID, worldIn,
				MathHelper.floor_double(player.posX), MathHelper.floor_double(player.posY),
				MathHelper.floor_double(player.posZ));
		return super.onItemRightClick(stack, worldIn, player, hand);
	}

	@Override
	public String getItemStackDisplayName(ItemStack stack) {
		Material mat = getMaterial(stack);
		if (mat == null)
			return super.getItemStackDisplayName(stack);
		else
			return I18n.translateToLocalFormatted("item.buildingbricks.materialBagBinded.name",
					mat.getLocalizedName());
	}

	public Container getContainer(EntityPlayer player) {
		ItemHandlerMaterialBag inv = new ItemHandlerMaterialBag(player);
		return new GenericContainer()
				.addGenericSlots(80, 44, 1, 1, (slot, x, y) -> new SlotMaterialBag(inv, slot, x, y))
				.addPlayerSlots(player.inventory, ImmutableSet.of(player.inventory.currentItem));
	}

	public ItemStack createStack() {
		return createStack(null);
	}

	public ItemStack createStack(Material mat) {
		if (mat == null)
			return new ItemStack(this);

		ItemStack stack = new ItemStack(this);
		setMaterial(stack, mat);
		return stack;
	}

	@Override
	public ICapabilityProvider initCapabilities(ItemStack stack, NBTTagCompound nbt) {
		return new ICapabilityProvider() {
			@Override
			public boolean hasCapability(Capability<?> capability, EnumFacing facing) {
				return capability == ITEM_HANDLER_CAPABILITY;
			}

			@Override
			public <T> T getCapability(Capability<T> capability, EnumFacing facing) {
				if (capability == ITEM_HANDLER_CAPABILITY)
					return ITEM_HANDLER_CAPABILITY.cast(new ItemHandlerMaterialBag(stack));
				return null;
			}
		};
	}

	@SubscribeEvent
	public void onItemPickup(EntityItemPickupEvent event) {
		if (event.isCanceled())
			return;

		ItemStack stack = event.getItem().getEntityItem();
		if (stack == null || stack.stackSize <= 0)
			return;
		Material mat = MaterialRegistry.getMaterialForStack(stack);
		if (mat == null)
			return;
		BlockDescription desc = mat.getBlock(stack);
		if(desc == null)
			return;

		ItemStack bagStack = findBag(new InvWrapper(event.getEntityPlayer().inventory), mat);
		if (bagStack == null)
			return;

		int volume = getVolume(bagStack);

		if (volume + desc.getType().getVolume() * stack.stackSize <= BAG_VOLUME) {
			setVolume(bagStack, volume + desc.getType().getVolume() * stack.stackSize);
			stack.stackSize = 0;
		} else {
			int countAdd = ((BAG_VOLUME - volume) / desc.getType().getVolume());
			setVolume(bagStack, volume + countAdd * desc.getType().getVolume());
			stack.stackSize -= countAdd;
		}

		if (stack.stackSize <= 0)
			event.setResult(Result.ALLOW);
	}

	private ItemStack findBag(InvWrapper itemHandler, Material mat) {
		for (int slot = 0; slot < itemHandler.getSlots(); slot++) {
			ItemStack bagStack = itemHandler.getStackInSlot(slot);
			if (bagStack == null)
				continue;
			if (bagStack.getItem() != this)
				continue;

			if (getMaterial(bagStack) == mat)
				return bagStack;
		}
		return null;
	}
} public class ItemTrowel extends Item implements ItemMaterial {

	public static boolean trowelsInCreative = true;

	public ItemTrowel() {
		setHasSubtypes(true);
	}

	@Override
	public void setMaterial(ItemStack stack, Material mat) {
		if (mat == null) {
			if (stack.hasTagCompound())
				stack.getTagCompound().removeTag("material");
		} else {
			if (!stack.hasTagCompound())
				stack.setTagCompound(new NBTTagCompound());
			stack.getTagCompound().setString("material", mat.getMaterialId());
		}
	}

	@Override
	public Material getMaterial(ItemStack stack) {
		if (stack.getTagCompound() != null && stack.getTagCompound().hasKey("material", NBT.TAG_STRING))
			return MaterialRegistry.get(stack.getTagCompound().getString("material"));
		else
			return null;
	}

	@Override
	public int getMetadata(ItemStack stack) {
		Material mat = getMaterial(stack);
		return mat == null ? 0 : MaterialRegistry.getMeta(mat);
	}

	@Override
	public int getDamage(ItemStack stack) {
		return getMetadata(stack);
	}

	public MaterialBlockType getCurrentBlockType(ItemStack stack) {
		if (stack.getTagCompound() == null) {
			Material result = getMaterial(stack);
			if (result == null)
				return null;
			setCurrentBlockType(stack, result.getBlockRotation().getFirst());
		}
		MaterialBlockType blockType =
				MaterialBlockType.getBlockType(stack.getTagCompound().getInteger("blockType"));
		Material mat = getMaterial(stack);
		if (mat != null && mat.getBlock(blockType) == null) {
			blockType = mat.getBlockRotation().getNext(blockType);
			setCurrentBlockType(stack, blockType);
		}
		if (mat != null && !blockType.isStackType()) {
			blockType = blockType.getStackType();
			setCurrentBlockType(stack, blockType);
		}
		return blockType;
	}

	public void setCurrentBlockType(ItemStack stack, MaterialBlockType blockType) {
		if (!stack.hasTagCompound())
			stack.setTagCompound(new NBTTagCompound());
		stack.getTagCompound().setInteger("blockType", blockType.ordinal());
	}

	public void setNextBlockRotation(ItemStack stack) {
		Material mat = getMaterial(stack);
		if (mat != null) {
			MaterialBlockType blockType = getCurrentBlockType(stack);
			blockType = mat.getBlockRotation().getNext(blockType);
			setCurrentBlockType(stack, blockType);
		}
	}

	public void setPrevBlockRotation(ItemStack stack) {
		Material mat = getMaterial(stack);
		if (mat != null) {
			MaterialBlockType blockType = getCurrentBlockType(stack);
			blockType = mat.getBlockRotation().getPrev(blockType);
			setCurrentBlockType(stack, blockType);
		}
	}

	@Override
	public ActionResult<ItemStack> onItemRightClick(ItemStack itemStackIn, World worldIn,
			EntityPlayer playerIn, EnumHand hand) {
		if (playerIn.isSneaking()) {
			playerIn.openGui(ModBuildingBricks.MODID, GuiTrowel.ID, worldIn,
					MathHelper.floor_double(playerIn.posX), MathHelper.floor_double(playerIn.posY),
					MathHelper.floor_double(playerIn.posZ));
		}
		return super.onItemRightClick(itemStackIn, worldIn, playerIn, hand);
	}

	@Override
	public EnumActionResult onItemUse(ItemStack stack, EntityPlayer player, World world, BlockPos pos,
			EnumHand hand, EnumFacing side, float hitX, float hitY, float hitZ) {
		Material mat = getMaterial(stack);
		if (mat == null)
			return super.onItemUse(stack, player, world, pos, hand, side, hitX, hitY, hitZ);
		else {
			MaterialBlockType blockType = getCurrentBlockType(stack);
			ItemStack useStack = mat.getBlock(blockType).getStack().copy();
			MaterialItemStackConsumer consumer =
					new MaterialItemStackConsumer(blockType, mat, new PlayerMainInvWrapper(player.inventory));
			if (!player.capabilities.isCreativeMode && consumer.failed())
				return EnumActionResult.FAIL;
			if (useStack.onItemUse(player, world, pos, hand, side, hitX, hitY, hitZ) == EnumActionResult.FAIL)
				return EnumActionResult.FAIL;
			if (!player.capabilities.isCreativeMode)
				consumer.apply(world, player.getPosition());
			return EnumActionResult.SUCCESS;
		}
	}

	@Override
	public String getItemStackDisplayName(ItemStack stack) {
		Material mat = getMaterial(stack);
		if (mat == null)
			return super.getItemStackDisplayName(stack);
		else
			return I18n.translateToLocalFormatted("item.buildingbricks.trowelBinded.name",
					mat.getLocalizedName());
	}

	public Container getContainer(EntityPlayer player) {
		return new GenericContainer()
				.addGenericSlots(44, 36, 1, 1, (slot, x, y) -> new SlotTrowelMaterial(player, slot, x, y))
				.addGenericSlots(98, 9, 4, 4, (slot, x, y) -> new SlotTrowelBlockType(player, slot, x, y))
				.addPlayerSlots(player.inventory, ImmutableSet.of(player.inventory.currentItem));
	}

	public ItemStack createStack() {
		return createStack(null);
	}

	public ItemStack createStack(Material mat) {
		if (mat == null)
			return new ItemStack(this);

		ItemStack stack = new ItemStack(this);
		setMaterial(stack, mat);
		return stack;
	}

	@Override
	public void getSubItems(Item itemIn, CreativeTabs tab, List<ItemStack> subItems) {
		subItems.add(createStack());
		if (trowelsInCreative) {
			subItems.addAll(
					MaterialRegistry.getAll().stream().map(this::createStack).collect(Collectors.toList()));
		}
	}
} public class RecipeBindTrowel extends ShapelessRecipes {
	public RecipeBindTrowel(Material mat, ItemStack stack) {
		super(ModBuildingBricks.trowel.createStack(mat), getIngredients(stack));
	}

	private static List<ItemStack> getIngredients(ItemStack stack) {
		ItemStack trowelStack = ModBuildingBricks.trowel.createStack();
		trowelStack.setItemDamage(OreDictionary.WILDCARD_VALUE);
		return Lists.newArrayList(trowelStack, stack);
	}

	public ItemStack[] getRemainingItems(InventoryCrafting inv) {
		ItemStack[] stacks = new ItemStack[inv.getSizeInventory()];

		for (int i = 0; i < stacks.length; ++i) {
			ItemStack stack = inv.getStackInSlot(i);
			stacks[i] = (stack != null && stack.getItem() != ModBuildingBricks.trowel) ?
					ItemHandlerHelper.copyStackWithSize(stack, 1) : null;
		}

		return stacks;
	}
} public static class MaterialBlockRecipeBuilder extends RecipeBuilder<MaterialBlockRecipeBuilder> {

	private Material mat;
	private MaterialBlockType type;

	public MaterialBlockRecipeBuilder bind(Material mat) {
		this.mat = mat;
		return this;
	}

	public MaterialBlockRecipeBuilder output(MaterialBlockType type) {
		this.type = type;
		return this;
	}

	@Override
	public IRecipe build() {
		output(mat.getBlock(type).getStack());
		return super.build();
	}

	@Override
	protected Object parseIngredient(String ingredient) {
		for (MaterialBlockType blockType : MaterialBlockType.values()) {
			if (blockType.toString().equals(ingredient)) {
				if (MaterialBlockRegistry.instance.enabledBlocks.get(blockType)) {
					return mat.getBlock(blockType).getStack();
				} else {
					throw new DisabledMaterialBlockRecipe();
				}
			}
		}
		return super.parseIngredient(ingredient);
	}

	public class DisabledMaterialBlockRecipe extends RuntimeException {
	}
} public class DisabledMaterialBlockRecipe extends RuntimeException {
} public class MaterialResourceLoader {
	private static Set<String> domains = new HashSet<>();

	public static void loadResources(ResourceScanner scanner) {
		for (ResourceLocation materialResLoc : scanner.scan("materials")) {
			if (domains.contains(materialResLoc.getResourceDomain())) {
//					isModLoaded(materialResLoc.getResourceDomain())) {
				try {
					for (InputStream matStream : scanner.getAllResources(materialResLoc)) {
						try (InputStream stream = matStream) {
							MaterialParser.loadMaterialFromStream(stream);
						}
					}
				} catch (Exception e) {
					throw new RuntimeException("Could not load material from " + materialResLoc, e);
				}
			}
		}
		for (MaterialBuilderSimple builder : MaterialParser.materials.values()) {
			Material mat = builder.build();
			if (mat == null)
				continue;
			for (MaterialBlockType blockType : mat.getStructureMaterial().getBlockTypes()) {
				if (mat.getBlock(blockType) == null) {
					mat.addBlock(BlockDescription.getTemplate(blockType,mat.getStructureMaterial() ));
				}
			}
			MaterialRegistry.registerMaterial(mat);
		}
	}

	public static void addDomain(String domain) {
		domains.add(domain);
	}

	protected static boolean isModLoaded(String name) {
		for (ModContainer mod : Loader.instance().getActiveModList()) {
			if (mod.getModId().toLowerCase().equals(name)) {
				return Loader.instance().getModState(mod) != LoaderState.ModState.DISABLED;
			}
		}
		return false;
	}
} public class TrowelRotateBlockTypeMessage implements IMessage {

	public static void send(boolean forward, MaterialBlockType blockType) {
		ModBuildingBricks.proxy.getNetChannel()
				.sendToServer(new TrowelRotateBlockTypeMessage(forward, blockType));
	}

	public static class Handler implements IMessageHandler<TrowelRotateBlockTypeMessage, IMessage> {

		@Override
		public IMessage onMessage(TrowelRotateBlockTypeMessage message, MessageContext ctx) {
			EntityPlayerMP player = ctx.getServerHandler().playerEntity;
			HeldEquipment equipment = PlayerUtil.getHeldEquipment(player, ModBuildingBricks.trowel);
			if (equipment != null && equipment.getStack().getItem() != null) {
				if (message.blockType != null) {
					ModBuildingBricks.trowel.setCurrentBlockType(equipment.getStack(), message.blockType);
				} else {
					if (message.forward) {
						ModBuildingBricks.trowel.setNextBlockRotation(equipment.getStack());
					} else {
						ModBuildingBricks.trowel.setPrevBlockRotation(equipment.getStack());
					}
				}
			}
			return null;
		}
	}

	private boolean forward;
	private MaterialBlockType blockType;

	public TrowelRotateBlockTypeMessage() {
	}

	private TrowelRotateBlockTypeMessage(boolean forward, MaterialBlockType blockType) {
		this.forward = forward;
		this.blockType = blockType;
	}

	@Override
	public void fromBytes(ByteBuf buf) {
		forward = buf.getBoolean(0);
		short blockTypeId = buf.getShort(1);
		blockType = blockTypeId >= 0 ? MaterialBlockType.getBlockType(blockTypeId) : null;
	}

	@Override
	public void toBytes(ByteBuf buf) {
		buf.writeBoolean(forward);
		buf.writeShort(blockType != null ? blockType.ordinal() : -1);
	}
} public class TileMaterial extends TileEntity {

	private static final Logger logger = LogManager.getLogger("BuildingBricks.TileMaterial");

	public static final PropertyMaterial MATERIAL = new PropertyMaterial();

	public static boolean blocksInCreative = true;

	private Material material;
	private String materialId;

	public TileMaterial() {
		super();
	}

	public TileMaterial(IExtendedBlockState state) {
		this();
		setMaterial(state.getValue(MATERIAL));
	}

	public Material getMaterial() {
		if (material != null)
			return material;
		else
			return null;
	}

	public void setMaterial(Material material) {
		if (material != null)
			materialId = material.getMaterialId();
		else
			logger.error("The material of a block was directly set to null");
		this.material = material;
	}

	public static IExtendedBlockState setStateMaterial(IExtendedBlockState state, Material mat) {
		return state.withProperty(MATERIAL, mat);
	}

	public static TileMaterial getTile(IBlockAccess world, BlockPos pos) {
		TileMaterial tile;
		TileEntity te = world.getTileEntity(pos);
		if (!(te instanceof TileMaterial))
			return null;
		tile = (TileMaterial) te;
		return tile;
	}

	private NBTTagCompound serializeMaterialData(NBTTagCompound nbt) {
		if (materialId != null)
			nbt.setString("material", materialId);
		return nbt;
	}

	private void deserializeMaterialData(NBTTagCompound nbt) {
		if (nbt.hasKey("material", NBT.TAG_STRING)) {
			String matId = nbt.getString("material");
			if (!matId.contains(":"))
				matId = "minecraft:" + matId;
			materialId = matId;
			setMaterial(MaterialRegistry.get(matId));
		} else {
			logger.error("Could not load the material of a block");
		}
	}

	@Override
	public NBTTagCompound writeToNBT(NBTTagCompound nbt) {
		super.writeToNBT(nbt);

		nbt = serializeMaterialData(nbt);
		return nbt;
	}

	@Override
	public void readFromNBT(NBTTagCompound nbt) {
		super.readFromNBT(nbt);

		deserializeMaterialData(nbt);
	}

	@Override
	public boolean shouldRefresh(World world, BlockPos pos, IBlockState oldState, IBlockState newSate) {
		return oldState.getBlock() != newSate.getBlock();
	}

	@Override
	public NBTTagCompound getTileData() {
		return serializeMaterialData(new NBTTagCompound());
	}

	@Override
	public NBTTagCompound getUpdateTag() {
		NBTTagCompound nbt = super.getUpdateTag();
		nbt = serializeMaterialData(nbt);
		return nbt;
	}

	@Override
	public void handleUpdateTag(NBTTagCompound nbt) {
		deserializeMaterialData(nbt);
	}

	@Nullable
	@Override
	public SPacketUpdateTileEntity getUpdatePacket() {
		return new SPacketUpdateTileEntity(pos, 0, serializeMaterialData(new NBTTagCompound()));
	}

	@Override
	public void onDataPacket(NetworkManager net, SPacketUpdateTileEntity pkt) {
		deserializeMaterialData(pkt.getNbtCompound());
	}

	public static BlockStateContainer createBlockState(BlockStateContainer superState) {
		Collection props = superState.getProperties();
		return new ExtendedBlockState(superState.getBlock(),
				(IProperty[]) props.toArray(new IProperty[props.size()]),
				new IUnlistedProperty[] {TileMaterial.MATERIAL});
	}

	public static IBlockState getExtendedState(IBlockState state, IBlockAccess world, BlockPos pos) {
		TileMaterial tile = getTile(world, pos);
		if (tile == null || tile.getMaterial() == null)
			return state;
		return TileMaterial.setStateMaterial((IExtendedBlockState) state, tile.getMaterial());
	}

	public static IBlockState onBlockPlaced(IBlockState state, World worldIn, BlockPos pos, EnumFacing facing,
			float hitX, float hitY,
			float hitZ, int meta, EntityLivingBase placer) {
		state = ((IExtendedBlockState) state).withProperty(MATERIAL, MaterialRegistry.getFromMeta(meta));
		return state;
	}

	public static void onBlockPlacedBy(Block block, World world, BlockPos pos, IBlockState state,
			EntityLivingBase placer, ItemStack stack) {
		TileMaterial te = TileMaterial.getTile(world, pos);
		if (te != null)
			te.setMaterial(MaterialRegistry.getMaterialForStack(stack));
	}

	public static ItemStack getPickBlock(Block block, RayTraceResult target, World world, BlockPos pos) {
		TileMaterial te = TileMaterial.getTile(world, pos);
		if (te == null)
			return null;
		Material mat = te.getMaterial();
		if (mat == null)
			return null;
		MaterialBlockType blockType = ((BlockBuildingBricks) block).getBlockLogic().getBlockType();
		return mat.getBlock(blockType.getStackType()).getStack().copy();
	}

	public static void getSubBlocks(Block block, Item item, CreativeTabs tab, List<ItemStack> list) {
		if (blocksInCreative) {
			for (Material mat : MaterialBlockRegistry.instance.getBlockMaterials(block)) {
				ItemStack stack = new ItemStack(item);
				MaterialStack.set(stack, mat);
				list.add(stack);
			}
		}
	}

	public static ItemStack getHarvestBlock(World world, BlockPos pos, EntityPlayer player) {
		Block block = world.getBlockState(pos).getBlock();
		if (!(block instanceof BlockBuildingBricks))
			return null;
		MaterialBlockType blockType = ((BlockBuildingBricks) block).getBlockLogic().getBlockType();

		TileMaterial te = TileMaterial.getTile(world, pos);
		if (te == null)
			return null;
		Material mat = te.getMaterial();
		if (mat == null)
			return null;

		boolean silk = mat.getSilkHarvestMaterial() != null &&
				EnchantmentHelper.getEnchantedItem(Enchantments.SILK_TOUCH, player) != null;
		String harvestMatId = silk ? mat.getSilkHarvestMaterial() : mat.getNormalHarvestMaterial();
		if (harvestMatId == null)
			return null;
		Material itemMat = MaterialRegistry.get(harvestMatId);
		if (itemMat == null)
			return null;

		return itemMat.getBlock(blockType.getStackType()).getStack().copy();
	}
}
import javax.vecmath.Vector3f;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.util.Optional;
import org.lwjgl.input.Keyboard;
import net.minecraft.block.Block;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Gui;
import net.minecraft.client.renderer.color.IBlockColor;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Items;
import net.minecraft.inventory.Container;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.config.ConfigCategory;
import net.minecraftforge.common.config.Property.Type;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import net.minecraftforge.oredict.RecipeSorter;
import net.minecraftforge.oredict.RecipeSorter.Category;
import com.hea3ven.buildingbricks.core.block.BlockPortableLadder;
import com.hea3ven.buildingbricks.core.block.base.BlockBuildingBricks;
import com.hea3ven.buildingbricks.core.block.base.BlockMaterial;
import com.hea3ven.buildingbricks.core.block.placement.BlockPlacementManager;
import com.hea3ven.buildingbricks.core.client.ModelBakerBlockMaterial;
import com.hea3ven.buildingbricks.core.client.ModelBakerItemMaterial;
import com.hea3ven.buildingbricks.core.client.gui.GuiMaterialBag;
import com.hea3ven.buildingbricks.core.client.gui.GuiTrowel;
import com.hea3ven.buildingbricks.core.client.settings.TrowelKeyBindings;
import com.hea3ven.buildingbricks.core.command.CommandCreateMaterial;
import com.hea3ven.buildingbricks.core.eventhandlers.EventHandlerShowMaterialTooltip;
import com.hea3ven.buildingbricks.core.eventhandlers.EventHandlerTrowelOverlay;
import com.hea3ven.buildingbricks.core.item.ItemMaterialBag;
import com.hea3ven.buildingbricks.core.item.ItemTrowel;
import com.hea3ven.buildingbricks.core.item.crafting.RecipeBindTrowel;
import com.hea3ven.buildingbricks.core.materials.*;
import com.hea3ven.buildingbricks.core.materials.MaterialBlockRecipes.MaterialBlockRecipeBuilder;
import com.hea3ven.buildingbricks.core.materials.MaterialBlockRecipes.MaterialBlockRecipeBuilder.DisabledMaterialBlockRecipe;
import com.hea3ven.buildingbricks.core.materials.loader.MaterialResourceLoader;
import com.hea3ven.buildingbricks.core.network.TrowelRotateBlockTypeMessage;
import com.hea3ven.buildingbricks.core.tileentity.TileMaterial;
import com.hea3ven.tools.commonutils.client.renderer.color.IColorHandler;
import com.hea3ven.tools.commonutils.inventory.ISimpleGuiHandler;
import com.hea3ven.tools.commonutils.mod.ProxyModComposite;
import com.hea3ven.tools.commonutils.mod.config.DirectoryConfigManagerBuilder;
import com.hea3ven.tools.commonutils.mod.config.FileConfigManagerBuilder;
import com.hea3ven.tools.commonutils.util.ConfigurationUtil;
import com.hea3ven.tools.commonutils.util.SidedCall;
import com.hea3ven.tools.commonutils.util.SidedCall.SidedRunnable;
		MinecraftForge.EVENT_BUS.register(ModBuildingBricks.materialBag);
		MinecraftForge.EVENT_BUS.register(BlockPlacementManager.getInstance());
		SidedCall.run(Side.CLIENT, new SidedRunnable() {
			@Override
			@SideOnly(Side.CLIENT)
			public void run() {
				MinecraftForge.EVENT_BUS.register(new EventHandlerTrowelOverlay());
			}
		});
	}

	@Override
	public void registerConfig() {
		addConfigManager(new DirectoryConfigManagerBuilder().setDirName("BuildingBricks")
				.addFile(new FileConfigManagerBuilder().setFileName("general.cfg")
						.setDesc("Building Bricks Configuration")
						.addCategory("general")
						.addValue("blocksInCreative", "true", Type.BOOLEAN,
								"Enable to add all the generated blocks to the creative menu", property -> {
									TileMaterial.blocksInCreative = property.getBoolean();
								}, false, false)
						.addValue("trowelsInCreative", "true", Type.BOOLEAN,
								"Enable to add binded trowels for each material to the creative menu",
								property -> {
									ItemTrowel.trowelsInCreative = property.getBoolean();
								}, false, false)
						.addValue("showMaterialInTooltip", "true", Type.BOOLEAN,
								"Display the material on the blocks' tooltips", property -> {
									if (property.getBoolean())
										MinecraftForge.EVENT_BUS.register(
												EventHandlerShowMaterialTooltip.getInstance());
									else
										MinecraftForge.EVENT_BUS.unregister(
												EventHandlerShowMaterialTooltip.getInstance());
								})
						.endCategory()
						.addCategory("blocks")
						.addValue("enable", "true", Type.BOOLEAN,
								"Enable to generate the missing blocks for the materials", property -> {
									MaterialBlockRegistry.instance.enableGenerateBlocks =
											property.getBoolean();
								}, true, true)
						.addValues(cat -> {
							for (final MaterialBlockType blockType : MaterialBlockType.values()) {
								String name = "enableGenerate" +
										Character.toUpperCase(blockType.getName().charAt(0)) +
										blockType.getName().substring(1);
								cat = cat.addValue(name, "true", Type.BOOLEAN,
										"Enable to generate blocks of " + blockType.getName() + " type",
										property -> {
											MaterialBlockRegistry.instance.enabledBlocks.put(blockType,
													property.getBoolean());
										});
							}
						})
						.endCategory()
						.addCategory("compat")
						.add(this.getModule("compatvanilla").getConfig())
						.endCategory()
						.Update(cfg -> {
							ConfigCategory worldCat = cfg.getCategory("world");
							ConfigCategory blocksCat = cfg.getCategory("blocks");
							ConfigCategory generalCat = cfg.getCategory("general");
							ConfigCategory vanillaCompatCat =
									ConfigurationUtil.getSubCategory(cfg.getCategory("compat"), "vanilla");
							assert vanillaCompatCat != null;
							if (worldCat.containsKey("generateGrassSlabs")) {
								vanillaCompatCat.get("generateGrassSlabs")
										.set(worldCat.get("generateGrassSlabs").getBoolean());
							}
							if (generalCat.containsKey("replaceGrassTexture")) {
								vanillaCompatCat.get("replaceGrassTexture")
										.set(generalCat.get("replaceGrassTexture").getBoolean());
								vanillaCompatCat.get("replaceGrassTextureForce")
										.set(generalCat.get("replaceGrassTexture").getBoolean());
							}
							if (generalCat.containsKey("generateBlocks")) {
								blocksCat.get("enable").set(generalCat.get("generateBlocks").getBoolean());
							}

							cfg.removeCategory(worldCat);
							generalCat.remove("replaceGrassTexture");
							generalCat.remove("generateBlocks");
						})));
	}

	@Override
	protected void registerBlocks() {
		addBlock(ModBuildingBricks.portableLadder, "portable_ladder",
				new BlockPortableLadder.ItemPortableLadder(ModBuildingBricks.portableLadder));
	}

	@Override
	protected void registerTileEntities() {
		addTileEntity(TileMaterial.class, "tile.material");
	}

	@Override
	protected void registerItems() {
		addItem(ModBuildingBricks.trowel, "trowel");
		addItem(ModBuildingBricks.materialBag, "material_bag");
	}

	@Override
	protected void registerCreativeTabs() {
		addCreativeTab("buildingBricks", ModBuildingBricks.trowel);
		CreativeTabs tab = getCreativeTab("buildingBricks");
		ModBuildingBricks.trowel.setCreativeTab(tab);
		ModBuildingBricks.materialBag.setCreativeTab(tab);
		ModBuildingBricks.portableLadder.setCreativeTab(tab);
		for (Block block : MaterialBlockRegistry.instance.getAllBlocks()) {
			block.setCreativeTab(tab);
		}
	}

	@Override
	@SideOnly(Side.CLIENT)
	protected void registerModelBakers() {
		addModelBaker(new ModelBakerBlockMaterial());
		addModelBaker(
				new ModelBakerItemMaterial("buildingbricks:item/trowel", "buildingbricks:trowel#inventory",