package common.block.artificial;

import java.util.List;

import common.block.Block;
import common.block.Rotatable;
import common.block.Material;
import common.collect.Lists;
import common.entity.npc.EntityNPC;
import common.init.Blocks;
import common.init.Items;
import common.item.Item;
import common.item.block.ItemDoor;
import common.model.BlockLayer;
import common.model.Model;
import common.model.ModelProvider;
import common.model.ModelRotation;
import common.properties.Property;
import common.properties.PropertyBool;
import common.properties.PropertyEnum;
import common.rng.Random;
import common.util.BlockPos;
import common.util.BoundingBox;
import common.util.Facing;
import common.util.HitPosition;
import common.util.Identifyable;
import common.util.Vec3;
import common.world.IBlockAccess;
import common.world.IWorldAccess;
import common.world.State;
import common.world.World;

public class BlockDoor extends Block implements Rotatable
{
    public static final PropertyBool OPEN = PropertyBool.create("open");
    public static final PropertyEnum<BlockDoor.EnumHingePosition> HINGE = PropertyEnum.<BlockDoor.EnumHingePosition>create("hinge", BlockDoor.EnumHingePosition.class);
    public static final PropertyBool POWERED = PropertyBool.create("powered");
    public static final PropertyEnum<BlockDoor.EnumDoorHalf> HALF = PropertyEnum.<BlockDoor.EnumDoorHalf>create("half", BlockDoor.EnumDoorHalf.class);
    public static final List<BlockDoor> DOORS = Lists.newArrayList();

    public BlockDoor(Material materialIn)
    {
        super(materialIn);
        this.setDefaultState(this.getBaseState().withProperty(FACING, Facing.NORTH).withProperty(OPEN, Boolean.valueOf(false)).withProperty(HINGE, BlockDoor.EnumHingePosition.LEFT).withProperty(POWERED, Boolean.valueOf(false)).withProperty(HALF, BlockDoor.EnumDoorHalf.LOWER));
        DOORS.add(this);
    }

//    /**
//     * Gets the localized name of this block. Used for the statistics page.
//     */
//    public String getLocalizedName()
//    {
//        return Strs.get((this.getUnlocalizedName() + ".name").replaceAll("tile", "item"));
//    }

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

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

    public boolean isFullCube()
    {
        return false;
    }

    public BoundingBox getSelectedBoundingBox(World worldIn, BlockPos pos)
    {
        this.setBlockBoundsBasedOnState(worldIn, pos);
        return super.getSelectedBoundingBox(worldIn, pos);
    }

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

    public void setBlockBoundsBasedOnState(IWorldAccess worldIn, BlockPos pos)
    {
        this.setBoundBasedOnMeta(combineMetadata(worldIn, pos));
    }

    private void setBoundBasedOnMeta(int combinedMeta)
    {
        float f = 0.1875F;
        this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 2.0F, 1.0F);
        Facing enumfacing = getFacing(combinedMeta);
        boolean flag = isOpen(combinedMeta);
        boolean flag1 = isHingeLeft(combinedMeta);

        if (flag)
        {
            if (enumfacing == Facing.EAST)
            {
                if (!flag1)
                {
                    this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, f);
                }
                else
                {
                    this.setBlockBounds(0.0F, 0.0F, 1.0F - f, 1.0F, 1.0F, 1.0F);
                }
            }
            else if (enumfacing == Facing.SOUTH)
            {
                if (!flag1)
                {
                    this.setBlockBounds(1.0F - f, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F);
                }
                else
                {
                    this.setBlockBounds(0.0F, 0.0F, 0.0F, f, 1.0F, 1.0F);
                }
            }
            else if (enumfacing == Facing.WEST)
            {
                if (!flag1)
                {
                    this.setBlockBounds(0.0F, 0.0F, 1.0F - f, 1.0F, 1.0F, 1.0F);
                }
                else
                {
                    this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, f);
                }
            }
            else if (enumfacing == Facing.NORTH)
            {
                if (!flag1)
                {
                    this.setBlockBounds(0.0F, 0.0F, 0.0F, f, 1.0F, 1.0F);
                }
                else
                {
                    this.setBlockBounds(1.0F - f, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F);
                }
            }
        }
        else if (enumfacing == Facing.EAST)
        {
            this.setBlockBounds(0.0F, 0.0F, 0.0F, f, 1.0F, 1.0F);
        }
        else if (enumfacing == Facing.SOUTH)
        {
            this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, f);
        }
        else if (enumfacing == Facing.WEST)
        {
            this.setBlockBounds(1.0F - f, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F);
        }
        else if (enumfacing == Facing.NORTH)
        {
            this.setBlockBounds(0.0F, 0.0F, 1.0F - f, 1.0F, 1.0F, 1.0F);
        }
    }

    public boolean onBlockActivated(World worldIn, BlockPos pos, State state, EntityNPC playerIn, Facing side, float hitX, float hitY, float hitZ)
    {
        if (this.material == Material.SOLID)
        {
            return true;
        }
        else
        {
            BlockPos blockpos = state.getValue(HALF) == BlockDoor.EnumDoorHalf.LOWER ? pos : pos.down();
            State iblockstate = pos.equals(blockpos) ? state : worldIn.getState(blockpos);

            if (iblockstate.getBlock() != this)
            {
                return false;
            }
            else
            {
                state = iblockstate.cycleProperty(OPEN);
                worldIn.setState(blockpos, state, 2);
                worldIn.markBlockRangeForRenderUpdate(blockpos, pos);
                worldIn.playAuxSFX(playerIn, ((Boolean)state.getValue(OPEN)).booleanValue() ? 1003 : 1006, pos, 0);
                return true;
            }
        }
    }

    public void toggleDoor(World worldIn, BlockPos pos, boolean open)
    {
        State iblockstate = worldIn.getState(pos);

        if (iblockstate.getBlock() == this)
        {
            BlockPos blockpos = iblockstate.getValue(HALF) == BlockDoor.EnumDoorHalf.LOWER ? pos : pos.down();
            State iblockstate1 = pos == blockpos ? iblockstate : worldIn.getState(blockpos);

            if (iblockstate1.getBlock() == this && ((Boolean)iblockstate1.getValue(OPEN)).booleanValue() != open)
            {
                worldIn.setState(blockpos, iblockstate1.withProperty(OPEN, Boolean.valueOf(open)), 2);
                worldIn.markBlockRangeForRenderUpdate(blockpos, pos);
                worldIn.playAuxSFX(open ? 1003 : 1006, pos, 0);
            }
        }
    }

    /**
     * Called when a neighboring block changes.
     */
    public void onNeighborBlockChange(World worldIn, BlockPos pos, State state, Block neighborBlock)
    {
        if (state.getValue(HALF) == BlockDoor.EnumDoorHalf.UPPER)
        {
            BlockPos blockpos = pos.down();
            State iblockstate = worldIn.getState(blockpos);

            if (iblockstate.getBlock() != this)
            {
                worldIn.setBlockToAir(pos);
            }
            else if (neighborBlock != this)
            {
                this.onNeighborBlockChange(worldIn, blockpos, iblockstate, neighborBlock);
            }
        }
        else
        {
            boolean flag1 = false;
            BlockPos blockpos1 = pos.up();
            State iblockstate1 = worldIn.getState(blockpos1);

            if (iblockstate1.getBlock() != this)
            {
                worldIn.setBlockToAir(pos);
                flag1 = true;
            }

            if (!worldIn.isBlockSolid(pos.down()))
            {
                worldIn.setBlockToAir(pos);
                flag1 = true;

                if (iblockstate1.getBlock() == this)
                {
                    worldIn.setBlockToAir(blockpos1);
                }
            }

            if (flag1)
            {
                if (!worldIn.client)
                {
                    this.dropBlockAsItem(worldIn, pos, state, 0);
                }
            }
            else
            {
                boolean flag = worldIn.isBlockPowered(pos) || worldIn.isBlockPowered(blockpos1);

                if ((flag || neighborBlock.canProvidePower()) && neighborBlock != this && flag != ((Boolean)iblockstate1.getValue(POWERED)).booleanValue())
                {
                    worldIn.setState(blockpos1, iblockstate1.withProperty(POWERED, Boolean.valueOf(flag)), 2);

                    if (flag != ((Boolean)state.getValue(OPEN)).booleanValue())
                    {
                        worldIn.setState(pos, state.withProperty(OPEN, Boolean.valueOf(flag)), 2);
                        worldIn.markBlockRangeForRenderUpdate(pos, pos);
                        worldIn.playAuxSFX(flag ? 1003 : 1006, pos, 0);
                    }
                }
            }
        }
    }

    /**
     * Get the Item that this Block should drop when harvested.
     */
    public Item getItemDropped(State state, Random rand, int fortune)
    {
        return state.getValue(HALF) == BlockDoor.EnumDoorHalf.UPPER ? null : this.getDoorItem();
    }

    /**
     * Ray traces through the blocks collision from start vector to end vector returning a ray trace hit.
     */
    public HitPosition collisionRayTrace(World worldIn, BlockPos pos, Vec3 start, Vec3 end)
    {
        this.setBlockBoundsBasedOnState(worldIn, pos);
        return super.collisionRayTrace(worldIn, pos, start, end);
    }

    public boolean canPlaceBlockAt(World worldIn, BlockPos pos)
    {
        return pos.getY() >= World.MAX_SIZE_Y - 1 ? false : worldIn.isBlockSolid(pos.down()) && super.canPlaceBlockAt(worldIn, pos) && super.canPlaceBlockAt(worldIn, pos.up());
    }

    public int getMobilityFlag()
    {
        return 1;
    }

    public static int combineMetadata(IBlockAccess worldIn, BlockPos pos)
    {
        State iblockstate = worldIn.getState(pos);
        int i = iblockstate.getBlock().getMetaFromState(iblockstate);
        boolean flag = isTop(i);
        State iblockstate1 = worldIn.getState(pos.down());
        int j = iblockstate1.getBlock().getMetaFromState(iblockstate1);
        int k = flag ? j : i;
        State iblockstate2 = worldIn.getState(pos.up());
        int l = iblockstate2.getBlock().getMetaFromState(iblockstate2);
        int i1 = flag ? i : l;
        boolean flag1 = (i1 & 1) != 0;
        boolean flag2 = (i1 & 2) != 0;
        return removeHalfBit(k) | (flag ? 8 : 0) | (flag1 ? 16 : 0) | (flag2 ? 32 : 0);
    }

    public Item getItem(World worldIn, BlockPos pos)
    {
        return this.getDoorItem();
    }

    private Item getDoorItem()
    {
        return this == Blocks.iron_door ? Items.iron_door : (this == Blocks.spruce_door ? Items.spruce_door : (this == Blocks.birch_door ? Items.birch_door : (this == Blocks.jungle_door ? Items.jungle_door : (this == Blocks.acacia_door ? Items.acacia_door : (this == Blocks.dark_oak_door ? Items.dark_oak_door : (this == Blocks.cherry_door ? Items.cherry_door : (this == Blocks.maple_door ? Items.maple_door : Items.oak_door)))))));
    }

//    public void onBlockHarvested(World worldIn, BlockPos pos, State state, EntityNPC player)
//    {
//        BlockPos blockpos = pos.down();
//
//        if (player.creative && state.getValue(HALF) == BlockDoor.EnumDoorHalf.UPPER && worldIn.getState(blockpos).getBlock() == this)
//        {
//            worldIn.setBlockToAir(blockpos);
//        }
//    }

    public BlockLayer getBlockLayer()
    {
        return BlockLayer.CUTOUT;
    }

    /**
     * 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)
    {
        if (state.getValue(HALF) == BlockDoor.EnumDoorHalf.LOWER)
        {
            State iblockstate = worldIn.getState(pos.up());

            if (iblockstate.getBlock() == this)
            {
                state = state.withProperty(HINGE, iblockstate.getValue(HINGE)).withProperty(POWERED, iblockstate.getValue(POWERED));
            }
        }
        else
        {
            State iblockstate1 = worldIn.getState(pos.down());

            if (iblockstate1.getBlock() == this)
            {
                state = state.withProperty(FACING, iblockstate1.getValue(FACING)).withProperty(OPEN, iblockstate1.getValue(OPEN));
            }
        }

        return state;
    }

    /**
     * Convert the given metadata into a BlockState for this Block
     */
    public State getStateFromMeta(int meta)
    {
        return (meta & 8) > 0 ? this.getState().withProperty(HALF, BlockDoor.EnumDoorHalf.UPPER).withProperty(HINGE, (meta & 1) > 0 ? BlockDoor.EnumHingePosition.RIGHT : BlockDoor.EnumHingePosition.LEFT).withProperty(POWERED, Boolean.valueOf((meta & 2) > 0)) : this.getState().withProperty(HALF, BlockDoor.EnumDoorHalf.LOWER).withProperty(FACING, Facing.getHorizontal(meta & 3).rotateYCCW()).withProperty(OPEN, Boolean.valueOf((meta & 4) > 0));
    }

    /**
     * Convert the BlockState into the correct metadata value
     */
    public int getMetaFromState(State state)
    {
        int i = 0;

        if (state.getValue(HALF) == BlockDoor.EnumDoorHalf.UPPER)
        {
            i = i | 8;

            if (state.getValue(HINGE) == BlockDoor.EnumHingePosition.RIGHT)
            {
                i |= 1;
            }

            if (((Boolean)state.getValue(POWERED)).booleanValue())
            {
                i |= 2;
            }
        }
        else
        {
            i = i | ((Facing)state.getValue(FACING)).rotateY().getHorizontalIndex();

            if (((Boolean)state.getValue(OPEN)).booleanValue())
            {
                i |= 4;
            }
        }

        return i;
    }

    protected static int removeHalfBit(int meta)
    {
        return meta & 7;
    }

    public static boolean isOpen(IBlockAccess worldIn, BlockPos pos)
    {
        return isOpen(combineMetadata(worldIn, pos));
    }

    public static Facing getFacing(IBlockAccess worldIn, BlockPos pos)
    {
        return getFacing(combineMetadata(worldIn, pos));
    }

    public static Facing getFacing(int combinedMeta)
    {
        return Facing.getHorizontal(combinedMeta & 3).rotateYCCW();
    }

    protected static boolean isOpen(int combinedMeta)
    {
        return (combinedMeta & 4) != 0;
    }

    protected static boolean isTop(int meta)
    {
        return (meta & 8) != 0;
    }

    protected static boolean isHingeLeft(int combinedMeta)
    {
        return (combinedMeta & 16) != 0;
    }

    protected Property[] getProperties()
    {
        return new Property[] {HALF, FACING, OPEN, HINGE, POWERED};
    }
    
    private static ModelRotation getRotation(Facing rot, int offset) {
    	return ModelRotation.getEastRot(Facing.getHorizontal(rot.getHorizontalIndex() + offset), false);
    }

    public Model getModel(ModelProvider provider, String name, State state) {
    	String bottom = name + "_bottom";
    	String top = name + "_top";
    	if(state.getValue(HALF) == EnumDoorHalf.LOWER) {
    		if(state.getValue(HINGE) == EnumHingePosition.LEFT == state.getValue(OPEN))
    			return provider.getModel(bottom)
    					.add(0, 0, 0, 3, 16, 16)
    						.d().uv(13, 0, 16, 16)
    						.n().uv(3, 0, 0, 16)
    						.s().uv(0, 0, 3, 16)
    						.w().uv(16, 0, 0, 16)
    						.e().uv(0, 0, 16, 16).noCull()
    						.rotate(getRotation(state.getValue(FACING), state.getValue(OPEN) ? 1 : 0));
    		else
    			return provider.getModel(bottom)
    					.add(0, 0, 0, 3, 16, 16)
    						.d().uv(13, 0, 16, 16)
    						.n().uv(3, 0, 0, 16)
    						.s().uv(0, 0, 3, 16)
    						.w().uv(0, 0, 16, 16)
    						.e().uv(16, 0, 0, 16).noCull()
    						.rotate(getRotation(state.getValue(FACING), state.getValue(OPEN) ? 3 : 0));
    	}
    	else {
    		if(state.getValue(HINGE) == EnumHingePosition.LEFT == state.getValue(OPEN))
    			return provider.getModel(top)
    					.add(0, 0, 0, 3, 16, 16)
    						.u(bottom).uv(13, 0, 16, 16)
    						.n().uv(3, 0, 0, 16)
    						.s().uv(0, 0, 3, 16)
    						.w().uv(16, 0, 0, 16)
    						.e().uv(0, 0, 16, 16).noCull()
    						.rotate(getRotation(state.getValue(FACING), state.getValue(OPEN) ? 1 : 0));
    		else
    			return provider.getModel(top)
    					.add(0, 0, 0, 3, 16, 16)
    						.u(bottom).uv(13, 0, 16, 16)
    						.n().uv(3, 0, 0, 16)
    						.s().uv(0, 0, 3, 16)
    						.w().uv(0, 0, 16, 16)
    						.e().uv(16, 0, 0, 16).noCull()
    						.rotate(getRotation(state.getValue(FACING), state.getValue(OPEN) ? 3 : 0));
    	}
    }

    public Property<?>[] getIgnoredProperties() {
    	return new Property[] {POWERED};
    }
	
	protected Item getItemToRegister() {
		return new ItemDoor(this);
	}

    public static enum EnumDoorHalf implements Identifyable
    {
        UPPER,
        LOWER;

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

        public String getName()
        {
            return this == UPPER ? "upper" : "lower";
        }
    }

    public static enum EnumHingePosition implements Identifyable
    {
        LEFT,
        RIGHT;

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

        public String getName()
        {
            return this == LEFT ? "left" : "right";
        }
    }
}
