public class CopygunItem extends Item{

	public CopygunItem(){
		this.setCreativeTab(ModMinePainter.tabMinePainter);
		this.setUnlocalizedName("copygun");
		this.setTextureName("minepainter:copygun");
		this.setMaxStackSize(1);
		this.setMaxDamage(512);
	}
	
	@Override public boolean getShareTag(){
        return true;
    }
	
	@Override
    public boolean onItemUse(ItemStack is, EntityPlayer ep, World w, int x, int y, int z, int face, float xs, float ys, float zs) {
		Block b = w.getBlock(x, y, z);
		if(b != ModMinePainter.sculpture.block){
			int meta = w.getBlockMetadata(x, y, z);
			if(b != Blocks.air && Operations.sculptable(b, meta)){
				int block_sig = Block.getIdFromBlock(b) << 4;
				block_sig += meta;
				
				int prev = getCharge(is, block_sig);
				if(prev + 512 > Short.MAX_VALUE)return false;
				
				setCharge(is, block_sig, prev  + 512);
				return w.setBlockToAir(x, y, z);
			}
			return false;
		}
		
		SculptureEntity se = Utils.getTE(w, x, y, z);
		Sculpture sculpture = se.sculpture();
		
		 if(sculpture.isEmpty())return false;
		 int[][] sigs = sculpture.getBlockSigs();
		 for(int i = 0; i < sigs[0].length; i ++){
			 if(sigs[0][i] == 0)break;
			 if(getCharge(is, sigs[0][i]) < sigs[1][i])return false;
		 }
		 for(int i = 0; i < sigs[0].length; i ++){
			 if(sigs[0][i] == 0)break;
			 int sig = sigs[0][i];
			 int count = sigs[1][i];
			 setCharge(is, sig, getCharge(is, sig) - count);
		 }
		 
		 if(!ep.capabilities.isCreativeMode)
			 is.damageItem(1, ep);
		
		return ModMinePainter.sculpture.block.dropSculptureToPlayer(w, ep, x, y, z);
	}
	
	public int getCharge(ItemStack is, int block_sig){
		NBTTagCompound nbt = is.getTagCompound();
		if(nbt == null)return 0;
		String key = "bs:" + block_sig;
		if(!nbt.hasKey(key))return 0;
		return nbt.getShort(key);
	}
	
	public void setCharge(ItemStack is, int block_sig, int count){
		NBTTagCompound nbt = is.getTagCompound();
		if(nbt == null)is.setTagCompound(nbt = new NBTTagCompound());
		String key = "bs:" + block_sig;
		nbt.setShort(key, (short) count);
	}
	
	@Override public void addInformation(ItemStack is, EntityPlayer ep, List list, boolean help)
	{
		NBTTagCompound nbt = is.getTagCompound();
		if(nbt == null)return;
		
		for(Object key : nbt.func_150296_c()){
			String str = (String)key;
			if(str.startsWith("bs:")){
				short sig = Short.parseShort(str.substring(3));
				Block block = Block.getBlockById(sig >>> 4);
				int meta = sig & 0xf;
				list.add(new ItemStack(block,1,meta).getDisplayName() + " x " + nbt.getShort(str));
			}
		}
	}
} public class DroppedSculptureItem extends Item{

	public DroppedSculptureItem(){
		super();
		this.setUnlocalizedName("dropped_sculpture");
	}
	
	public void readTo(ItemStack is, Sculpture sculpture){
		if(is.hasTagCompound())
			sculpture.read(is.getTagCompound());
	}
	
	@SideOnly(Side.CLIENT)
	@Override public void registerIcons(IIconRegister r){}
	
	@Override public boolean getShareTag(){
        return true;
    }
	
	@Override public boolean isValidArmor(ItemStack stack, int armorType, Entity entity){
		return armorType == 0;
	}
	
	@Override public boolean onItemUse(ItemStack is, EntityPlayer ep, World w, int x, int y, int z, int face, float xs, float ys, float zs) {

		if(!w.getBlock(x, y, z).getMaterial().isSolid())return false;
		 
        ForgeDirection dir = ForgeDirection.getOrientation(face);
        int _x = x + dir.offsetX;
        int _y = y + dir.offsetY;
        int _z = z + dir.offsetZ;
		
        if(!w.isAirBlock(_x, _y, _z))return false;
        if(!ep.canPlayerEdit(x,y,z,face,is))return false;
		
        w.setBlock(_x, _y, _z, ModMinePainter.sculpture.block);
        SculptureEntity se = Utils.getTE(w, _x, _y, _z);
        se.sculpture().read(is.getTagCompound());
        SculptureBlock.applyPlayerRotation(se.sculpture().getRotation(), ep, false);
        
        if(!ep.capabilities.isCreativeMode)
        	is.stackSize--;
		
        return true;
	}

} @SideOnly(Side.CLIENT)
public class DroppedSculptureRenderer implements IItemRenderer{
	SculptureRenderBlocks rb = new SculptureRenderBlocks();
	RenderItem renderItem = new RenderItem();
	ItemStack is;

	ExpirablePool<ItemStack, CompiledRender> renders = new ExpirablePool<ItemStack, CompiledRender>(12){

		@Override
		public void release(CompiledRender v) {
			v.clear();
		}

		@Override
		public CompiledRender get() {
			return new CompiledRender();
		}
		
	};
	
	
	@Override
	public boolean handleRenderType(ItemStack item, ItemRenderType type) {
//		return false;
		return type == ItemRenderType.INVENTORY ||
				type == ItemRenderType.ENTITY ||
				type == ItemRenderType.EQUIPPED ||
				type == ItemRenderType.EQUIPPED_FIRST_PERSON;
	}

	@Override
	public boolean shouldUseRenderHelper(ItemRenderType type, ItemStack item,
			ItemRendererHelper helper) {
		return type == ItemRenderType.ENTITY || helper == ItemRendererHelper.EQUIPPED_BLOCK;
	}

	@Override
	public void renderItem(ItemRenderType type, ItemStack item, Object... data) {
		CompiledRender cr = renders.get(item);
		if(!cr.compiled(type))cr.compile(item.getTagCompound(),type,data);
		if(!cr.compiled(type))return;
		TextureManager tm = Minecraft.getMinecraft().renderEngine;
		tm.bindTexture(TextureMap.locationBlocksTexture);
		GL11.glCallList(cr.glDispList);
	}

	private class CompiledRender{
		
		int glDispList = -1;
		ItemRenderType type = null;
		Sculpture sculpture = new Sculpture();
		
		public boolean compiled(ItemRenderType type){
			return glDispList >= 0 && this.type == type;
		}
		
		public void clear(){
			if(glDispList >= 0)
				GLAllocation.deleteDisplayLists(glDispList);
		}
		
		public void compile(NBTTagCompound nbt, ItemRenderType type, Object... data){
			this.type = type;
			if(nbt == null)return;
			sculpture.read(nbt);
			
			if(glDispList < 0)glDispList = GLAllocation.generateDisplayLists(1);
			if(is == null)is = new ItemStack(ModMinePainter.sculpture.block);
			
			GL11.glNewList(glDispList, GL11.GL_COMPILE);
			TextureManager tm = Minecraft.getMinecraft().renderEngine;
			tm.bindTexture(TextureMap.locationBlocksTexture);
			SculptureBlock sb = ModMinePainter.sculpture.block;
			
			if(type == ItemRenderType.INVENTORY){
				RenderHelper.enableGUIStandardItemLighting();
			}else if(type == ItemRenderType.EQUIPPED ||
				type == ItemRenderType.EQUIPPED_FIRST_PERSON){
				GL11.glTranslatef(0.5f, 0.5f, 0.5f);
			}else if(type == ItemRenderType.ENTITY){
				GL11.glTranslatef(0, 0.5f, 0f);
			}
			
			for(int i = 0; i < 512; i ++){
				int x = (i >> 6) & 7;
				int y = (i >> 3) & 7;
				int z = (i >> 0) & 7;
				
				if(sculpture.getBlockAt(x, y, z, null) == Blocks.air)continue;
				
				sb.setCurrentBlock(sculpture.getBlockAt(x, y, z, null), sculpture.getMetaAt(x, y, z, null));
				sb.setBlockBounds(x/8f, y/8f, z/8f, (x+1)/8f, (y+1)/8f, (z+1)/8f);
				
				if(type == ItemRenderType.INVENTORY){
//					renderItem.renderItemIntoGUI(
//							Minecraft.getMinecraft().fontRenderer,
//							Minecraft.getMinecraft().renderEngine, is, 0, 0);
					GL11.glPushMatrix();
			        GL11.glEnable(GL11.GL_BLEND);
			        OpenGlHelper.glBlendFunc(770, 771, 1, 0);
		            GL11.glTranslatef(-2f, 3f, -3.0F + 50);
		            GL11.glScalef(10.0F, 10.0F, 10.0F);
		            GL11.glTranslatef(1.0F, 0.5F, 1.0F);
		            GL11.glScalef(1.0F, 1.0F, -1.0F);
		            GL11.glRotatef(210.0F, 1.0F, 0.0F, 0.0F);
		            GL11.glRotatef(45.0F, 0.0F, 1.0F, 0.0F);
		            GL11.glRotatef(-90.0F, 0.0F, 1.0F, 0.0F);
					rb.cull(sculpture, x, y, z);
		            rb.renderBlockAsItem(sb, 0, 1.0F);
		            GL11.glEnable(GL11.GL_CULL_FACE);
		            GL11.glPopMatrix();
				}else {
					GL11.glPushMatrix();
					rb.cull(sculpture, x, y, z);
					rb.renderBlockAsItem(sb, 0, 1f);
					GL11.glPopMatrix();
				}
			}
			
			GL11.glEndList();
			
			sb.setCurrentBlock(null, 0);
			sb.setBlockBounds(0,0,0,1,1,1);
		}
	}
} public class CanvasItem extends Item{

	public CanvasItem(){
		super();
		this.setCreativeTab(ModMinePainter.tabMinePainter);
		this.setFull3D();
		this.setUnlocalizedName("canvas");
		this.setTextureName("minepainter:canvas");
	}
	
	@Override public boolean getShareTag(){
        return true;
    }
	
	@Override public boolean isValidArmor(ItemStack stack, int armorType, Entity entity){
		return armorType == 0;
	}
	
	 @Override
    public boolean onItemUse(ItemStack is, EntityPlayer ep, World w, int x, int y, int z, int face, float xs, float ys, float zs) {

		if(!w.getBlock(x, y, z).getMaterial().isSolid())return false;
		 
        ForgeDirection dir = ForgeDirection.getOrientation(face);
        int _x = x + dir.offsetX;
        int _y = y + dir.offsetY;
        int _z = z + dir.offsetZ;
		
        if(!w.isAirBlock(_x, _y, _z))return false;
        if(!ep.canPlayerEdit(x,y,z,face,is))return false;
		 
        PaintingPlacement pp = PaintingPlacement.of(ep.getLookVec(), face);
        w.setBlock(_x,_y,_z,ModMinePainter.painting.block, pp.ordinal(), 3);
        PaintingEntity pe = Utils.getTE(w, _x, _y, _z);
        pe.readFromNBTToImage(is.getTagCompound());
        
        if(!ep.capabilities.isCreativeMode)
        	is.stackSize--;
        
        return true;
	}
	 
	
} @SideOnly(Side.CLIENT)
public class CanvasRenderer implements IItemRenderer{
	public static boolean overrideUseRenderHelper = false;
	
    @Override
    public boolean handleRenderType(ItemStack item, ItemRenderType type) {
        return type == ItemRenderType.INVENTORY ||
        		type == ItemRenderType.EQUIPPED ||
        		type == ItemRenderType.EQUIPPED_FIRST_PERSON ||
        		type == ItemRenderType.ENTITY;
    }

    @Override
    public boolean shouldUseRenderHelper(ItemRenderType type, ItemStack item, ItemRendererHelper helper) {
    	if(overrideUseRenderHelper)return true;
    	if(type == ItemRenderType.ENTITY){
    		return helper == ItemRendererHelper.ENTITY_ROTATION ||
    			   helper == ItemRendererHelper.ENTITY_BOBBING;
    	}
        return false;
    }

    @Override
    public void renderItem(ItemRenderType type, ItemStack item, Object... data) {
        
    	IIcon icon = ModMinePainter.canvas.item.getIconFromDamage(0);
    	if(item.hasTagCompound()){
    		PaintingIcon pi = PaintingCache.get(item);
    		GL11.glBindTexture(GL11.GL_TEXTURE_2D, pi.glTexId());
    		icon = pi;
    	}
        if(type == ItemRenderType.INVENTORY)
        	renderInventory(icon);
        else if(type == ItemRenderType.EQUIPPED || type == ItemRenderType.EQUIPPED_FIRST_PERSON)
        	renderEquipped(icon);
        else{
        	GL11.glTranslatef(-0.5f, 0, 0);
        	renderEquipped(icon);
        }
    }
    
    private void renderInventory(IIcon icon)
    {
        Tessellator tes = Tessellator.instance;
        tes.startDrawingQuads();
        tes.addVertexWithUV(1,  1,  0, icon.getMinU(), icon.getMinV());
        tes.addVertexWithUV(1,  15, 0, icon.getMinU(), icon.getMaxV());
        tes.addVertexWithUV(15, 15, 0, icon.getMaxU(), icon.getMaxV());
        tes.addVertexWithUV(15, 1,  0, icon.getMaxU(), icon.getMinV());
        tes.draw();
    }
    
    private void renderEquipped(IIcon icon)
    {
        Tessellator var5 = Tessellator.instance;
        float var7 = icon.getMinU();
        float var8 = icon.getMaxU();
        float var9 = icon.getMinV();
        float var10 = icon.getMaxV();
        ItemRenderer.renderItemIn2D(var5, var8, var9, var7, var10, 256, 256, 0.0625F);
    }
} public class ChiselItem extends Item{
	
	public ChiselItem(){
		super();
		this.setCreativeTab(ModMinePainter.tabMinePainter);
		this.setUnlocalizedName("chisel");
		this.setTextureName("minepainter:chisel");
		this.setMaxStackSize(1);
		this.setMaxDamage(960);
		this.setContainerItem(this);
		this.setFull3D();
	}
	
	@Override
    public boolean onItemUse(ItemStack is, EntityPlayer ep, World w, int x, int y, int z, int face, float xs, float ys, float zs) {
		if(!w.isRemote)return false;
		int[] pos = Operations.raytrace(x,y,z,ep); 
		
		int flags = this.getChiselFlags(ep);
		Block editBlock = this.getEditBlock(is);
		int editMeta = this.getEditMeta(is);
		if(!Operations.validOperation(w, x, y, z, pos, flags))
			return false;
		
		if(MinecraftServer.getServer() == null){
			boolean done = Operations.applyOperation(w, x, y, z, pos, flags,editBlock, editMeta);
			if(!done)return false;
		}
		
		ModMinePainter.network.sendToServer(new SculptureOperationMessage(pos,x,y,z,editBlock,editMeta,flags));
		
		w.playSoundEffect(x+0.5d, y+0.5d, z+0.5d, getEditBlock(is).stepSound.func_150496_b(), 0.5f, 0.5f);
		
		return true;
	}
	
	public Block getEditBlock(ItemStack is){
		return Blocks.air;
	}
	
	public int getEditMeta(ItemStack is){
		return 0;
	}
	
	public int getChiselFlags(EntityPlayer ep){
		return Operations.DAMAGE;
	}
	
	public static class Saw extends ChiselItem{

		public Saw(){
			super();
			this.setUnlocalizedName("saw");
			this.setTextureName("minepainter:saw");
		}

		@Override
		public int getChiselFlags(EntityPlayer ep){
			int axis = Operations.getLookingAxis(ep);
			switch(axis){
			case 0: return Operations.ALLY | Operations.ALLZ | Operations.DAMAGE;
			case 1: return Operations.ALLX | Operations.ALLZ | Operations.DAMAGE;
			case 2: return Operations.ALLX | Operations.ALLY | Operations.DAMAGE;
			}
			return 0;
		}
	}

	public static class Barcutter extends ChiselItem{
		
		public Barcutter(){
			super();
			this.setUnlocalizedName("barcutter");
			this.setTextureName("minepainter:shaver");
		}

		@Override
		public int getChiselFlags(EntityPlayer ep){
			int axis = Operations.getLookingAxis(ep);
			switch(axis){
			case 0: return Operations.ALLX | Operations.DAMAGE;
			case 1: return Operations.ALLY | Operations.DAMAGE;
			case 2: return Operations.ALLZ | Operations.DAMAGE;
			}
			return 0;
		}
	}

} public class HingeItem extends Item{

	public HingeItem(){
		this.setCreativeTab(ModMinePainter.tabMinePainter);
		this.setUnlocalizedName("hinge");
		this.setTextureName("minepainter:hinge");
		this.setMaxStackSize(16);
	}
	
	@Override
    public boolean onItemUse(ItemStack is, EntityPlayer ep, World w, int x, int y, int z, int face, float xs, float ys, float zs) {
		if(w.getBlock(x, y, z) != ModMinePainter.sculpture.block)return false;
		
		SculptureEntity se = Utils.getTE(w, x, y, z);
		
		se.setHinge(Hinge.placedAt(xs, ys, zs));
		
		if(w.isRemote)se.getRender().changed = true;
		else w.markBlockForUpdate(x, y, z);
		
		if(!ep.capabilities.isCreativeMode)is.stackSize--;
		
		w.playSoundEffect(x+0.5d, y+0.5d, z+0.5d, Blocks.iron_block.stepSound.getBreakSound(), 0.5f, 0.5f);
		
		return true;
	}
} public class Palette extends Item{
	
    private IIcon[] colors = new IIcon[6];

	public Palette() {
		super();
		setCreativeTab(ModMinePainter.tabMinePainter);
		this.setMaxStackSize(1);
		this.setTextureName("minepainter:palette");
		this.setUnlocalizedName("palette");
	}
	
        @Override
	public boolean requiresMultipleRenderPasses()
    {
        return true;
    }
	
        @Override
	public int getRenderPasses(int metadata)
	{
		return 7;
	}
	
        @Override
	public IIcon getIcon(ItemStack is, int renderPass)
	{
		if(renderPass == 0)return itemIcon;
		return colors[renderPass - 1];
	}
        
    @SideOnly(Side.CLIENT)
    @Override
    public void registerIcons(IIconRegister par1IconRegister)
    {
        super.registerIcons(par1IconRegister);
        for(int i =0;i<6;i++)colors[i]=par1IconRegister.registerIcon(this.getIconString() + i);
    }
	
        @Override
	public int getColorFromItemStack(ItemStack par1ItemStack, int par2)
	{
		int[] colors = getColors(par1ItemStack);
		
		if(par2 == 0)return super.getColorFromItemStack(par1ItemStack, par2);
		return colors[par2 - 1];
	}
	
	public static int[] getColors(ItemStack is)
	{
		NBTTagCompound nbt = is.getTagCompound();
		if(nbt == null) is.setTagCompound(nbt = new NBTTagCompound());
		
		NBTTagCompound palette = nbt.getCompoundTag("palette");
		int[] colors = palette.getIntArray("colors");
		if(colors.length == 0)colors =  new int[]{0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff};
		
		palette.setIntArray("colors", colors);
		nbt.setTag("palette", palette);
		
		return colors;
	}
	
        @Override
	public boolean onItemUse(ItemStack is, EntityPlayer ep, World w, int x, int y, int z, int par7, float _x, float _y, float _z)
	{
		if(w.getBlock(x, y, z) == ModMinePainter.painting.block)
		{
			int face = w.getBlockMetadata(x, y, z );
			PaintingEntity pe = Utils.getTE(w, x, y, z);
			PaintingPlacement pp = PaintingPlacement.of(face);
			float[] point = pp.block2painting(_x, _y, _z);
			
			int px = (int) (point[0] * 16 + 16) - 16;
			int py = (int) (point[1] * 16 + 16) - 16;
			if(px>15 || px<0 || py>15||py<0)return false;
			
			int[] colors = getColors(is); 
			colors[0] = pe.getImg().getRGB(px,py);
			setColors(is, colors);
			return true;
		}
		
		return false;
	}
	
	
        @Override
	public ItemStack onItemRightClick(ItemStack is, World w, EntityPlayer ep)
    {
		setColors(is, shift(getColors(is)));
        return is;
    } 
	
	public static int[] shift(int[] colors)
	{
		int t = colors[0];
		for(int i = 1; i < colors.length; i ++)
			colors[i-1] = colors[i];
		colors[colors.length-1]=t;
		
		return colors;
	}
	
	public static void setColors(ItemStack is, int[] colors)
	{
		NBTTagCompound nbt = is.getTagCompound();
		if(nbt == null) is.setTagCompound(nbt = new NBTTagCompound());
		
		NBTTagCompound palette = nbt.getCompoundTag("palette");
		palette.setIntArray("colors", colors);
		nbt.setTag("palette", palette);
	}
	
        @Override
	public void addInformation(ItemStack is, EntityPlayer ep, List list, boolean help)
	{
		int color = getColors(is)[0];
		list.add("Alpha : " 	+ ((color >> 24) & 0xff));
		list.add("\u00a7cRed : " 	+ ((color >> 16) & 0xff));
		list.add("\u00a7aGreen : " 	+ ((color >>  8) & 0xff));
		list.add("\u00a79Blue : " 	+ ((color >>  0) & 0xff));
	}
} public class PieceItem extends ChiselItem{

	public PieceItem(){
		this.setCreativeTab(null);
		this.setUnlocalizedName("sculpture_piece");
		this.setTextureName("");
		this.setHasSubtypes(true);
		this.setMaxStackSize(64);
		this.setMaxDamage(0);
		this.setContainerItem(null);
	}
	
	@Override public void registerIcons(IIconRegister r){}
	
	@Override
	public Block getEditBlock(ItemStack is){
		return Block.getBlockById( (is.getItemDamage() >> 4) & 0xfff);
	}
	
	@Override
	public int getEditMeta(ItemStack is){
		return is.getItemDamage() & 0xf;
	}
	
	@Override
	public int getChiselFlags(EntityPlayer ep){
		return Operations.PLACE | Operations.CONSUME;
	}
	
	public int getWorthPiece(){
		return 1;
	}
	
	public static class Bar extends PieceItem{
		@Override
		public int getChiselFlags(EntityPlayer ep){
			int axis = Operations.getLookingAxis(ep);
			switch(axis){
			case 0 : return Operations.PLACE | Operations.ALLX | Operations.CONSUME;
			case 1 : return Operations.PLACE | Operations.ALLY | Operations.CONSUME;
			case 2 : return Operations.PLACE | Operations.ALLZ | Operations.CONSUME;
			}
			return Operations.PLACE;
		}
		
		public int getWorthPiece(){
			return 8;
		}
	}
	
	public static class Cover extends PieceItem{
		@Override
		public int getChiselFlags(EntityPlayer ep){
			int axis = Operations.getLookingAxis(ep);
			switch(axis){
			case 0 : return Operations.PLACE | Operations.ALLY | Operations.ALLZ | Operations.CONSUME;
			case 1 : return Operations.PLACE | Operations.ALLX | Operations.ALLZ | Operations.CONSUME;
			case 2 : return Operations.PLACE | Operations.ALLX | Operations.ALLY | Operations.CONSUME;
			}
			return Operations.PLACE;
		}
		
		public int getWorthPiece(){
			return 64;
		}
	}
} public static class Bar extends PieceItem{
	@Override
	public int getChiselFlags(EntityPlayer ep){
		int axis = Operations.getLookingAxis(ep);
		switch(axis){
		case 0 : return Operations.PLACE | Operations.ALLX | Operations.CONSUME;
		case 1 : return Operations.PLACE | Operations.ALLY | Operations.CONSUME;
		case 2 : return Operations.PLACE | Operations.ALLZ | Operations.CONSUME;
		}
		return Operations.PLACE;
	}
		
	public int getWorthPiece(){
		return 8;
	}
} @SideOnly(Side.CLIENT)
public class PieceRenderer implements IItemRenderer {
	private RenderItem renderItem = new RenderItem();
	private ItemStack is;
	private Minecraft mc = Minecraft.getMinecraft();
	
	@Override
	public boolean handleRenderType(ItemStack item, ItemRenderType type) {
		return type == ItemRenderType.INVENTORY ||
				type == ItemRenderType.ENTITY ||
				type == ItemRenderType.EQUIPPED ||
				type == ItemRenderType.EQUIPPED_FIRST_PERSON;
	}

	@Override
	public boolean shouldUseRenderHelper(ItemRenderType type, ItemStack item,
			ItemRendererHelper helper) {
		return type == ItemRenderType.ENTITY || type == ItemRenderType.EQUIPPED_FIRST_PERSON;
	}

	@Override
	public void renderItem(ItemRenderType type, ItemStack item, Object... data) {
		
		if(is == null) is = new ItemStack(ModMinePainter.sculpture.block);
		SculptureBlock sculpture = ModMinePainter.sculpture.block;
		PieceItem piece = Utils.getItem(item); 
		sculpture.setCurrentBlock(piece.getEditBlock(item), piece.getEditMeta(item));
		setBounds(sculpture);
		
		if(type == ItemRenderType.INVENTORY)
		{
			RenderHelper.enableGUIStandardItemLighting();
			renderItem.renderItemIntoGUI(
					Minecraft.getMinecraft().fontRenderer,
					Minecraft.getMinecraft().renderEngine, is, 0, 0);
		}else if(type == ItemRenderType.ENTITY)
		{
			EntityItem eis = (EntityItem)data[1];
			GL11.glScalef(.5f,.5f,.5f);
			mc.renderEngine.bindTexture(TextureMap.locationBlocksTexture);
			RenderBlocks rb = (RenderBlocks) (data[0]);
			rb.renderBlockAsItem(sculpture, 0, 1f);

		}else
		{
			Minecraft.getMinecraft().entityRenderer.itemRenderer.renderItem((EntityLivingBase) data[1],
					is, 0, type);
		}
		
		sculpture.setCurrentBlock(null, 0);
		sculpture.setBlockBounds(0,0,0,1,1,1);
	}

	protected void setBounds(SculptureBlock sculpture){
		sculpture.setBlockBounds(0.3f, 0.3f, 0.3f, 0.7f, 0.7f, 0.7f);
	}
	
	public static class Bar extends PieceRenderer{
		@Override protected void setBounds(SculptureBlock sculpture){
			sculpture.setBlockBounds(0.3f, 0.0f, 0.3f, 0.7f, 1.0f, 0.7f);
		}
	}
	
	public static class Cover extends PieceRenderer{
		@Override protected void setBounds(SculptureBlock sculpture){
			sculpture.setBlockBounds(0.3f, 0.0f, 0.0f, 0.7f, 1.0f, 1.0f);
		}
	}
} public class TransmuterItem extends ChiselItem{
	
	
	public TransmuterItem(){
		this.setCreativeTab(ModMinePainter.tabMinePainter);
		this.setUnlocalizedName("transmuter");
		this.setTextureName("minepainter:transmuter");
		this.setMaxStackSize(1);
	}
	
	@Override public boolean getShareTag(){
        return true;
    }
	
	public int getBlockAndMeta(ItemStack is){
		NBTTagCompound nbt = is.getTagCompound();
		if(nbt == null)return 0;
		return nbt.getInteger("TBID");
	}
	
	public void setBlockAndMeta(ItemStack is, int tbid){
		NBTTagCompound nbt = is.getTagCompound();
		if(nbt == null)is.setTagCompound(nbt = new NBTTagCompound());
		nbt.setInteger("TBID", tbid);
	}
	
	public void addInformation(ItemStack is, EntityPlayer ep, List list, boolean thing){
		int tbid = this.getBlockAndMeta(is);
		Block block = Block.getBlockById(tbid >>> 4);
		int meta = tbid & 0xf;
		if(block == null)return;
		if(block == Blocks.air)return;
		list.add(new ItemStack(block,1,meta).getDisplayName());
	}
	
	
	public boolean onItemUse(ItemStack is, EntityPlayer ep, World w, int x, int y, int z, int face, float xs, float ys, float zs) {
		if(w.getBlock(x, y, z) == ModMinePainter.sculpture.block)
			return super.onItemUse(is, ep, w, x, y, z, face, xs, ys, zs);
	
		MovingObjectPosition mop = this.getMovingObjectPositionFromPlayer(w, ep, true);
		x = mop.blockX;
		y = mop.blockY;
		z = mop.blockZ;
		Block that = w.getBlock(x, y, z);
		if(that == null)return false;
		int meta = w.getBlockMetadata(x, y, z);
		this.setBlockAndMeta(is, (Block.getIdFromBlock(that) << 4) + meta);
		return true;
	}
	
	@Override
	public Block getEditBlock(ItemStack is){
		return Block.getBlockById( getBlockAndMeta(is)>>>4);
	}
	
	@Override
	public int getEditMeta(ItemStack is){
		return getBlockAndMeta(is) & 0xf;
	}
	
	@Override
	public int getChiselFlags(EntityPlayer ep){
		return Operations.TRANSMUTE;
	}
} public class WrenchItem extends Item{

	public WrenchItem(){
		this.setCreativeTab(ModMinePainter.tabMinePainter);
		this.setUnlocalizedName("wrench");
		this.setTextureName("minepainter:wrench");
		this.setMaxStackSize(1);
	}
	
	@Override
    public boolean onItemUse(ItemStack is, EntityPlayer ep, World w, int x, int y, int z, int face, float xs, float ys, float zs) {
		if(w.getBlock(x, y, z) != ModMinePainter.sculpture.block)return false;
		
		SculptureEntity se = Utils.getTE(w, x, y, z);
		
		if(ep.isSneaking())se.sculpture().getRotation().rotate(face);
		else se.sculpture().getRotation().rotate(face ^ 1);
		
		if(se.getHinge() != null){
			se.setHinge(null);
			ItemStack nis = new ItemStack(ModMinePainter.hinge.item);
    		ModMinePainter.sculpture.block.dropScrap(w, x, y, z, nis);
		}
		
		if(w.isRemote)se.getRender().changed = true;
		else w.markBlockForUpdate(x, y, z);
		
		w.playSoundEffect(x+0.5d, y+0.5d, z+0.5d, "tile.piston.out", 0.5f, 0.5f);
		
		return true;
	}
} public class CommandImportPainting extends CommandBase {
	private static LinkedBlockingQueue<Runnable> tasks = new LinkedBlockingQueue<Runnable>();
	private static Thread worker;
	
	@Override
	public String getCommandName() {
		return "mpimport";
	}

	@Override
	public String getCommandUsage(ICommandSender var1) {
		return "mpimport <image url> [--size <w> <h>]\n" +
				"to import image as w * h pieces of 16x16 paintings";
	}

	@Override
	public void processCommand(ICommandSender var1, String[] var2) {
		startWorking();
		int w = 1,h = 1;
		String url = var2[0];
		for(int i = 0 ; i < var2.length; i++){
			if(var2[i].equals("--size") && var2.length - i > 2){
				w = Integer.parseInt(var2[i+1]);
				h = Integer.parseInt(var2[i+2]);
			}
		}
		try {
			BufferedImage img = null;
			if(url.startsWith("http://")){
				img = ImageIO.read(new URL(url));
			}else{
				img = ImageIO.read(new File(url));
			}
			
			for(int i = 0; i < w;i ++)
				for(int j = 0; j < h; j++){
					
					BufferedImage slot = new BufferedImage(16,16, BufferedImage.TYPE_INT_ARGB);
					Graphics g = slot.getGraphics();
					g.drawImage(img, 0, 0, 16, 16, i * img.getWidth() / w, 
																    j * img.getHeight() / h, 
																    (i+1) * img.getWidth() / w, 
																    (j+1) * img.getHeight() / h, null);
					ByteArrayOutputStream baos = new ByteArrayOutputStream();
					ImageIO.write(slot, "png", baos);
					ItemStack is = new ItemStack(ModMinePainter.canvas.item);
			    	NBTTagCompound nbt = new NBTTagCompound();
			    	nbt.setByteArray("image_data", baos.toByteArray());
			    	is.setTagCompound(nbt);
			    	
			    	EntityItem entityitem = new EntityItem(var1.getEntityWorld(), 
			    			var1.getPlayerCoordinates().posX + 0.5f, 
			    			var1.getPlayerCoordinates().posY + 0.5f, 
			    			var1.getPlayerCoordinates().posZ + 0.5f, is);
		            entityitem.delayBeforeCanPickup = 0;
		            var1.getEntityWorld().spawnEntityInWorld(entityitem);
				}
			
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}

	private void startWorking(){
		if(worker == null || !worker.isAlive()){
			worker = new Thread(){
				@Override public void run(){
					while(true)
						try {
							tasks.take().run();
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
				}
			};
			worker.start();
		}
	}
} public class PaintTool extends Item{
	
	public PaintTool(){
		this.setCreativeTab(ModMinePainter.tabMinePainter);
		this.setMaxStackSize(1);
	}

	public boolean onItemUse(ItemStack is, EntityPlayer ep, World w, int x, int y, int z, int face, float xs, float ys, float zs) {
		
		if(!w.isRemote)return false;
		
		boolean changed = paintAt(w,x,y,z,xs,ys,zs,getColor(ep,is), ep.isSneaking());
		
		if(changed)ModMinePainter.network.sendToServer(new PaintingOperationMessage(this,x,y,z,xs,ys,zs,getColor(ep,is)));
		
		return changed;
	}
	
	public int getColor(EntityPlayer ep, ItemStack is){
		int size = ep.inventory.getSizeInventory();
		for(int i = 0; i < size; i ++){
			ItemStack slot = ep.inventory.getStackInSlot(i);
			if(slot == null)continue;
			if(!(slot.getItem() instanceof Palette))continue;
			
			return Palette.getColors(slot)[0];
		}
		return 0;
	}
	
	public boolean apply(BufferedImage img, float[] point, int color, boolean isSneaking) {
		return false;
	}
	
	public boolean inBounds(int x,int y){
		return x>=0 && x<16 && y>=0 && y<16;
	}
	
	public boolean paintAt(World w,int x,int y,int z,float xs,float ys,float zs,int color,boolean isSneaking){
		if(w.getBlock(x, y, z) != ModMinePainter.painting.block)return false;
		PaintingEntity pe = Utils.getTE(w, x, y, z);
		if(pe == null)return false;
		PaintingPlacement place = PaintingPlacement.of(w.getBlockMetadata(x, y, z));
		float[] point = place.block2painting(xs, ys, zs);
		
		boolean changed = false;
		for(int i = -1;i<=1;i++)
			for(int j = -1;j<=1;j++){
				int _x = x + place.xpos.offsetX * i + place.ypos.offsetX * j;
				int _y = y + place.xpos.offsetY * i + place.ypos.offsetY * j;
				int _z = z + place.xpos.offsetZ * i + place.ypos.offsetZ * j;
				
				if(w.getBlock(_x, _y, _z) != ModMinePainter.painting.block)continue;
				if(w.getBlockMetadata(_x, _y, _z) != place.ordinal())continue;
				
				PaintingEntity painting = Utils.getTE(w, _x, _y, _z);
				
				point[0] -= i;
				point[1] -= j;
				boolean _changed = apply(painting.image, point, color, isSneaking);
				point[0] += i;
				point[1] += j;
				
				if(_changed){
					if(w.isRemote)pe.getIcon().fill(pe.image);
					else w.markBlockForUpdate(_x, _y, _z);
					changed = true;
				}
			}
		return changed;
	}

	public static class Mini extends PaintTool{
		
		public Mini(){
			super();
			this.setUnlocalizedName("mini_brush");
			this.setTextureName("minepainter:brush_small");
		}
		
		@Override public boolean apply(BufferedImage img, float[] point, int color, boolean isSneaking){
			
			int x = (int) (point[0] * 16 + 16) - 16;
			int y = (int) (point[1] * 16 + 16) - 16;
			
			if(!inBounds(x,y))return false;
			
			img.setRGB(x, y, color);
			return true;
		}
	}
	
	public static class Bucket extends PaintTool{
		IIcon fill;
		public Bucket(){
			super();
			this.setUnlocalizedName("paint_bucket");
			this.setTextureName("minepainter:bucket");
			this.setHasSubtypes(true);
		}
		
		@Override public ItemStack onItemRightClick(ItemStack is, World w, EntityPlayer ep){
			MovingObjectPosition mop = this.getMovingObjectPositionFromPlayer(w, ep, true);
			if(mop == null)return is;
			Material m = w.getBlock(mop.blockX, mop.blockY, mop.blockZ).getMaterial();
			if(m.isLiquid())return new ItemStack(Items.bucket);
			return is;
		}
		
		@Override public boolean requiresMultipleRenderPasses(){
	        return true;
	    }
		
	    @Override public int getRenderPasses(int metadata){
			return 2;
		}
	    
	    @Override public IIcon getIcon(ItemStack is, int renderPass){
			if(renderPass == 0)return itemIcon;
			return fill;
		}
	    
	    @SideOnly(Side.CLIENT) @Override public void registerIcons(IIconRegister par1IconRegister){
	        super.registerIcons(par1IconRegister);
	        fill = par1IconRegister.registerIcon("minepainter:bucket_fill");
	    }
	    
	    @Override public int getColorFromItemStack(ItemStack par1ItemStack, int par2){
			if(par2==1)return getColor(par1ItemStack.getItemDamage(),0);
			return super.getColorFromItemStack(par1ItemStack, par2);
		}
	    
	    @Override public int getColor(EntityPlayer ep, ItemStack is){
	    	return getColor(is.getItemDamage(), 0xff000000);
	    }
	    
	    private int getColor(int dmg, int mask){
	    	if(dmg<16)return ItemDye.field_150922_c[dmg] | mask;
	    	return 0xffffff;
	    }
		
		@Override public boolean apply(BufferedImage img, float[] point, int color,boolean isSneaking){
			
			int x = (int) (point[0] * 16 + 16) - 16;
			int y = (int) (point[1] * 16 + 16) - 16;
			
			if(!inBounds(x,y))return false;
			
			int from_color = img.getRGB(x, y);
			
			for(int i = 0; i < 256; i ++){
				x = i / 16;
				y = i % 16;
				img.setRGB(x, y, color);
			}
			
			return true;
		}
	}
	
	public static class Mixer extends PaintTool{
		
		public Mixer(){
			super();
			this.setUnlocalizedName("mixer_brush").setTextureName("minepainter:brush");
		}
		
		@Override public boolean apply(BufferedImage img, float[] point, int color, boolean isSneaking){
			
			int x = (int) (point[0] * 16 + 16) - 16;
			int y = (int) (point[1] * 16 + 16) - 16;
			
			int a75 = (int)(((color >> 24) & 0xff) * 0.75f) << 24; 
			a75 += color & 0xffffff;
			int a50 = (int)(((color >> 24) & 0xff) * 0.5f) << 24;
			a50 += color & 0xffffff;
			
			boolean changed = false;
			for(int i = -1; i <=1; i++)
				for(int j = -1; j <= 1; j++){
					if(!inBounds(x+i,y+j))continue;
					changed = true;
					
					int to_blend = Math.abs(i) + Math.abs(j);
					if(to_blend == 0)to_blend = color;
					else if(to_blend == 1)to_blend = a75;
					else to_blend = a50;
					
					img.setRGB(x+i, y+j, mix(to_blend, img.getRGB(x+i, y+j)));
				}
			return changed;
		}
		
		private int mix(int color,int original){
			float a_alpha = (color >> 24 & 0xFF) / 255.0F;
		    float b_alpha = (original >> 24 & 0xFF) / 255.0F;
		    float c_alpha = a_alpha + b_alpha * (1.0F - a_alpha);
		    int result = 0;

		    for (int b = 0; b < 24; b += 8)
		    {
		      int ca = color >> b & 0xFF;
		      ca = (int)(ca * a_alpha);
		      int cb = original >> b & 0xFF;
		      cb = (int)(cb * b_alpha * (1.0F - a_alpha));
		      result += ((int)((ca + cb) / c_alpha) << b);
		    }

		    result += ((int)(255.0F * c_alpha) << 24);
		    return result;
		}
		
	}
	
	public static class Eraser extends Mixer{
		public Eraser(){
			super();
			this.setUnlocalizedName("eraser").setTextureName("minepainter:eraser");
		}
		
		@Override public boolean apply(BufferedImage img, float[] point, int color, boolean isSneaking){
			
			int x = (int) (point[0] * 16 + 16) - 16;
			int y = (int) (point[1] * 16 + 16) - 16;
			
			boolean changed = false;
			for(int i = -1; i <=1; i++)
				for(int j = -1; j <= 1; j++){
					if(isSneaking && (i != 0 || j != 0))continue;
					if(!inBounds(x+i,y+j))continue;
					changed = true;
					
					color = img.getRGB(x+i, y+j);
					int a75 = (int)(((color >> 24) & 0xff) * 0.75f) << 24; 
					a75 += color & 0xffffff;
					int a50 = (int)(((color >> 24) & 0xff) * 0.5f) << 24;
					a50 += color & 0xffffff;
					
					int to_blend = Math.abs(i) + Math.abs(j);
					if(to_blend == 0)to_blend = 0;
					else if(to_blend == 1)to_blend = a50;
					else to_blend = a75;
					
					img.setRGB(x+i, y+j, to_blend);
				}
			return changed;
		}
	}
} public class PaintingBlock extends BlockContainer{

	public boolean ignore_bounds_on_state;
	
	public PaintingBlock() {
		super(Material.cloth);
		this.setBlockTextureName("minepainter:palette");
		this.setHardness(0.2f);
		this.setBlockName("painting");
	}

    @Override public void registerBlockIcons(IIconRegister register){}

	@Override public TileEntity createNewTileEntity(World var1, int var2) {
		return new PaintingEntity();
	}
	
	@Override public boolean isOpaqueCube()
	{
		return false;
	}

    @Override public boolean renderAsNormalBlock()
	{
		return false;
	}

    @Override public AxisAlignedBB getCollisionBoundingBoxFromPool(World par1World, int par2, int par3, int par4) {
        return null;
    }
    
    @Override public void setBlockBoundsBasedOnState(IBlockAccess iba,int x,int y,int z) {
    	if(ignore_bounds_on_state)return;
    	PaintingPlacement placement = PaintingPlacement.of(iba.getBlockMetadata(x, y, z));
    	placement.setBlockBounds(this);
    }
    
    @Override public void setBlockBoundsForItemRender() {
    	this.setBlockBounds(0, 0, 0, 1, 1, 1);
    }
    
    @Override public int getRenderType(){
    	return -1;
    }
    
    @Override public ItemStack getPickBlock(MovingObjectPosition target, World world,int x,int y,int z){
    	ItemStack is = new ItemStack(ModMinePainter.canvas.item);
    	NBTTagCompound nbt = new NBTTagCompound();
    	PaintingEntity pe = Utils.getTE(world, x, y, z);
    	
    	pe.writeImageToNBT(nbt);
    	is.setTagCompound(nbt);
    	return is;
    }
    
    @Override
    public void onNeighborBlockChange(World w, int x, int y, int z, Block block) {
    	PaintingPlacement pp = PaintingPlacement.of(w.getBlockMetadata(x, y, z));
    	int tx = x - pp.normal.offsetX;
    	int ty = y - pp.normal.offsetY;
    	int tz = z - pp.normal.offsetZ;
    	if(w.getBlock(tx, ty, tz).getMaterial().isSolid())return;
    	
    	w.setBlockToAir(x, y, z);
    }
    
    protected ItemStack createStackedBlock(int p_149644_1_){return null;}
    
    @Override @SideOnly(Side.CLIENT) public IIcon getIcon(int side, int meta){
		return Blocks.wool.getIcon(side, 0);
	}
    
    public Item getItemDropped(int p_149650_1_, Random p_149650_2_, int p_149650_3_)
    {
        return null;
    }
    
    @Override public void breakBlock(World w,int x,int y,int z,Block b, int meta){
    	
    	ItemStack is = new ItemStack(ModMinePainter.canvas.item);
    	NBTTagCompound nbt = new NBTTagCompound();
    	PaintingEntity pe = Utils.getTE(w, x, y, z);
    	
    	pe.writeImageToNBT(nbt);
    	is.setTagCompound(nbt);
    	this.dropBlockAsItem(w, x, y, z, is);
    	
    	super.breakBlock(w, x, y, z, b, meta);
    }
} public class PaintingEntity extends TileEntity{
	
	BufferedImage image;
	
	public PaintingEntity(){
		 image = new BufferedImage(16,16, BufferedImage.TYPE_INT_ARGB);
		 
//		 WritableRaster raster = image.getRaster();
		 for(int i = 0 ; i < 16; i ++){
			 image.setRGB(i, 0, getColorForDye(i) | 0xff000000);
//			 raster.setSample(i/16, i%16, 0, i%2 == 0 ? 0xffffffff : 0xff000000);
		 }
		 Graphics g = image.getGraphics();
		 g.setColor(Color.white);
		 g.fillRect(0, 1, 16, 15);
	}
	
	public int getColorForDye(int dye_index){
		return ItemDye.field_150922_c[dye_index];
	}
	
	public BufferedImage getImg(){
		return image;
	}
	
	@SideOnly(Side.CLIENT)
	private PaintingIcon icon;
	
	@SideOnly(Side.CLIENT)
	public PaintingIcon getIcon(){
		if(icon == null)icon = PaintingCache.get();
		return icon;
	}
	
	@Override
	public void invalidate()
	{
        super.invalidate();
        if(this.worldObj.isRemote){
        	getIcon().release();
        	icon = null;
        }
	}
	
        @Override
	public void onChunkUnload()
	{
        super.onChunkUnload();
		if(this.worldObj.isRemote){
			getIcon().release();
			icon = null;
		}
	}
	
	@Override
	public void writeToNBT(NBTTagCompound nbt)
	{
		super.writeToNBT(nbt);
		writeImageToNBT(nbt);
	}
	
	public void writeImageToNBT(NBTTagCompound nbt){
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		try {
			ImageIO.write(image, "png", baos);
		} catch (IOException e) {
			e.printStackTrace();
		}
		nbt.setByteArray("image_data", baos.toByteArray());
	}

	@Override
	public void readFromNBT(NBTTagCompound nbt)
	{
		readFromNBTToImage(nbt);
		super.readFromNBT(nbt);
	}
	
	public void readFromNBTToImage(NBTTagCompound nbt){
		if(nbt == null)return;
		byte[] data = nbt.getByteArray("image_data");
		ByteArrayInputStream bais = new ByteArrayInputStream(data);
		try{
			BufferedImage img = ImageIO.read(bais);
			this.image = img;
			if(worldObj != null && worldObj.isRemote)
				this.getIcon().fill(img);
		}catch(IOException e){
			this.getIcon().fill(this.image);
		}
	}
	
	public Packet getDescriptionPacket()
    {
        NBTTagCompound nbttagcompound = new NBTTagCompound();
        this.writeToNBT(nbttagcompound);
        return new S35PacketUpdateTileEntity(this.xCoord, this.yCoord, this.zCoord, 17, nbttagcompound);
    }

    public void onDataPacket(NetworkManager net, S35PacketUpdateTileEntity pkt)
    {
        readFromNBT(pkt.func_148857_g());
    }
} public class PaintingOperationMessage implements IMessage{

	PaintTool tool;
	int x,y,z;
	float xs,ys,zs;
	int color;
	
	public PaintingOperationMessage(){}
	
	public PaintingOperationMessage(PaintTool tool, int x,int y,int z,float xs,float ys, float zs,int color){
		this.tool = tool;
		this.x = x; this.y = y; this.z = z;
		this.xs = xs; this.ys = ys; this.zs = zs;
		this.color = color;
	}
	
	@Override
	public void fromBytes(ByteBuf buf) {
		tool = (PaintTool) Item.getItemById(buf.readInt());
		x = buf.readInt(); y = buf.readInt(); z = buf.readInt();
		xs = buf.readFloat(); ys = buf.readFloat(); zs = buf.readFloat();
		color = buf.readInt();
	}

	@Override
	public void toBytes(ByteBuf buf) {
		buf.writeInt(Item.getIdFromItem(tool));
		buf.writeInt(x); buf.writeInt(y); buf.writeInt(z);
		buf.writeFloat(xs); buf.writeFloat(ys); buf.writeFloat(zs);
		buf.writeInt(color);
	}

	public static class PaintingOperationHandler implements IMessageHandler<PaintingOperationMessage, IMessage>{

		@Override
		public IMessage onMessage(PaintingOperationMessage message,
				MessageContext ctx) {

			message.tool.paintAt(ctx.getServerHandler().playerEntity.worldObj, 
					message.x, message.y, message.z, 
					message.xs, message.ys, message.zs, 
					message.color, ctx.getServerHandler().playerEntity.isSneaking());
			
			return null;
		}
		
	}
} @SideOnly(Side.CLIENT)
public class PaintingRenderer extends TileEntitySpecialRenderer{

	@Override
	public void renderTileEntityAt(TileEntity entity, double x, double y,
			double z, float partial) {

		PaintingEntity pe = (PaintingEntity)entity;
		Tessellator tes = Tessellator.instance;
		PaintingIcon icon = pe.getIcon();
		PaintingPlacement placement = PaintingPlacement.of(pe.getBlockMetadata());

		GL11.glPushMatrix();
		GL11.glBindTexture(GL11.GL_TEXTURE_2D, icon.sheet.glTexId);
        RenderHelper.disableStandardItemLighting();
        GL11.glBlendFunc(GL11.GL_SRC_ALPHA,GL11.GL_ONE_MINUS_SRC_ALPHA);
        GL11.glEnable(GL11.GL_BLEND);
		GL11.glTranslated(x, y, z);
//		GL11.glScalef(0.875f, 0.875f, 0.875f);
		
		//face
		tes.startDrawingQuads();
		addPoint(placement,0,0,icon);
		addPoint(placement,0,1,icon);
		addPoint(placement,1,1,icon);
		addPoint(placement,1,0,icon);
		tes.draw();
		
		GL11.glPopMatrix();
		GL11.glDisable(GL11.GL_BLEND);
	}
	
	private void addPoint(PaintingPlacement pp, int x,int y, IIcon icon){
		float[] pos = pp.painting2blockWithShift(x, y, 0.003f);
		Tessellator.instance.addVertexWithUV(pos[0], pos[1], pos[2], 
				x == 0 ? icon.getMinU() : icon.getMaxU(), 
				y == 0 ? icon.getMinV() : icon.getMaxV());
	}
	
	private void drawSides(Tessellator tes, PaintingPlacement placement){		
		//sides
		
		tes.startDrawingQuads();
		addColoredPoint(placement,0,0,0.0625f);
		addColoredPoint(placement,1,0,0.0625f);
		addColoredPoint(placement,1,0,0f);
		addColoredPoint(placement,0,0,0f);
		tes.draw();
		
		tes.startDrawingQuads();
		addColoredPoint(placement,1,0,0.0625f);
		addColoredPoint(placement,1,1,0.0625f);
		addColoredPoint(placement,1,1,0f);
		addColoredPoint(placement,1,0,0f);
		tes.draw();
		
		tes.startDrawingQuads();
		addColoredPoint(placement,1,1,0.0625f);
		addColoredPoint(placement,0,1,0.0625f);
		addColoredPoint(placement,0,1,0f);
		addColoredPoint(placement,1,1,0f);
		tes.draw();
		
		tes.startDrawingQuads();
		addColoredPoint(placement,0,1,0.0625f);
		addColoredPoint(placement,0,0,0.0625f);
		addColoredPoint(placement,0,0,0f);
		addColoredPoint(placement,0,1,0f);
		tes.draw();
	}
	
	private void addColoredPoint(PaintingPlacement pp, int x,int y, float d){
		float[] pos = pp.painting2blockWithShift(x, y, 0);
		Tessellator.instance.setColorOpaque_I(0xffffff);
		Tessellator.instance.addVertex(pos[0], pos[1], pos[2]);
	}
} public class SculptureBlock extends BlockContainer{

	private int x,y,z,meta = 0;
	private Block current = Blocks.stone;
	private int renderID = -1;
	public void setCurrentBlock(Block that, int meta){
		if(that == null){
			meta = 0;
			renderID = -1;
			current = Blocks.stone;
			return;
		}
		current = that;
		this.meta = meta;
		renderID = that.getRenderType();
		if(!SculptureRenderCuller.isMergeable(that))
			renderID = 0;
	}
	public void useStandardRendering(){
		renderID = 0;
	}
	public void setSubCoordinate(int x,int y,int z){
		this.x = x; this.y = y; this.z = z;
	}
	
	public void dropScrap(World w, int x, int y, int z, ItemStack is ){
		this.dropBlockAsItem(w, x, y, z, is);
	}
	
	
	public SculptureBlock() {
		super(Material.rock);
		this.setHardness(1.0f);
		this.setBlockName("sculpture");
	}

	@Override
	public MovingObjectPosition collisionRayTrace(World w, int x, int y, int z, Vec3 st, Vec3 ed)
	{
		SculptureEntity tile = Utils.getTE(w, x, y, z);
		Sculpture sculpture = tile.sculpture();

		int[] pos = Operations.raytrace(sculpture, st.addVector(-x, -y, -z), ed.addVector(-x,-y,-z));
		if(pos[0] == -1)return null;

		ForgeDirection dir = ForgeDirection.getOrientation(pos[3]);
		Vec3 hit = null;
		if(dir.offsetX != 0)hit = st.getIntermediateWithXValue(ed, x + pos[0]/8f + (dir.offsetX+1)/16f);
		else if(dir.offsetY != 0)hit = st.getIntermediateWithYValue(ed, y + pos[1]/8f + (dir.offsetY+1)/16f);
		else if(dir.offsetZ != 0)hit = st.getIntermediateWithZValue(ed, z + pos[2]/8f + (dir.offsetZ+1)/16f);
		if(hit == null){
			if(sculpture.isEmpty())return super.collisionRayTrace(w, x, y, z, st, ed);
			return null;
		}
		
		return new MovingObjectPosition(x,y,z,pos[3], hit);
	}
	
    @Override
	public void addCollisionBoxesToList(World par1World, int par2, int par3, int par4, AxisAlignedBB par5AxisAlignedBB, List par6List, Entity par7Entity)
	{
		SculptureEntity tile = Utils.getTE(par1World, par2, par3, par4);
		Sculpture sculpture = tile.sculpture();
		
		for(int x = 0 ; x < 8 ; x ++)
			for(int y = 0 ; y < 8 ; y ++)
				for(int z = 0 ; z < 8 ; z ++){
					if(sculpture.getBlockAt(x, y, z, null) == Blocks.air)continue;
					this.setBlockBounds(x/8f, y/8f,z/8f, (x+1)/8f, (y+1)/8f,(z+1)/8f);
					super.addCollisionBoxesToList(par1World,par2,par3,par4,par5AxisAlignedBB,par6List,par7Entity);
				}
		this.setBlockBounds(0, 0, 0, 1, 1, 1);
	}
	
	@Override
	public boolean shouldSideBeRendered(IBlockAccess iba, int x, int y, int z, int side){
//		if(x>=0 && y>=0 && z>=0 && x<8 && y<8 && z<8)
//			return iba.isAirBlock(x, y, z);
		if(iba.getBlock(x, y, z) == this.current)return false;
		return iba.isAirBlock(x, y, z) || !iba.getBlock(x, y, z).isOpaqueCube();
	}
	
	@Override
	public TileEntity createNewTileEntity(World var1, int var2) {
		return new SculptureEntity();
	}
	
	@Override @SideOnly(Side.CLIENT) public void registerBlockIcons(IIconRegister p_149651_1_){}
	
	@Override @SideOnly(Side.CLIENT) public IIcon getIcon(int side, int meta){
		return current.getIcon(side, this.meta);
	}
	
	@Override @SideOnly(Side.CLIENT) public int getRenderType()
	{
		if(renderID == -1)
			return ModMinePainter.sculpture.renderID;
		return renderID;
	}
	
    @Override public boolean isOpaqueCube()
	{
		return false;
	}

    @Override public boolean renderAsNormalBlock()
	{
		return false;
	}
    
    @Override public ItemStack getPickBlock(MovingObjectPosition target, World world, int x, int y, int z){
    	SculptureEntity se = Utils.getTE(world, x, y, z);
    	NBTTagCompound nbt = new NBTTagCompound();
    	ItemStack is = new ItemStack(ModMinePainter.droppedSculpture.item);
    	
    	se.sculpture.write(nbt);
    	is.setTagCompound(nbt);
    	return is;
    }
  
    @Override public int getLightValue(IBlockAccess world,int x,int y,int z){
    	TileEntity te = world.getTileEntity(x, y, z);
    	if(te == null || !(te instanceof SculptureEntity))return super.getLightValue(world, x, y, z);
    	SculptureEntity se = (SculptureEntity) te;
    	return se.sculpture.getLight();
    }
    
    protected ItemStack createStackedBlock(int p_149644_1_){return null;}
    
    public boolean dropSculptureToPlayer(World w, EntityPlayer ep, int x,int y,int z){
    	SculptureEntity se = Utils.getTE(w, x, y, z);
    	if(se == null || se.sculpture().isEmpty()){
    		Debug.log("hey this is null!");
    		return false;
    	}
    	NBTTagCompound nbt = new NBTTagCompound();
    	ItemStack is = new ItemStack(ModMinePainter.droppedSculpture.item);
    	
    	applyPlayerRotation(se.sculpture.r, ep, true);
    	se.sculpture.write(nbt);
    	applyPlayerRotation(se.sculpture.r, ep, false);
    	is.setTagCompound(nbt);
    	this.dropBlockAsItem(w, x, y, z, is);
    	
    	if(se.getHinge() != null){
    		is = new ItemStack(ModMinePainter.hinge.item);
    		this.dropBlockAsItem(w, x, y, z, is);
    	}
    	
    	return true;
    }
    
    public boolean removedByPlayer(World w, EntityPlayer ep, int x, int y, int z)
    {
    	dropSculptureToPlayer(w,ep,x,y,z);
        return super.removedByPlayer(w, ep, x, y, z);
    }
    
    public static void applyPlayerRotation(Rotation r, EntityPlayer ep, boolean reverse){
    	Vec3 look  = ep.getLookVec();
    	double dx = Math.abs(look.xCoord);
    	double dz = Math.abs(look.zCoord);
    	
    	int rotation = 0;
    	if(dx > dz)rotation = look.xCoord > 0 ? 3 : 1;
    	else rotation = look.zCoord > 0 ? 2 : 0;
    	
    	if(reverse)rotation = (4 - rotation) % 4;
//    	Debug.log("rotation : " + rotation);
    	
    	for(int i = 0; i < rotation; i ++)r.rotate(1);
    }
    
    public Item getItemDropped(int p_149650_1_, Random p_149650_2_, int p_149650_3_)
    {
    	return null;
    }
    
    public boolean onBlockActivated(World w,int x,int y,int z,EntityPlayer ep, int face, float xs, float ys, float zs){
//    	if(ep.getCurrentEquippedItem() != null)
//    		return false;
    	
    	if(ep.isSneaking())
    		return push(w,x,y,z,face^1);
    	else
    		return push(w,x,y,z,face);
    }
    
    public boolean transpose(World w, int x,int y, int z, int dx, int dy, int dz, int rotate, ForgeDirection shift){
    	
    	int tx = x+shift.offsetX, ty = y+shift.offsetY, tz = z+shift.offsetZ;
    	
    	Rotation r = new Rotation();
    	r.rotate(rotate);
    	r.applyUnbounded(dx, dy, dz);
    	tx += r.x; ty += r.y; tz += r.z;
    	x+=dx; y+=dy; z+=dz;
    	
//    	Debug.log("from " + x + "," + y + "," + z + " to " + tx + "," + ty + "," + tz);
    	
    	if(!w.isAirBlock(tx,ty,tz))return false;
    	
    	SculptureEntity se = Utils.getTE(w, x, y, z);
    	Sculpture sculpture = se.sculpture;
    	sculpture.r.rotate(rotate);
    	Hinge hinge = se.getHinge();
    	
    	w.setBlockToAir(x, y, z);
    	w.setBlock(tx, ty, tz, ModMinePainter.sculpture.block);
    	se = Utils.getTE(w, tx, ty, tz);
    	se.sculpture = sculpture;
    	if(hinge != null)se.setHinge(HingeRotationTable.rotate(hinge, rotate));
    	
    	return true;
    }
    
    TreeSet<Location> sculpture_set = new TreeSet<Location>();
    public boolean push(World w, int x,int y,int z, int face){
    	Hinge hinge = Hinge.fromSculpture(w, x, y, z);
    	if(hinge == null)return false;
    	
    	ForgeDirection push = ForgeDirection.getOrientation(face^1);
    	int rotate = hinge.getRotationFace(push);
    	ForgeDirection shift = hinge.getShift(push);
    	
    	
    	if(shift == null)return false;
    	
    	sculpture_set.clear();
    	add_connected(w,x,y,z);
    	if(sculpture_set.size() > 256)return false;
    	
    	boolean flag = true;
    	for(Location loc : sculpture_set)
    		flag &= transpose(w,x,y,z,loc.x-x,loc.y-y,loc.z-z,rotate,shift);
    	
    	sculpture_set.clear();
    	return flag;
    }
    
    private void add_connected(World w,int x,int y,int z){
    	if(w.getBlock(x, y, z) != ModMinePainter.sculpture.block)return;
    	
    	Location loc = new Location(x,y,z);
    	if(sculpture_set.contains(loc))return;    	
    	
    	sculpture_set.add(loc);
    	if(sculpture_set.size() > 256)return;
    	Nail nail = Nail.fromSculpture(w, x, y, z);
    	for(int i = 0; i < 6; i ++){
    		if(!nail.isOnFace(i))continue;
    		ForgeDirection dir = ForgeDirection.getOrientation(i);
    		add_connected(w,x+dir.offsetX,y+dir.offsetY,z+dir.offsetZ);
    	}
    }
} public class SculptureEntity extends TileEntity{

	Sculpture sculpture = new Sculpture();
	Hinge hinge = null;
	final Nail nail = new Nail();
	
	@SideOnly(Side.CLIENT)
	private SculptureRenderCompiler render;
	
	public Sculpture sculpture(){
		return sculpture;
	}
	
	public Hinge getHinge(){
		return hinge;
	}
	
	public void setHinge(Hinge h){
		this.hinge = h;
	}
	
	public Nail getNail(){
		return nail;
	}
	
	@SideOnly(Side.CLIENT)
	public SculptureRenderCompiler getRender(){
		if(render == null)render = new SculptureRenderCompiler();
		return render;
	}
	
	@SideOnly(Side.CLIENT)
	public void updateRender(){
		if(this.worldObj.isRemote){
			BlockSlice slice = BlockSlice.at(worldObj, xCoord, yCoord, zCoord);
			getRender().update(slice);
			slice.clear();
		}
	}
	
    @Override @SideOnly(Side.CLIENT)
	public void invalidate()
	{
        super.invalidate();
        if(this.worldObj.isRemote)
        	getRender().clear();
	}
	
    @Override @SideOnly(Side.CLIENT)
	public void onChunkUnload()
	{
        super.onChunkUnload();
		if(this.worldObj.isRemote)
			getRender().clear();
	}

	//TileEntity util

	@Override
	public void writeToNBT(NBTTagCompound nbt)
	{
		super.writeToNBT(nbt);
		sculpture.write(nbt);
		if(hinge!=null)hinge.toNBT(nbt);
		nail.writeTo(nbt);
	}

	@Override
	public void readFromNBT(NBTTagCompound nbt)
	{
		if(worldObj != null && worldObj.isRemote)
			getRender().changed = true;
		super.readFromNBT(nbt);
		sculpture.read(nbt);
		hinge = Hinge.fromNBT(nbt);
		nail.readFrom(nbt);
	}
	
	public Packet getDescriptionPacket()
    {
        NBTTagCompound nbttagcompound = new NBTTagCompound();
        this.writeToNBT(nbttagcompound);
        return new S35PacketUpdateTileEntity(this.xCoord, this.yCoord, this.zCoord, 17, nbttagcompound);
    }

    public void onDataPacket(NetworkManager net, S35PacketUpdateTileEntity pkt)
    {
        readFromNBT(pkt.func_148857_g());
    }
} @SideOnly(Side.CLIENT)
public class SculptureEntityRenderer  extends TileEntitySpecialRenderer{

	@Override
	public void renderTileEntityAt(TileEntity var1, double xd, double yd,
			double zd, float partial) {
		SculptureEntity se = (SculptureEntity) var1;
	    
	    if(!se.getRender().ready() && !se.getRender().hasContext()){
	    	
	    	int lightX = (int) (OpenGlHelper.lastBrightnessX);
	    	int lightY = (int) (OpenGlHelper.lastBrightnessY);
	    	int light = lightY * 65536 + lightX; 
	    	
	    	se.getRender().initFromSculptureAndLight(se.sculpture(), light);
	    }
	    else
	    	se.updateRender();

		RenderHelper.disableStandardItemLighting();
		GL11.glPushMatrix();
		GL11.glTranslated(xd,yd,zd);
		
		int displayList = se.getRender().glDisplayList[0];
		if(displayList > 0)
			GL11.glCallList(displayList);
		
		displayList = se.getRender().glDisplayList[1];
		if(displayList > 0){
			GL11.glEnable(GL11.GL_BLEND);
		    OpenGlHelper.glBlendFunc(770, 771, 1, 0);
		    GL11.glAlphaFunc(GL11.GL_GREATER, 0.1f);
			GL11.glCallList(displayList);
			GL11.glDisable(GL11.GL_BLEND);
		}
		
		GL11.glPopMatrix();
		
//        GL11.glEnable(GL11.GL_ALPHA_TEST);
        RenderHelper.enableStandardItemLighting();
	}

} public class SculptureOperationMessage implements IMessage{

	int[] pos = new int[4];
	int x,y,z;
	Block block;
	int meta;
	int flags;
	
	public SculptureOperationMessage(){}
	
	public SculptureOperationMessage(int[] pos, int x,int y,int z, Block block, int meta, int flags){
		this.pos = pos;
		this.x = x; this.y = y; this.z = z;
		this.block = block; this.meta = meta;
		this.flags = flags;
	}
	
	@Override
	public void fromBytes(ByteBuf buf) {
		pos[0] = buf.readByte();
		pos[1] = buf.readByte();
		pos[2] = buf.readByte();
		pos[3] = buf.readByte();
		x = buf.readInt();
		y = buf.readInt();
		z = buf.readInt();
		block = Block.getBlockById(buf.readInt());
		meta = buf.readByte();
		flags = buf.readByte();
	}

	@Override
	public void toBytes(ByteBuf buf) {
		buf.writeByte(pos[0]);
		buf.writeByte(pos[1]);
		buf.writeByte(pos[2]);
		buf.writeByte(pos[3]);
		buf.writeInt(x);
		buf.writeInt(y);
		buf.writeInt(z);
		buf.writeInt(Block.getIdFromBlock(block));
		buf.writeByte(meta);
		buf.writeByte(flags);
	}

	public static class SculptureOperationHandler implements IMessageHandler<SculptureOperationMessage, IMessage>{

		@Override
		public IMessage onMessage(SculptureOperationMessage message,
				MessageContext ctx) {
			
			World w = ctx.getServerHandler().playerEntity.worldObj;
			if(Operations.validOperation(w, message.x, message.y, message.z, message.pos, message.flags))
				Operations.applyOperation(w, message.x, message.y, message.z, message.pos, message.flags, message.block, message.meta);
			
			EntityPlayer ep = ctx.getServerHandler().playerEntity;
			ItemStack is = ep.getCurrentEquippedItem();
			
			if((message.flags & Operations.DAMAGE) > 0)
				is.damageItem(1, ep);
			else if((Operations.CONSUME & message.flags) > 0){
				if(!ep.capabilities.isCreativeMode){
					is.stackSize--;
					if(is.stackSize <= 0){
						ForgeEventFactory.onPlayerDestroyItem(ep,is);
						ep.inventory.mainInventory[ep.inventory.currentItem] = null;
					}
				}
			}				
			
			return null;
		}
		
	}
} @SideOnly(Side.CLIENT)
public class SculptureRender implements ISimpleBlockRenderingHandler{
	
	@Override
	public void renderInventoryBlock(Block block, int metadata, int modelId,
			RenderBlocks renderer) {
	}

	@Override
	public boolean renderWorldBlock(IBlockAccess world, int x, int y, int z,
			Block block, int modelId, RenderBlocks renderer) {

		if(world.getBlock(x, y, z) != ModMinePainter.sculpture.block)return false;
		
		SculptureEntity se = (SculptureEntity) world.getTileEntity(x, y, z);
		
		se.getRender().updateLight(block.getMixedBrightnessForBlock(world, x, y, z));
		se.getRender().updateAO(world, x, y, z);
		
		return false;
	}

	@Override
	public boolean shouldRender3DInInventory(int modelId) {
		return false;
	}

	@Override
	public int getRenderId() {
		return ModMinePainter.sculpture.renderID;
	}

} @SideOnly(Side.CLIENT)
public class SculptureRenderCompiler {
	public static boolean CULL = true;
	public static RenderBlocks rb = new SculptureRenderBlocks();

	int[] glDisplayList;
	int light;
	public boolean changed = true;
	boolean context = false;
	float[][][] neighborAO = new float[3][3][3];
	
	public void updateAO(IBlockAccess w, int x,int y,int z){
		for(int i = 0 ; i < 27; i ++){
			int dx = i%3;
			int dy = (i/3)%3;
			int dz = (i/9)%3;
			
			float ao = w.getBlock(x+dx-1, y+dy-1, z+dz-1).getAmbientOcclusionLightValue();
			if(ao != neighborAO[dx][dy][dz]){
				changed = true;
				neighborAO[dx][dy][dz] = ao;
			}
		}
		context = true;
	}
	
	public void updateLight(int light){
		if(light != this.light)
			changed = true;
		this.light = light;
		context = true;
	}	
	
	public boolean hasContext(){
		return context;
	}
	
	public boolean update(BlockSlice slice){
		if(glDisplayList != null && !changed)return false;
		
		if(glDisplayList == null)glDisplayList = new int[]{-1,-1};
		for(int pass = 0; pass < 2; pass++){
			if(glDisplayList[pass] < 0)glDisplayList[pass] = GLAllocation.generateDisplayLists(1);
			GL11.glPushMatrix();
			GL11.glNewList(glDisplayList[pass], GL11.GL_COMPILE);
			if(slice.sculpture.needRenderPass(pass))
				build(slice,pass);
			GL11.glEndList();
			GL11.glPopMatrix();
		}		
		
		changed = false;
		return true;
	}
	
	public void build(BlockSlice slice, int pass){
		rb.blockAccess = slice;
		SculptureBlock sculpture = ModMinePainter.sculpture.block;
		
		TextureManager tm = Minecraft.getMinecraft().renderEngine;
		tm.bindTexture(TextureMap.locationBlocksTexture);
		
		Tessellator tes = Tessellator.instance;
		double[] offs = getTesOffsets();
		tes.setTranslation(0, 0, 0);
		tes.startDrawingQuads();
		
		if(!CULL){
			for(int i = 0; i < 512; i ++){
				int x = (i >> 6) & 7;
				int y = (i >> 3) & 7;
				int z = (i >> 0) & 7;
	
				Block b = slice.getBlock(x, y, z);
				if(b == Blocks.air)continue;
				if(!b.canRenderInPass(pass))continue;
				int meta = slice.getBlockMetadata(x, y, z);
				sculpture.setCurrentBlock(b, meta);
				
				tes.setTranslation(-x, -y, -z);
				sculpture.setBlockBounds(x/8f, y/8f, z/8f, (x+1)/8f, (y+1)/8f, (z+1)/8f);
				try{
					rb.renderBlockByRenderType(sculpture, x,y,z);
				}catch(RuntimeException e){
					sculpture.useStandardRendering();
					rb.renderBlockByRenderType(sculpture, x,y,z);
				}
			}
		}else{
			int ao = Minecraft.getMinecraft().gameSettings.ambientOcclusion;
			Minecraft.getMinecraft().gameSettings.ambientOcclusion = 0;
			int[][][] merged = SculptureRenderCuller.culler.getMergeMap(slice.sculpture);
			for(int i = 0; i < 512; i ++){
				int x = (i >> 6) & 7;
				int y = (i >> 3) & 7;
				int z = (i >> 0) & 7;
				
				if((merged[x][y][z] & 3) != 3)continue;
				int index = merged[x][y][z] >> 11;
				Block b = Block.getBlockById(slice.sculpture.block_ids[index]);
				if(b == Blocks.air)continue;
				if(!b.canRenderInPass(pass))continue;
				int meta = slice.sculpture.block_metas[index];
				int ex = (merged[x][y][z] >> 2)&7;
				int ey = (merged[x][y][z] >> 5)&7;
				int ez = (merged[x][y][z] >> 8)&7;
				
				sculpture.setCurrentBlock(b, meta);				
				tes.setTranslation(-x, -y, -z);
				sculpture.setBlockBounds(x/8f, y/8f, z/8f, (x+ex+1)/8f, (y+ey+1)/8f, (z+ez+1)/8f);
				rb.renderAllFaces = SculptureRenderCuller.isMergeable(b);
				try{
					rb.renderBlockByRenderType(sculpture, x,y,z);
				}catch(RuntimeException e){
					sculpture.useStandardRendering();
					rb.renderBlockByRenderType(sculpture, x,y,z);
				}
			}
			Minecraft.getMinecraft().gameSettings.ambientOcclusion = ao;
		}
		
//		Hinge hinge = Hinge.fromSculpture((SculptureEntity) slice.getTileEntity(0, 0, 0));
//		if(hinge != null){
//			hinge.setRenderBounds(sculpture);
//			sculpture.setCurrentBlock(Blocks.iron_block, 0);
//			tes.setTranslation(0, 0, 0);
//			rb.setRenderBoundsFromBlock(sculpture);
//			rb.renderAllFaces = true;
//			rb.renderStandardBlockWithColorMultiplier(sculpture, 0,0,0, 1f,1f,1f);
//		}
//		
//		Nail nail = Nail.fromSculpture(slice, 0, 0, 0);
//		for(ForgeDirection dir : ForgeDirection.VALID_DIRECTIONS){
//			if(!nail.isOnFace(dir.ordinal()))continue;
//			
//			
//		}
		
		sculpture.setCurrentBlock(null,0);
		sculpture.setBlockBounds(0,0,0,1,1,1);
		rb.blockAccess = null;
		tes.draw();
		tes.setTranslation(offs[0], offs[1], offs[2]);
	}

	public void clear(){
		if(glDisplayList == null)return;
		for(int i = 0; i < glDisplayList.length; i ++)
		if(glDisplayList[i]>=0)
			GL11.glDeleteLists(glDisplayList[i], 1);
	}


	public boolean ready() {
		return glDisplayList != null;
	}
	
	private double[] getTesOffsets(){
		double[] off = new double[3];
		
		int count = 0;
		int xoff = 0;
		Field[] fields = Tessellator.class.getDeclaredFields();
		for(int i = 0;i < fields.length; i++)
			if(fields[i].getType() == double.class){
				count++;
				if(count == 3)xoff = i-2;
			}else
				count = 0;
		
		off[0] = ObfuscationReflectionHelper.getPrivateValue(Tessellator.class, Tessellator.instance, xoff  );
		off[1] = ObfuscationReflectionHelper.getPrivateValue(Tessellator.class, Tessellator.instance, xoff+1);
		off[2] = ObfuscationReflectionHelper.getPrivateValue(Tessellator.class, Tessellator.instance, xoff+2);
		
		return off;
	}

	public void initFromSculptureAndLight(Sculpture sculpture,int light) {
		this.update(BlockSlice.of(sculpture, light));
	}
	
} public class SculptureRenderCuller {
	public static SculptureRenderCuller culler = new SculptureRenderCuller();
	
	// 0 = merge x, 1 = merge y,  2 = merge z, 3 = unmerged
	private static final int TYPE_X = 0x0;
	private static final int TYPE_Y = 0x1;
	private static final int TYPE_Z = 0x2;
	private static final int TYPE_S = 0x3;
	private static final int BIT_XLEN = 0x1 << 2;
	private static final int BIT_YLEN = 0x1 << 5;
	private static final int BIT_ZLEN = 0x1 << 8;
	private static final int BIT_INDEX = 0x1 << 11; 
	
	private int[][][] mergeMap = new int[8][8][8];
	
	public static boolean isMergeable(Block b){
		if(b.getLightOpacity() < 255)return false;
		return true;
	}
	
	private static boolean isMergeable(int id){
		return isMergeable(Block.getBlockById(id));
	}
	
	public int[][][] getMergeMap(Sculpture sculpture){
		
		// cull z direction
		for(int i = 0; i < 8; i ++){
			for(int j = 0; j < 8; j ++){
				int prev = -1;
				int source = -1;
				for(int k = 0; k < 8; k ++){
					int now = sculpture.getIndex(i, j, k);
					if(now == prev && isMergeable(sculpture.block_ids[now])){
						mergeMap[i][j][k] = TYPE_Z;
						mergeMap[i][j][source] += BIT_ZLEN;
					}else{
						source = k;
						mergeMap[i][j][source] = TYPE_S | (now * BIT_INDEX);
					}
					prev = now;
				}
			}
		}
		
		// cull y direction
		for(int i = 0; i < 8; i ++)
			for(int k = 0; k < 8; k ++){
				int prev = -1;
				int source = -1;
				for(int j = 0; j < 8; j ++){
					
					if((mergeMap[i][j][k] & 3) != TYPE_S){
						prev = -1;
						source =-1;
						continue;
					}
					
					int now = mergeMap[i][j][k]; 
					if(now == prev && isMergeable(sculpture.block_ids[now/BIT_INDEX])){
						mergeMap[i][j][k] = TYPE_Y;
						mergeMap[i][source][k] += BIT_YLEN; 
					}else{
						source = j;
					}
					prev = now;
				}
			}
		
		for(int j = 0; j < 8; j ++)
			for(int k = 0; k < 8; k ++){
				int prev = -1;
				int source = -1;
				for(int i = 0; i < 8; i ++){
					
					if((mergeMap[i][j][k] & 3) != TYPE_S){
						prev = -1;
						source =-1;
						continue;
					}
					
					int now = mergeMap[i][j][k]; 
					if(now == prev && isMergeable(sculpture.block_ids[now/BIT_INDEX])){
						mergeMap[i][j][k] = TYPE_X;
						mergeMap[source][j][k] += BIT_XLEN; 
					}else{
						source = i;
					}
					prev = now;
				}
				
			}
		
		return mergeMap;
	}
} public class BlockLoader<T extends Block> {
	
	public final T block;
	public final Class<? extends TileEntity> tileEntityClass;
	
	@SideOnly(Side.CLIENT)
	public int renderID;
	
	public BlockLoader(T block, Class<? extends TileEntity> clazz){
		this.block = block;
		this.tileEntityClass = clazz;
	}
	
	public void load(){
		GameRegistry.registerBlock(block, block.getClass().getSimpleName());
		if(tileEntityClass != null)GameRegistry.registerTileEntity(tileEntityClass, tileEntityClass.getSimpleName());
	}
	
	@SideOnly(Side.CLIENT)
	public void registerRendering(ISimpleBlockRenderingHandler blockRenderer, TileEntitySpecialRenderer tileRenderer){
		if(blockRenderer != null){
			renderID = RenderingRegistry.getNextAvailableRenderId();
			RenderingRegistry.registerBlockHandler(renderID, blockRenderer);
		}
		if(tileRenderer != null){
			ClientRegistry.bindTileEntitySpecialRenderer(tileEntityClass, tileRenderer);
		}
	}
} public class Debug {

	
	public static <T> void log(T... thing){
		StringBuilder sb = new StringBuilder();
		for(T i : thing)sb.append(i + ", ");
		System.err.println(sb.toString());
	}
	
} public class ItemLoader <T extends Item>{

	public T item;
	public ItemLoader(T item){
		this.item = item;
	}
	
	public void load(String name){
		GameRegistry.registerItem(item, name);
	}
	
	public void load(){
		load(item.getClass().getSimpleName().replace("$", "_"));
	}
	
	@SideOnly(Side.CLIENT)
	public void registerRendering(IItemRenderer renderer){
		MinecraftForgeClient.registerItemRenderer(item, renderer);
	}
}
import hx.minepainter.item.CopygunItem;
import hx.minepainter.item.DroppedSculptureItem;
import hx.minepainter.item.DroppedSculptureRenderer;
import hx.minepainter.item.CanvasItem;
import hx.minepainter.item.CanvasRenderer;
import hx.minepainter.item.ChiselItem;
import hx.minepainter.item.HingeItem;
import hx.minepainter.item.Palette;
import hx.minepainter.item.PieceItem;
import hx.minepainter.item.PieceItem.Bar;
import hx.minepainter.item.PieceRenderer;
import hx.minepainter.item.TransmuterItem;
import hx.minepainter.item.WrenchItem;
import hx.minepainter.painting.CommandImportPainting;
import hx.minepainter.painting.PaintTool;
import hx.minepainter.painting.PaintingBlock;
import hx.minepainter.painting.PaintingEntity;
import hx.minepainter.painting.PaintingOperationMessage;
import hx.minepainter.painting.PaintingRenderer;
import hx.minepainter.sculpture.SculptureBlock;
import hx.minepainter.sculpture.SculptureEntity;
import hx.minepainter.sculpture.SculptureEntityRenderer;
import hx.minepainter.sculpture.SculptureOperationMessage;
import hx.minepainter.sculpture.SculptureRender;
import hx.minepainter.sculpture.SculptureRenderCompiler;
import hx.minepainter.sculpture.SculptureRenderCuller;
import hx.utils.BlockLoader;
import hx.utils.Debug;
import hx.utils.ItemLoader;
import net.minecraft.block.Block;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.init.Blocks;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.config.Configuration;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.Mod.EventHandler;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.event.FMLServerStartingEvent;
import cpw.mods.fml.common.network.NetworkRegistry;
import cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper;
import cpw.mods.fml.common.registry.GameRegistry;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
package hx.minepainter;


//TODO [DEFER] add a sculpture motor block thing
//TODO [DEFER] add a sculpture printer block
@Mod(modid = "minepainter", version = "0.3.0f1")
public class ModMinePainter {
	
	public static CreativeTabs tabMinePainter = new CreativeTabs("minepainter"){

		@Override public Item getTabIconItem() {
			return ModMinePainter.mixerbrush.item;
		}
		
	};
	
	public static BlockLoader<SculptureBlock> sculpture = 
			new BlockLoader(new SculptureBlock(), SculptureEntity.class);
	public static BlockLoader<PaintingBlock> painting = 
			new BlockLoader(new PaintingBlock(), PaintingEntity.class);
	
	public static ItemLoader<Item> handle = new ItemLoader(new Item().setUnlocalizedName("handle").setTextureName("minepainter:handle"));