package common.block.tech;

import common.block.Block;
import common.block.Material;
import common.entity.npc.EntityNPC;
import common.entity.types.EntityLiving;
import common.init.SoundEvent;
import common.item.CheatTab;
import common.item.Item;
import common.item.block.ItemBlock;
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.util.BlockPos;
import common.util.BoundingBox;
import common.util.Facing;
import common.util.Identifyable;
import common.world.IWorldAccess;
import common.world.State;
import common.world.World;
import common.world.AWorldServer;

public class BlockLever extends Block
{
    public static final PropertyEnum<BlockLever.EnumOrientation> FACING = PropertyEnum.<BlockLever.EnumOrientation>create("facing", BlockLever.EnumOrientation.class);
    public static final PropertyBool POWERED = PropertyBool.create("powered");

    public BlockLever()
    {
        super(Material.SMALL);
        this.setDefaultState(this.getBaseState().withProperty(FACING, BlockLever.EnumOrientation.NORTH).withProperty(POWERED, Boolean.valueOf(false)));
        this.setTab(CheatTab.TECHNOLOGY);
    }

    public BoundingBox getCollisionBoundingBox(World worldIn, BlockPos pos, State state)
    {
        return null;
    }

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

    public boolean isFullCube()
    {
        return false;
    }

    /**
     * Check whether this Block can be placed on the given side
     */
    public boolean canPlaceBlockOnSide(World worldIn, BlockPos pos, Facing side)
    {
        return canPlaceLeverOn(worldIn, pos, side.getOpposite());
    }

    public boolean canPlaceBlockAt(World worldIn, BlockPos pos)
    {
        for (Facing enumfacing : Facing.values())
        {
            if (canPlaceLeverOn(worldIn, pos, enumfacing))
            {
                return true;
            }
        }

        return false;
    }

    protected static boolean canPlaceLeverOn(World worldIn, BlockPos pos, Facing face)
    {
        return BlockButton.canPlaceButtonOn(worldIn, pos, face);
    }

    /**
     * Called by ItemBlocks just before a block is actually set in the world, to allow for adjustments to the
     * IBlockstate
     */
    public State onBlockPlaced(World worldIn, BlockPos pos, Facing facing, float hitX, float hitY, float hitZ, EntityLiving placer)
    {
        State iblockstate = this.getState().withProperty(POWERED, Boolean.valueOf(false));

        if (canPlaceLeverOn(worldIn, pos, facing.getOpposite()))
        {
            return iblockstate.withProperty(FACING, BlockLever.EnumOrientation.forFacings(facing, placer.getHorizontalFacing()));
        }
        else
        {
            for (Facing enumfacing : Facing.Plane.HORIZONTAL)
            {
                if (enumfacing != facing && canPlaceLeverOn(worldIn, pos, enumfacing.getOpposite()))
                {
                    return iblockstate.withProperty(FACING, BlockLever.EnumOrientation.forFacings(enumfacing, placer.getHorizontalFacing()));
                }
            }

            if (worldIn.isBlockSolid(pos.down()))
            {
                return iblockstate.withProperty(FACING, BlockLever.EnumOrientation.forFacings(Facing.UP, placer.getHorizontalFacing()));
            }
            else
            {
                return iblockstate;
            }
        }
    }

    public static int getMetadataForFacing(Facing facing)
    {
        switch (facing)
        {
            case DOWN:
                return 0;

            case UP:
                return 5;

            case NORTH:
                return 4;

            case SOUTH:
                return 3;

            case WEST:
                return 2;

            case EAST:
                return 1;

            default:
                return -1;
        }
    }

    /**
     * Called when a neighboring block changes.
     */
    public void onNeighborBlockChange(World worldIn, BlockPos pos, State state, Block neighborBlock)
    {
        if (this.checkDrop(worldIn, pos, state) && !canPlaceLeverOn(worldIn, pos, ((BlockLever.EnumOrientation)state.getValue(FACING)).getFacing().getOpposite()))
        {
            this.dropBlockAsItem(worldIn, pos, state, 0);
            worldIn.setBlockToAir(pos);
        }
    }

    private boolean checkDrop(World worldIn, BlockPos pos, State state)
    {
        if (this.canPlaceBlockAt(worldIn, pos))
        {
            return true;
        }
        else
        {
            this.dropBlockAsItem(worldIn, pos, state, 0);
            worldIn.setBlockToAir(pos);
            return false;
        }
    }

    public void setBlockBoundsBasedOnState(IWorldAccess worldIn, BlockPos pos)
    {
        float f = 0.1875F;

        switch ((BlockLever.EnumOrientation)worldIn.getState(pos).getValue(FACING))
        {
            case EAST:
                this.setBlockBounds(0.0F, 0.2F, 0.5F - f, f * 2.0F, 0.8F, 0.5F + f);
                break;

            case WEST:
                this.setBlockBounds(1.0F - f * 2.0F, 0.2F, 0.5F - f, 1.0F, 0.8F, 0.5F + f);
                break;

            case SOUTH:
                this.setBlockBounds(0.5F - f, 0.2F, 0.0F, 0.5F + f, 0.8F, f * 2.0F);
                break;

            case NORTH:
                this.setBlockBounds(0.5F - f, 0.2F, 1.0F - f * 2.0F, 0.5F + f, 0.8F, 1.0F);
                break;

            case UP_Z:
            case UP_X:
                f = 0.25F;
                this.setBlockBounds(0.5F - f, 0.0F, 0.5F - f, 0.5F + f, 0.6F, 0.5F + f);
                break;

            case DOWN_X:
            case DOWN_Z:
                f = 0.25F;
                this.setBlockBounds(0.5F - f, 0.4F, 0.5F - f, 0.5F + f, 1.0F, 0.5F + f);
        }
    }

    public boolean onBlockActivated(World worldIn, BlockPos pos, State state, EntityNPC playerIn, Facing side, float hitX, float hitY, float hitZ)
    {
        if (worldIn.client)
        {
            return true;
        }
        else
        {
            state = state.cycleProperty(POWERED);
            worldIn.setState(pos, state, 3);
            worldIn.playSound(SoundEvent.CLICK, (double)pos.getX() + 0.5D, (double)pos.getY() + 0.5D, (double)pos.getZ() + 0.5D, 0.3F);
            worldIn.notifyNeighborsOfStateChange(pos, this);
            Facing enumfacing = ((BlockLever.EnumOrientation)state.getValue(FACING)).getFacing();
            worldIn.notifyNeighborsOfStateChange(pos.offset(enumfacing.getOpposite()), this);
            return true;
        }
    }

    public void onBlockRemoved(AWorldServer worldIn, BlockPos pos, State state)
    {
        if (((Boolean)state.getValue(POWERED)).booleanValue())
        {
            worldIn.notifyNeighborsOfStateChange(pos, this);
            Facing enumfacing = ((BlockLever.EnumOrientation)state.getValue(FACING)).getFacing();
            worldIn.notifyNeighborsOfStateChange(pos.offset(enumfacing.getOpposite()), this);
        }

        super.onBlockRemoved(worldIn, pos, state);
    }

    public int getWeakPower(IWorldAccess worldIn, BlockPos pos, State state, Facing side)
    {
        return ((Boolean)state.getValue(POWERED)).booleanValue() ? 15 : 0;
    }

    public int getStrongPower(IWorldAccess worldIn, BlockPos pos, State state, Facing side)
    {
        return !((Boolean)state.getValue(POWERED)).booleanValue() ? 0 : (((BlockLever.EnumOrientation)state.getValue(FACING)).getFacing() == side ? 15 : 0);
    }

    /**
     * Can this block provide power. Only wire currently seems to have this change based on its state.
     */
    public boolean canProvidePower()
    {
        return true;
    }

    /**
     * Convert the given metadata into a BlockState for this Block
     */
    public State getStateFromMeta(int meta)
    {
        return this.getState().withProperty(FACING, BlockLever.EnumOrientation.byMetadata(meta & 7)).withProperty(POWERED, Boolean.valueOf((meta & 8) > 0));
    }

    /**
     * Convert the BlockState into the correct metadata value
     */
    public int getMetaFromState(State state)
    {
        int i = 0;
        i = i | ((BlockLever.EnumOrientation)state.getValue(FACING)).getMetadata();

        if (((Boolean)state.getValue(POWERED)).booleanValue())
        {
            i |= 8;
        }

        return i;
    }

    protected Property[] getProperties()
    {
        return new Property[] {FACING, POWERED};
    }
    
    private static ModelRotation getRotation(EnumOrientation face) {
    	switch(face) {
			case DOWN_X:
				return ModelRotation.X180_Y90;
			case DOWN_Z:
				return ModelRotation.X180_Y180;
			case UP_X:
				return ModelRotation.X0_Y270;
			case UP_Z:
			default:
				return ModelRotation.X0_Y0;
			case NORTH:
				return ModelRotation.X90_Y0;
			case SOUTH:
				return ModelRotation.X90_Y180;
			case WEST:
				return ModelRotation.X90_Y270;
			case EAST:
				return ModelRotation.X90_Y90;
    	}
    }

    public Model getModel(ModelProvider provider, String name, State state) {
    	return provider.getModel("cobblestone")
			.add(5, 0, 4, 11, 3, 12)
				.d().uv(5, 4, 11, 12).noCull()
				.u().uv(5, 4, 11, 12).noCull()
				.n().uv(5, 0, 11, 3).noCull()
				.s().uv(5, 0, 11, 3).noCull()
				.w().uv(4, 0, 12, 3).noCull()
				.e().uv(4, 0, 12, 3).noCull()
			.add(7, 1, 7, 9, 11, 9).rotate(8, 1, 8, Facing.Axis.X, state.getValue(POWERED) ? -45 : 45, false)
				.d("lever").uv(7, 6, 9, 8).noCull()
				.u("lever").uv(7, 6, 9, 8).noCull()
				.n("lever").uv(7, 6, 9, 16).noCull()
				.s("lever").uv(7, 6, 9, 16).noCull()
				.w("lever").uv(7, 6, 9, 16).noCull()
				.e("lever").uv(7, 6, 9, 16).noCull()
    			.rotate(getRotation(state.getValue(FACING)));
    }
	
	protected Item getItemToRegister() {
		return new ItemBlock(this, "lever", false);
	}

    public static enum EnumOrientation implements Identifyable
    {
        DOWN_X(0, "down_x", Facing.DOWN),
        EAST(1, "east", Facing.EAST),
        WEST(2, "west", Facing.WEST),
        SOUTH(3, "south", Facing.SOUTH),
        NORTH(4, "north", Facing.NORTH),
        UP_Z(5, "up_z", Facing.UP),
        UP_X(6, "up_x", Facing.UP),
        DOWN_Z(7, "down_z", Facing.DOWN);

        private static final BlockLever.EnumOrientation[] META_LOOKUP = new BlockLever.EnumOrientation[values().length];
        private final int meta;
        private final String name;
        private final Facing facing;

        private EnumOrientation(int meta, String name, Facing facing)
        {
            this.meta = meta;
            this.name = name;
            this.facing = facing;
        }

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

        public Facing getFacing()
        {
            return this.facing;
        }

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

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

            return META_LOOKUP[meta];
        }

        public static BlockLever.EnumOrientation forFacings(Facing clickedSide, Facing entityFacing)
        {
            switch (clickedSide)
            {
                case DOWN:
                    switch (entityFacing.getAxis())
                    {
                        case X:
                            return DOWN_X;

                        case Z:
                            return DOWN_Z;

                        default:
                            throw new IllegalArgumentException("Invalid entityFacing " + entityFacing + " for facing " + clickedSide);
                    }

                case UP:
                    switch (entityFacing.getAxis())
                    {
                        case X:
                            return UP_X;

                        case Z:
                            return UP_Z;

                        default:
                            throw new IllegalArgumentException("Invalid entityFacing " + entityFacing + " for facing " + clickedSide);
                    }

                case NORTH:
                    return NORTH;

                case SOUTH:
                    return SOUTH;

                case WEST:
                    return WEST;

                case EAST:
                    return EAST;

                default:
                    throw new IllegalArgumentException("Invalid facing: " + clickedSide);
            }
        }

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

        static {
            for (BlockLever.EnumOrientation blocklever$enumorientation : values())
            {
                META_LOOKUP[blocklever$enumorientation.getMetadata()] = blocklever$enumorientation;
            }
        }
    }
}
