package info.jbcs.minecraft.chisel;

import java.util.ArrayList;
import java.util.List;

import net.minecraft.block.Block;
import net.minecraft.block.BlockPane;
import net.minecraft.client.renderer.texture.IconRegister;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Icon;
import net.minecraft.world.IBlockAccess;
import net.minecraftforge.common.MinecraftForge;
import cpw.mods.fml.common.registry.GameRegistry;
import cpw.mods.fml.common.registry.LanguageRegistry;

public class CarvableHelper {
	static final String				modName			= "chisel";
	static final ArrayList<Block>	chiselBlocks	= new ArrayList<Block>();

	static final int				NORMAL			= 0;
	static final int				TOPSIDE			= 1;
	static final int				TOPBOTSIDE		= 2;
	static final int				CTM				= 3;
	static final int				CTMV			= 4;
	static final int				CTMH			= 5;

	CarvableHelper() {
	}

	static class CarvableVariationCTM {
		TextureSubmap	seams[]	= new TextureSubmap[3];
	}

	static class CarvableVariation {
		String					description;
		int						metadata;
		int						kind;

		Block					block;
		int						blockMeta;

		String					texture;

		Icon					icon;
		Icon					iconTop;
		Icon					iconBot;

		CarvableVariationCTM	ctm;
		TextureSubmap			seamsCtmVert;
	}

	ArrayList<CarvableVariation>	variations	= new ArrayList<CarvableVariation>();
	CarvableVariation[]				map			= new CarvableVariation[16];

	public void addVariation(String description, int metadata, Block bb) {
		addVariation(description, metadata, null, bb, 0);
	}

	public void addVariation(String description, int metadata, Block bb, int blockMeta) {
		addVariation(description, metadata, null, bb, blockMeta);
	}

	public void addVariation(String description, int metadata, String texture) {
		addVariation(description, metadata, texture, null, 0);
	}

	public void addVariation(String description, int metadata, String texture, Block block, int blockMeta) {
		if (variations.size() > 15)
			return;

		CarvableVariation variation = new CarvableVariation();
		variation.description = description;
		variation.metadata = metadata;

		if (texture != null) {
			variation.texture = texture;

			String path = "/mods/" + modName + "/textures/blocks/" + variation.texture;

			boolean any = Chisel.class.getResource(path + ".png") != null;
			boolean ctm3 = Chisel.class.getResource(path + "-ctm1.png") != null && Chisel.class.getResource(path + "-ctm2.png") != null && Chisel.class.getResource(path + "-ctm3.png") != null;
			boolean ctmv = Chisel.class.getResource(path + "-ctmv.png") != null;
			boolean ctmh = Chisel.class.getResource(path + "-ctmh.png") != null;
			boolean side = Chisel.class.getResource(path + "-side.png") != null;
			boolean top = Chisel.class.getResource(path + "-top.png") != null;
			boolean bot = Chisel.class.getResource(path + "-bottom.png") != null;

			if (ctm3) {
				variation.kind = 3;
			} else if (ctmh && top) {
				variation.kind = 5;
			} else if (ctmh) {
				variation.kind = 6;
			} else if (ctmv && top) {
				variation.kind = 4;
			} else if (bot && top && side) {
				variation.kind = 2;
			} else if (top && side) {
				variation.kind = 1;
			} else if (any) {
				variation.kind = 0;
			} else {
				throw new RuntimeException("No valid textures found for chisel block variation '" + description + "' (" + variation.texture + ")");
			}
		} else {
			variation.block = block;
			variation.kind = 2;
			variation.blockMeta = blockMeta;
		}

		variations.add(variation);
		map[metadata] = variation;
	}

	public Icon getIcon(int side, int metadata) {
		if (metadata < 0 || metadata > 15)
			metadata = 0;

		CarvableVariation variation = map[metadata];
		if (variation == null)
			return GeneralClient.getMissingIcon();

		switch (variation.kind) {
		case 0:
			return variation.icon;
		case 1:
			if (side == 0 || side == 1)
				return variation.iconTop;
			else
				return variation.icon;
		case 2:
			if (side == 1)
				return variation.iconTop;
			else if (side == 0)
				return variation.iconBot;
			else
				return variation.icon;
		case 3:
			return variation.ctm.seams[0].icons[0];
		case 4:
			if (side < 2)
				return variation.iconTop;
			else
				return variation.seamsCtmVert.icons[0];
		case 5:
			if (side < 2)
				return variation.iconTop;
			else
				return variation.seamsCtmVert.icons[0];
		case 6:
			return variation.seamsCtmVert.icons[0];
		}

		return GeneralClient.getMissingIcon();
	}

	public Icon getBlockTexture(IBlockAccess world, int x, int y, int z, int side) {
		int metadata=world.getBlockMetadata(x, y, z);
		
		if (metadata < 0 || metadata > 15)
			metadata = 0;

		CarvableVariation variation = map[metadata];
		if (variation == null)
			return GeneralClient.getMissingIcon();

		switch (variation.kind) {
		case 0:
		case 1:
		case 2:
			return getIcon(side,metadata);
		case 3:
			int tex = getTexture(world, x, y, z, side);

			int row = tex / 16;
			int col = tex % 16;

			return variation.ctm.seams[col / 4].icons[col % 4 + row * 4];
		case 4:
			if(side<2)
				return variation.iconTop;
			
			int blockId=world.getBlockId(x, y, z);
			boolean topConnected= blockId==world.getBlockId(x, y+1, z) && metadata==world.getBlockMetadata(x, y+1, z);
			boolean botConnected= blockId==world.getBlockId(x, y-1, z) && metadata==world.getBlockMetadata(x, y-1, z);
			
			if( topConnected &&  botConnected) return variation.seamsCtmVert.icons[2];
			if( topConnected && !botConnected) return variation.seamsCtmVert.icons[3];
			if(!topConnected &&  botConnected) return variation.seamsCtmVert.icons[1];
			return variation.seamsCtmVert.icons[0];
		case 5:
		case 6:
			if(side<2)
				return variation.kind==5?variation.iconTop:variation.seamsCtmVert.icons[0];
			
			int id=world.getBlockId(x, y, z);
			
			boolean p;
			boolean n;
			boolean reverse=side==2||side==4;
			
			if(side<4){
				p=isSame(world,x-1,y,z,id,metadata);
				n=isSame(world,x+1,y,z,id,metadata);
			} else{
				p=isSame(world,x,y,z+1,id,metadata);
				n=isSame(world,x,y,z-1,id,metadata);
			}
			
			if(p && n) return  variation.seamsCtmVert.icons[1];
			else if(p) return  variation.seamsCtmVert.icons[reverse?2:3];
			else if(n) return  variation.seamsCtmVert.icons[reverse?3:2];
			return variation.seamsCtmVert.icons[0];
		}

		return GeneralClient.getMissingIcon();
	}

	void register(Block block, String name) {
		register(block, name, ItemCarvable.class);
	}

	void register(Block block, String name, Class cl) {
		block.setUnlocalizedName(name);

		Item.itemsList[block.blockID] = null;
		GameRegistry.registerBlock(block, cl, name);

		if (block instanceof BlockMarbleSlab) {
			BlockMarbleSlab slab = (BlockMarbleSlab) block;
			GameRegistry.registerBlock(slab.top, cl, name + ".top");
		}

		for (CarvableVariation variation : variations) {
			registerVariation(name, variation, block, variation.metadata);
			
			if (block instanceof BlockMarbleSlab && ((BlockMarbleSlab) block).isBottom) {
				BlockMarbleSlab slab = (BlockMarbleSlab) block;
				MinecraftForge.setBlockHarvestLevel(slab.top, variation.metadata, "chisel", 0);
				Carving.chisel.addVariation(name + ".top", slab.top.blockID, variation.metadata, 0);
				Carving.chisel.setGroupClass(name + ".top", name);
			}
		}

		chiselBlocks.add(block);
	}

	public static void registerVariation(String name, CarvableVariation variation, Block block, int blockMeta) {
		LanguageRegistry.addName(new ItemStack(block.blockID, 1, blockMeta), variation.description);

		if (variation.block == null) {
			Carving.chisel.addVariation(name, block.blockID, blockMeta, variation.metadata);
			MinecraftForge.setBlockHarvestLevel(block, blockMeta, "chisel", 0);
		} else {
			Carving.chisel.addVariation(name, variation.block.blockID, variation.blockMeta, variation.metadata);
			MinecraftForge.setBlockHarvestLevel(variation.block, variation.blockMeta, "chisel", 0);
		}
	}

	public void registerIcons(String modName, Block block, IconRegister register) {
		modName=modName.toLowerCase();
		
		for (CarvableVariation variation : variations) {
			if (variation.block != null) {
				variation.block.registerIcons(register);

				if (variation.block instanceof BlockPane) {
					variation.icon = variation.block.getBlockTextureFromSide(2);
					variation.iconTop = ((BlockPane) variation.block).getSideTextureIndex();
					variation.iconBot = ((BlockPane) variation.block).getSideTextureIndex();
				} else {
					switch (variation.kind) {
					case 0:
						variation.icon = variation.block.getIcon(2, variation.blockMeta);
						break;
					case 1:
						variation.icon = variation.block.getIcon(2, variation.blockMeta);
						variation.iconTop = variation.block.getIcon(0, variation.blockMeta);
						break;
					case 2:
						variation.icon = variation.block.getIcon(2, variation.blockMeta);
						variation.iconTop = variation.block.getIcon(1, variation.blockMeta);
						variation.iconBot = variation.block.getIcon(0, variation.blockMeta);
						break;
					}
				}
			} else {
				switch (variation.kind) {
				case 0:
					variation.icon = register.registerIcon(modName + ":" + variation.texture);
					break;
				case 1:
					variation.icon = register.registerIcon(modName + ":" + variation.texture + "-side");
					variation.iconTop = register.registerIcon(modName + ":" + variation.texture + "-top");
					break;
				case 2:
					variation.icon = register.registerIcon(modName + ":" + variation.texture + "-side");
					variation.iconTop = register.registerIcon(modName + ":" + variation.texture + "-top");
					variation.iconBot = register.registerIcon(modName + ":" + variation.texture + "-bottom");
					break;
				case 3:
					CarvableVariationCTM ctm=new CarvableVariationCTM();
					ctm.seams[0]=new TextureSubmap(register.registerIcon(modName + ":" + variation.texture+"-ctm1"),4,4);
					ctm.seams[1]=new TextureSubmap(register.registerIcon(modName + ":" + variation.texture+"-ctm2"),4,4);
					ctm.seams[2]=new TextureSubmap(register.registerIcon(modName + ":" + variation.texture+"-ctm3"),4,4);
					variation.ctm=ctm;
					break;
				case 4:
					variation.seamsCtmVert=new TextureSubmap(register.registerIcon(modName + ":" + variation.texture+"-ctmv"),2,2);
					variation.iconTop = register.registerIcon(modName + ":" + variation.texture + "-top");
					break;
				case 5:
					variation.seamsCtmVert=new TextureSubmap(register.registerIcon(modName + ":" + variation.texture+"-ctmh"),2,2);
					variation.iconTop = register.registerIcon(modName + ":" + variation.texture + "-top");
					break;
				case 6:
					variation.seamsCtmVert=new TextureSubmap(register.registerIcon(modName + ":" + variation.texture+"-ctmh"),2,2);
					break;
				}
			}
		}
	}

	public void registerSubBlocks(Block block, CreativeTabs tabs, List list) {
		for (CarvableVariation variation : variations) {
			if ((!Chisel.overrideVanillaBlocks) && variation.block != null)
				continue;

			list.add(new ItemStack(block.blockID, 1, variation.metadata));
		}
	}

	public void setBlockHarvestLevel(Block block, String tool, int level) {
		for (CarvableVariation variation : variations) {
			MinecraftForge.setBlockHarvestLevel(block, variation.metadata, tool, level);
		}
	}

	public int getTexture(IBlockAccess world, int x, int y, int z, int side) {
		int texture = 0;
		int blockId=world.getBlockId(x, y, z);
		int blockMetadata=world.getBlockMetadata(x, y, z);
		
		boolean b[] = new boolean[6];
		if (side <= 1) {
			b[0] = isConnected(world, x - 1, y, z, side, blockId, blockMetadata);
			b[1] = isConnected(world, x + 1, y, z, side, blockId, blockMetadata);
			b[2] = isConnected(world, x, y, z + 1, side, blockId, blockMetadata);
			b[3] = isConnected(world, x, y, z - 1, side, blockId, blockMetadata);
		} else if (side == 2) {
			b[0] = isConnected(world, x + 1, y, z, side, blockId, blockMetadata);
			b[1] = isConnected(world, x - 1, y, z, side, blockId, blockMetadata);
			b[2] = isConnected(world, x, y - 1, z, side, blockId, blockMetadata);
			b[3] = isConnected(world, x, y + 1, z, side, blockId, blockMetadata);
		} else if (side == 3) {
			b[0] = isConnected(world, x - 1, y, z, side, blockId, blockMetadata);
			b[1] = isConnected(world, x + 1, y, z, side, blockId, blockMetadata);
			b[2] = isConnected(world, x, y - 1, z, side, blockId, blockMetadata);
			b[3] = isConnected(world, x, y + 1, z, side, blockId, blockMetadata);
		} else if (side == 4) {
			b[0] = isConnected(world, x, y, z - 1, side, blockId, blockMetadata);
			b[1] = isConnected(world, x, y, z + 1, side, blockId, blockMetadata);
			b[2] = isConnected(world, x, y - 1, z, side, blockId, blockMetadata);
			b[3] = isConnected(world, x, y + 1, z, side, blockId, blockMetadata);
		} else if (side == 5) {
			b[0] = isConnected(world, x, y, z + 1, side, blockId, blockMetadata);
			b[1] = isConnected(world, x, y, z - 1, side, blockId, blockMetadata);
			b[2] = isConnected(world, x, y - 1, z, side, blockId, blockMetadata);
			b[3] = isConnected(world, x, y + 1, z, side, blockId, blockMetadata);
		}
		if (b[0] & !b[1] & !b[2] & !b[3])
			texture = 3;
		else if (!b[0] & b[1] & !b[2] & !b[3])
			texture = 1;
		else if (!b[0] & !b[1] & b[2] & !b[3])
			texture = 16;
		else if (!b[0] & !b[1] & !b[2] & b[3])
			texture = 48;
		else if (b[0] & b[1] & !b[2] & !b[3])
			texture = 2;
		else if (!b[0] & !b[1] & b[2] & b[3])
			texture = 32;
		else if (b[0] & !b[1] & b[2] & !b[3])
			texture = 19;
		else if (b[0] & !b[1] & !b[2] & b[3])
			texture = 51;
		else if (!b[0] & b[1] & b[2] & !b[3])
			texture = 17;
		else if (!b[0] & b[1] & !b[2] & b[3])
			texture = 49;
		else if (!b[0] & b[1] & b[2] & b[3])
			texture = 33;
		else if (b[0] & !b[1] & b[2] & b[3])
			texture = 35;
		else if (b[0] & b[1] & !b[2] & b[3])
			texture = 50;
		else if (b[0] & b[1] & b[2] & !b[3])
			texture = 18;
		else if (b[0] & b[1] & b[2] & b[3])
			texture = 34;

		boolean b2[] = new boolean[6];
		if (side <= 1) {
			b2[0] = !isConnected(world, x + 1, y, z + 1, side, blockId, blockMetadata);
			b2[1] = !isConnected(world, x - 1, y, z + 1, side, blockId, blockMetadata);
			b2[2] = !isConnected(world, x + 1, y, z - 1, side, blockId, blockMetadata);
			b2[3] = !isConnected(world, x - 1, y, z - 1, side, blockId, blockMetadata);
		} else if (side == 2) {
			b2[0] = !isConnected(world, x - 1, y - 1, z, side, blockId, blockMetadata);
			b2[1] = !isConnected(world, x + 1, y - 1, z, side, blockId, blockMetadata);
			b2[2] = !isConnected(world, x - 1, y + 1, z, side, blockId, blockMetadata);
			b2[3] = !isConnected(world, x + 1, y + 1, z, side, blockId, blockMetadata);
		} else if (side == 3) {
			b2[0] = !isConnected(world, x + 1, y - 1, z, side, blockId, blockMetadata);
			b2[1] = !isConnected(world, x - 1, y - 1, z, side, blockId, blockMetadata);
			b2[2] = !isConnected(world, x + 1, y + 1, z, side, blockId, blockMetadata);
			b2[3] = !isConnected(world, x - 1, y + 1, z, side, blockId, blockMetadata);
		} else if (side == 4) {
			b2[0] = !isConnected(world, x, y - 1, z + 1, side, blockId, blockMetadata);
			b2[1] = !isConnected(world, x, y - 1, z - 1, side, blockId, blockMetadata);
			b2[2] = !isConnected(world, x, y + 1, z + 1, side, blockId, blockMetadata);
			b2[3] = !isConnected(world, x, y + 1, z - 1, side, blockId, blockMetadata);
		} else if (side == 5) {
			b2[0] = !isConnected(world, x, y - 1, z - 1, side, blockId, blockMetadata);
			b2[1] = !isConnected(world, x, y - 1, z + 1, side, blockId, blockMetadata);
			b2[2] = !isConnected(world, x, y + 1, z - 1, side, blockId, blockMetadata);
			b2[3] = !isConnected(world, x, y + 1, z + 1, side, blockId, blockMetadata);
		}

		if (texture == 17 && b2[0])
			texture = 4;
		if (texture == 19 && b2[1])
			texture = 5;
		if (texture == 49 && b2[2])
			texture = 20;
		if (texture == 51 && b2[3])
			texture = 21;

		if (texture == 18 && b2[0] && b2[1])
			texture = 7;
		if (texture == 33 && b2[0] && b2[2])
			texture = 6;
		if (texture == 35 && b2[3] && b2[1])
			texture = 23;
		if (texture == 50 && b2[3] && b2[2])
			texture = 22;

		if (texture == 18 && !b2[0] && b2[1])
			texture = 39;
		if (texture == 33 && b2[0] && !b2[2])
			texture = 38;
		if (texture == 35 && !b2[3] && b2[1])
			texture = 53;
		if (texture == 50 && b2[3] && !b2[2])
			texture = 52;

		if (texture == 18 && b2[0] && !b2[1])
			texture = 37;
		if (texture == 33 && !b2[0] && b2[2])
			texture = 36;
		if (texture == 35 && b2[3] && !b2[1])
			texture = 55;
		if (texture == 50 && !b2[3] && b2[2])
			texture = 54;

		if (texture == 34 && b2[0] && b2[1] && b2[2] && b2[3])
			texture = 58;

		if (texture == 34 && !b2[0] && b2[1] && b2[2] && b2[3])
			texture = 9;
		if (texture == 34 && b2[0] && !b2[1] && b2[2] && b2[3])
			texture = 25;
		if (texture == 34 && b2[0] && b2[1] && !b2[2] && b2[3])
			texture = 8;
		if (texture == 34 && b2[0] && b2[1] && b2[2] && !b2[3])
			texture = 24;

		if (texture == 34 && b2[0] && b2[1] && !b2[2] && !b2[3])
			texture = 11;
		if (texture == 34 && !b2[0] && !b2[1] && b2[2] && b2[3])
			texture = 26;
		if (texture == 34 && !b2[0] && b2[1] && !b2[2] && b2[3])
			texture = 27;
		if (texture == 34 && b2[0] && !b2[1] && b2[2] && !b2[3])
			texture = 10;

		if (texture == 34 && b2[0] && !b2[1] && !b2[2] && b2[3])
			texture = 42;
		if (texture == 34 && !b2[0] && b2[1] && b2[2] && !b2[3])
			texture = 43;

		if (texture == 34 && b2[0] && !b2[1] && !b2[2] && !b2[3])
			texture = 40;
		if (texture == 34 && !b2[0] && b2[1] && !b2[2] && !b2[3])
			texture = 41;
		if (texture == 34 && !b2[0] && !b2[1] && b2[2] && !b2[3])
			texture = 56;
		if (texture == 34 && !b2[0] && !b2[1] && !b2[2] && b2[3])
			texture = 57;
		return texture;
	}

	private boolean isSame(IBlockAccess world, int x, int y, int z,int id,int meta) {
		return world.getBlockId(x, y, z) == id && world.getBlockMetadata(x, y, z) == meta;
	}
	
	private boolean isConnected(IBlockAccess world, int x, int y, int z, int side,int id, int meta) {
		int x2=x,y2=y,z2=z;
		
		switch(side){
		case 0: y2--; break;
		case 1: y2++; break;
		case 2: z2--; break;
		case 3: z2++; break;
		case 4: x2--; break;
		case 5: x2++; break;
		}
		
		return 
				world.getBlockId(x, y, z) == id &&
				world.getBlockMetadata(x, y, z) == meta &&
				(world.getBlockId(x2, y2, z2) != id || world.getBlockMetadata(x2, y2, z2) != meta)
		;
	}
		

}
