package common.block;

import java.util.List;

import common.item.CheatTab;
import common.item.Item;
import common.item.ItemStack;
import common.material.Material;
import common.model.Model;
import common.model.ModelProvider;
import common.model.ModelRotation;
import common.properties.IProperty;
import common.properties.PropertyBool;
import common.properties.PropertyEnum;
import common.util.BlockPos;
import common.util.BoundingBox;
import common.util.Facing;
import common.util.Identifyable;
import common.world.IBlockAccess;
import common.world.IWorldAccess;
import common.world.State;
import common.world.World;

public class BlockWall extends Block
{
    public static final PropertyBool UP = PropertyBool.create("up");
    public static final PropertyBool NORTH = PropertyBool.create("north");
    public static final PropertyBool EAST = PropertyBool.create("east");
    public static final PropertyBool SOUTH = PropertyBool.create("south");
    public static final PropertyBool WEST = PropertyBool.create("west");
    public static final PropertyEnum<BlockWall.EnumType> VARIANT = PropertyEnum.<BlockWall.EnumType>create("variant", BlockWall.EnumType.class);

    public BlockWall(Block modelBlock)
    {
        super(modelBlock.material);
        this.setDefaultState(this.getBaseState().withProperty(UP, Boolean.valueOf(false)).withProperty(NORTH, Boolean.valueOf(false)).withProperty(EAST, Boolean.valueOf(false)).withProperty(SOUTH, Boolean.valueOf(false)).withProperty(WEST, Boolean.valueOf(false)).withProperty(VARIANT, BlockWall.EnumType.NORMAL));
        this.setHardness(modelBlock.blockHardness);
        this.setResistance(modelBlock.blockResistance / 3.0F);
        this.setStepSound(modelBlock.sound);
        this.setTab(CheatTab.tabBlocks);
    }

//    /**
//     * Gets the localized name of this block. Used for the statistics page.
//     */
//    public String getLocalizedName()
//    {
//        return "Bruchsteinmauer";
//    }

    public boolean isFullCube()
    {
        return false;
    }

    public boolean isPassable(IBlockAccess worldIn, BlockPos pos)
    {
        return false;
    }

    /**
     * Used to determine ambient occlusion and culling when rebuilding chunks for render
     */
    public boolean isOpaqueCube()
    {
        return false;
    }

    public void setBlockBoundsBasedOnState(IWorldAccess worldIn, BlockPos pos)
    {
        boolean flag = this.canConnectTo(worldIn, pos.north());
        boolean flag1 = this.canConnectTo(worldIn, pos.south());
        boolean flag2 = this.canConnectTo(worldIn, pos.west());
        boolean flag3 = this.canConnectTo(worldIn, pos.east());
        float f = 0.25F;
        float f1 = 0.75F;
        float f2 = 0.25F;
        float f3 = 0.75F;
        float f4 = 1.0F;

        if (flag)
        {
            f2 = 0.0F;
        }

        if (flag1)
        {
            f3 = 1.0F;
        }

        if (flag2)
        {
            f = 0.0F;
        }

        if (flag3)
        {
            f1 = 1.0F;
        }

        if (flag && flag1 && !flag2 && !flag3)
        {
            f4 = 0.8125F;
            f = 0.3125F;
            f1 = 0.6875F;
        }
        else if (!flag && !flag1 && flag2 && flag3)
        {
            f4 = 0.8125F;
            f2 = 0.3125F;
            f3 = 0.6875F;
        }

        this.setBlockBounds(f, 0.0F, f2, f1, f4, f3);
    }

    public BoundingBox getCollisionBoundingBox(World worldIn, BlockPos pos, State state)
    {
        this.setBlockBoundsBasedOnState(worldIn, pos);
        this.maxY = 1.5D;
        return super.getCollisionBoundingBox(worldIn, pos, state);
    }

    public boolean canConnectTo(IBlockAccess worldIn, BlockPos pos)
    {
        Block block = worldIn.getState(pos).getBlock();
        return (block != this && !(block instanceof BlockFenceGate) ? (block.material.isOpaque() && block.isFullCube() ? block.material != Material.gourd : false) : true);
    }

    /**
     * returns a list of blocks with the same ID, but different meta (eg: wood returns 4 blocks)
     */
    public void getSubBlocks(Item itemIn, CheatTab tab, List<ItemStack> list)
    {
        for (BlockWall.EnumType blockwall$enumtype : BlockWall.EnumType.values())
        {
            list.add(new ItemStack(itemIn, 1, blockwall$enumtype.getMetadata()));
        }
    }

    /**
     * Gets the metadata of the item this Block can drop. This method is called when the block gets destroyed. It
     * returns the metadata of the dropped item based on the old metadata of the block.
     */
    public int damageDropped(State state)
    {
        return ((BlockWall.EnumType)state.getValue(VARIANT)).getMetadata();
    }

    public boolean shouldSideBeRendered(IWorldAccess worldIn, BlockPos pos, Facing side)
    {
        return side == Facing.DOWN ? super.shouldSideBeRendered(worldIn, pos, side) : true;
    }

    /**
     * Convert the given metadata into a BlockState for this Block
     */
    public State getStateFromMeta(int meta)
    {
        return this.getState().withProperty(VARIANT, BlockWall.EnumType.byMetadata(meta));
    }

    /**
     * Convert the BlockState into the correct metadata value
     */
    public int getMetaFromState(State state)
    {
        return ((BlockWall.EnumType)state.getValue(VARIANT)).getMetadata();
    }

    /**
     * Get the actual Block state of this Block at the given position. This applies properties not visible in the
     * metadata, such as fence connections.
     */
    public State getActualState(State state, IWorldAccess worldIn, BlockPos pos)
    {
        return state.withProperty(UP, Boolean.valueOf(worldIn.getState(pos.up()).getBlock().getMaterial() != Material.air)).withProperty(NORTH, Boolean.valueOf(this.canConnectTo(worldIn, pos.north()))).withProperty(EAST, Boolean.valueOf(this.canConnectTo(worldIn, pos.east()))).withProperty(SOUTH, Boolean.valueOf(this.canConnectTo(worldIn, pos.south()))).withProperty(WEST, Boolean.valueOf(this.canConnectTo(worldIn, pos.west())));
    }

    protected IProperty[] getProperties()
    {
        return new IProperty[] {UP, NORTH, EAST, WEST, SOUTH, VARIANT};
    }

    public Model getModel(ModelProvider provider, String name, State state) {
    	String wall = state.getValue(VARIANT).getName();
    	boolean n = state.getValue(NORTH);
    	boolean s = state.getValue(SOUTH);
    	boolean w = state.getValue(WEST);
    	boolean e = state.getValue(EAST);
    	int sides = (n ? 1 : 0) + (s ? 1 : 0) + (w ? 1 : 0) + (e ? 1 : 0);
    	if(sides == 0)
    		return provider.getModel(wall)
    				.add(4, 0, 4, 12, 16, 12)
    					.d().uv(4, 4, 12, 12)
    					.u().uv(4, 4, 12, 12)
    					.n().uv(4, 0, 12, 16).noCull()
    					.s().uv(4, 0, 12, 16).noCull()
    					.w().uv(4, 0, 12, 16).noCull()
    					.e().uv(4, 0, 12, 16).noCull();
        else if(sides == 4)
    		return provider.getModel(wall)
    				.add(4, 0, 4, 12, 16, 12)
    					.d().uv(4, 4, 12, 12)
    					.u().uv(4, 4, 12, 12)
    					.n().uv(4, 0, 12, 16).noCull()
    					.s().uv(4, 0, 12, 16).noCull()
    					.w().uv(4, 0, 12, 16).noCull()
    					.e().uv(4, 0, 12, 16).noCull()
    				.add(5, 0, 0, 11, 13, 4)
    					.d().uv(5, 0, 11, 4)
    					.u().uv(5, 0, 11, 4).noCull()
    					.n().uv(5, 3, 11, 16)
    					.w().uv(0, 3, 4, 16).noCull()
    					.e().uv(0, 3, 4, 16).noCull()
    				.add(5, 0, 12, 11, 13, 16)
    					.d().uv(5, 12, 11, 16)
    					.u().uv(5, 12, 11, 16).noCull()
    					.s().uv(5, 3, 11, 16)
    					.w().uv(12, 3, 16, 16).noCull()
    					.e().uv(12, 3, 16, 16).noCull()
    				.add(12, 0, 5, 16, 13, 11)
    					.d().uv(5, 12, 11, 16).rot(90)
    					.u().uv(5, 12, 11, 16).rot(90).noCull()
    					.n().uv(12, 3, 16, 16).noCull()
    					.s().uv(12, 3, 16, 16).noCull()
    					.e().uv(5, 3, 11, 16)
    				.add(0, 0, 5, 4, 13, 11)
    					.d().uv(5, 0, 11, 4).rot(90)
    					.u().uv(5, 0, 11, 4).rot(90).noCull()
    					.n().uv(0, 3, 4, 16).noCull()
    					.s().uv(0, 3, 4, 16).noCull()
    					.w().uv(5, 3, 11, 16);
    	else if(sides == 1)
    		return provider.getModel(wall).uvLock()
    				.add(4, 0, 4, 12, 16, 12)
    					.d().uv(4, 4, 12, 12)
    					.u().uv(4, 4, 12, 12)
    					.n().uv(4, 0, 12, 16).noCull()
    					.s().uv(4, 0, 12, 16).noCull()
    					.w().uv(4, 0, 12, 16).noCull()
    					.e().uv(4, 0, 12, 16).noCull()
    				.add(5, 0, 0, 11, 13, 4)
    					.d().uv(5, 0, 11, 4)
    					.u().uv(5, 0, 11, 4).noCull()
    					.n().uv(5, 3, 11, 16)
    					.w().uv(0, 3, 4, 16).noCull()
    					.e().uv(0, 3, 4, 16).noCull()
        				.rotate(n ? ModelRotation.X0_Y0 : (s ? ModelRotation.X0_Y180 : (w ? ModelRotation.X0_Y270 : ModelRotation.X0_Y90)));
    	else if(sides == 2 && ((e != w) || (n != s)))
    		return provider.getModel(wall).uvLock()
    				.add(4, 0, 4, 12, 16, 12)
    					.d().uv(4, 4, 12, 12)
    					.u().uv(4, 4, 12, 12)
    					.n().uv(4, 0, 12, 16).noCull()
    					.s().uv(4, 0, 12, 16).noCull()
    					.w().uv(4, 0, 12, 16).noCull()
    					.e().uv(4, 0, 12, 16).noCull()
    				.add(5, 0, 0, 11, 13, 4)
    					.d().uv(5, 0, 11, 4)
    					.u().uv(5, 0, 11, 4).noCull()
    					.n().uv(5, 3, 11, 16)
    					.w().uv(0, 3, 4, 16).noCull()
    					.e().uv(0, 3, 4, 16).noCull()
    				.add(12, 0, 5, 16, 13, 11)
    					.d().uv(5, 12, 11, 16).rot(90)
    					.u().uv(5, 12, 11, 16).rot(90).noCull()
    					.n().uv(12, 3, 16, 16).noCull()
    					.s().uv(12, 3, 16, 16).noCull()
    					.e().uv(5, 3, 11, 16)
    	        		.rotate(n && e ? ModelRotation.X0_Y0 : (s && w ? ModelRotation.X0_Y180 : 
    	        			(n && w ? ModelRotation.X0_Y270 : ModelRotation.X0_Y90)));
    	else if(sides == 2 && state.getValue(UP))
    		return provider.getModel(wall).uvLock()
    				.add(4, 0, 4, 12, 16, 12)
    					.d().uv(4, 4, 12, 12)
    					.u().uv(4, 4, 12, 12)
    					.n().uv(4, 0, 12, 16).noCull()
    					.s().uv(4, 0, 12, 16).noCull()
    					.w().uv(4, 0, 12, 16).noCull()
    					.e().uv(4, 0, 12, 16).noCull()
    				.add(5, 0, 0, 11, 13, 16)
    					.d().uv(5, 0, 11, 16)
    					.u().uv(5, 0, 11, 16).noCull()
    					.n().uv(5, 3, 11, 16)
    					.s().uv(5, 3, 11, 16)
    					.w().uv(0, 3, 16, 16).noCull()
    					.e().uv(0, 3, 16, 16).noCull()
        	        	.rotate(n ? ModelRotation.X0_Y0 : ModelRotation.X0_Y90);
        else if(sides == 2)
    		return provider.getModel(wall).uvLock()
    				.add(5, 0, 0, 11, 13, 16)
    					.d().uv(5, 0, 11, 16)
    					.u().uv(5, 0, 11, 16).noCull()
    					.n().uv(5, 3, 11, 16)
    					.s().uv(5, 3, 11, 16)
    					.w().uv(0, 3, 16, 16).noCull()
    					.e().uv(0, 3, 16, 16).noCull()
        	        	.rotate(n ? ModelRotation.X0_Y0 : ModelRotation.X0_Y90);
        else
    		return provider.getModel(wall).uvLock()
    				.add(4, 0, 4, 12, 16, 12)
    					.d().uv(4, 4, 12, 12)
    					.u().uv(4, 4, 12, 12)
    					.n().uv(4, 0, 12, 16).noCull()
    					.s().uv(4, 0, 12, 16).noCull()
    					.w().uv(4, 0, 12, 16).noCull()
    					.e().uv(4, 0, 12, 16).noCull()
    				.add(5, 0, 0, 11, 13, 4)
    					.d().uv(5, 0, 11, 4)
    					.u().uv(5, 0, 11, 4).noCull()
    					.n().uv(5, 3, 11, 16)
    					.w().uv(0, 3, 4, 16).noCull()
    					.e().uv(0, 3, 4, 16).noCull()
    				.add(5, 0, 12, 11, 13, 16)
    					.d().uv(5, 12, 11, 16)
    					.u().uv(5, 12, 11, 16).noCull()
    					.s().uv(5, 3, 11, 16)
    					.w().uv(12, 3, 16, 16).noCull()
    					.e().uv(12, 3, 16, 16).noCull()
    				.add(12, 0, 5, 16, 13, 11)
    					.d().uv(5, 12, 11, 16).rot(90)
    					.u().uv(5, 12, 11, 16).rot(90).noCull()
    					.n().uv(12, 3, 16, 16).noCull()
    					.s().uv(12, 3, 16, 16).noCull()
    					.e().uv(5, 3, 11, 16)
        	        	.rotate(!w ? ModelRotation.X0_Y0 : (!e ? ModelRotation.X0_Y180 : (!s ? ModelRotation.X0_Y270 : ModelRotation.X0_Y90)));
    }

    public static enum EnumType implements Identifyable
    {
        NORMAL(0, "cobblestone", "Bruchsteinmauer"),
        MOSSY(1, "mossy_cobblestone", "Bemooste Bruchsteinmauer");

        private static final BlockWall.EnumType[] META_LOOKUP = new BlockWall.EnumType[values().length];
        private final int meta;
        private final String name;
        private String display;

        private EnumType(int meta, String name, String display)
        {
            this.meta = meta;
            this.name = name;
            this.display = display;
        }

        public int getMetadata()
        {
            return this.meta;
        }

        public String toString()
        {
            return this.name;
        }

        public static BlockWall.EnumType byMetadata(int meta)
        {
            if (meta < 0 || meta >= META_LOOKUP.length)
            {
                meta = 0;
            }

            return META_LOOKUP[meta];
        }

        public String getName()
        {
            return this.name;
        }

        public String getDisplay()
        {
            return this.display;
        }

        static {
            for (BlockWall.EnumType blockwall$enumtype : values())
            {
                META_LOOKUP[blockwall$enumtype.getMetadata()] = blockwall$enumtype;
            }
        }
    }
}
