package common.block.tech;

import java.util.List;
import java.util.function.Predicate;

import common.entity.Entity;
import common.entity.item.EntityCart;
import common.inventory.Container;
import common.inventory.IInventory;
import common.model.Model;
import common.model.ModelProvider;
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.world.IWorldAccess;
import common.world.State;
import common.world.World;
import common.world.AWorldServer;

public class BlockRailDetector extends BlockRailBase
{
    public static final PropertyEnum<BlockRailBase.EnumRailDirection> SHAPE = PropertyEnum.<BlockRailBase.EnumRailDirection>create("shape", EnumRailDirection.class, EnumRailDirection.NORTH_SOUTH, EnumRailDirection.EAST_WEST, EnumRailDirection.ASCENDING_EAST, EnumRailDirection.ASCENDING_WEST, EnumRailDirection.ASCENDING_NORTH, EnumRailDirection.ASCENDING_SOUTH);
    public static final PropertyBool POWERED = PropertyBool.create("powered");

    public BlockRailDetector()
    {
        super(true);
        this.setDefaultState(this.getBaseState().withProperty(POWERED, Boolean.valueOf(false)).withProperty(SHAPE, BlockRailBase.EnumRailDirection.NORTH_SOUTH));
    }

    /**
     * How many world ticks before ticking
     */
    public int tickRate(World worldIn, BlockPos pos)
    {
        return 20;
    }

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

    /**
     * Called When an Entity Collided with the Block
     */
    public void onEntityCollidedWithBlock(World worldIn, BlockPos pos, State state, Entity entityIn)
    {
        if (!worldIn.client)
        {
            if (!((Boolean)state.getValue(POWERED)).booleanValue())
            {
                this.updatePoweredState(worldIn, pos, state);
            }
        }
    }

    /**
     * Called randomly when setTickRandomly is set to true (used by e.g. crops to grow, etc.)
     */
    public void randomTick(AWorldServer worldIn, BlockPos pos, State state, Random random)
    {
    }

    public void updateTick(AWorldServer worldIn, BlockPos pos, State state, Random rand)
    {
        if (/* !worldIn.client && */ ((Boolean)state.getValue(POWERED)).booleanValue())
        {
            this.updatePoweredState(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 : (side == Facing.UP ? 15 : 0);
    }

    private void updatePoweredState(World worldIn, BlockPos pos, State state)
    {
        boolean flag = ((Boolean)state.getValue(POWERED)).booleanValue();
        boolean flag1 = false;
        List<EntityCart> list = this.<EntityCart>findMinecarts(worldIn, pos, EntityCart.class, null);

        if (!list.isEmpty())
        {
            flag1 = true;
        }

        if (flag1 && !flag)
        {
            worldIn.setState(pos, state.withProperty(POWERED, Boolean.valueOf(true)), 3);
            worldIn.notifyNeighborsOfStateChange(pos, this);
            worldIn.notifyNeighborsOfStateChange(pos.down(), this);
            worldIn.markBlockRangeForRenderUpdate(pos, pos);
        }

        if (!flag1 && flag)
        {
            worldIn.setState(pos, state.withProperty(POWERED, Boolean.valueOf(false)), 3);
            worldIn.notifyNeighborsOfStateChange(pos, this);
            worldIn.notifyNeighborsOfStateChange(pos.down(), this);
            worldIn.markBlockRangeForRenderUpdate(pos, pos);
        }

        if (flag1)
        {
            worldIn.scheduleUpdate(pos, this, this.tickRate(worldIn, pos));
        }

        worldIn.updateComparatorOutputLevel(pos, this);
    }

    public void onBlockAdded(AWorldServer worldIn, BlockPos pos, State state)
    {
        super.onBlockAdded(worldIn, pos, state);
        this.updatePoweredState(worldIn, pos, state);
    }

    public Property<BlockRailBase.EnumRailDirection> getShapeProperty()
    {
        return SHAPE;
    }

    public boolean hasComparatorInputOverride()
    {
        return true;
    }

    public int getComparatorInputOverride(World worldIn, BlockPos pos)
    {
        if (((Boolean)worldIn.getState(pos).getValue(POWERED)).booleanValue())
        {
//            List<EntityMinecartCommandBlock> list = this.<EntityMinecartCommandBlock>findMinecarts(worldIn, pos, EntityMinecartCommandBlock.class, null);
//
//            if (!list.isEmpty())
//            {
//                return ((EntityMinecartCommandBlock)list.get(0)).isPowered() ? 15 : 0;
//            }

            List<EntityCart> list1 = this.<EntityCart>findMinecarts(worldIn, pos, EntityCart.class, new Predicate<EntityCart>() {
        		public boolean test(EntityCart entity) {
        			return entity instanceof IInventory && entity.isEntityAlive();
        		}
        	});

            if (!list1.isEmpty())
            {
                return Container.calcRedstoneFromInventory((IInventory)list1.get(0));
            }
        }

        return 0;
    }

    protected <T extends EntityCart> List<T> findMinecarts(World worldIn, BlockPos pos, Class<T> clazz, Predicate<EntityCart> filter)
    {
        BoundingBox axisalignedbb = this.getDectectionBox(pos);
        return filter == null ? worldIn.getEntitiesWithinAABB(clazz, axisalignedbb) : worldIn.getEntitiesWithinAABB(clazz, axisalignedbb, filter);
    }

    private BoundingBox getDectectionBox(BlockPos pos)
    {
        float f = 0.2F;
        return new BoundingBox((double)((float)pos.getX() + 0.2F), (double)pos.getY(), (double)((float)pos.getZ() + 0.2F), (double)((float)(pos.getX() + 1) - 0.2F), (double)((float)(pos.getY() + 1) - 0.2F), (double)((float)(pos.getZ() + 1) - 0.2F));
    }

    /**
     * Convert the given metadata into a BlockState for this Block
     */
    public State getStateFromMeta(int meta)
    {
        return this.getState().withProperty(SHAPE, BlockRailBase.EnumRailDirection.byMetadata((meta & 7) > 5 ? 0 : (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 | ((BlockRailBase.EnumRailDirection)state.getValue(SHAPE)).getMetadata();

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

        return i;
    }

    protected Property[] getProperties()
    {
        return new Property[] {SHAPE, POWERED};
    }
    
    public Model getModel(ModelProvider provider, String name, State state) {
    	return super.getModel(provider, name + (state.getValue(POWERED) ? "_powered" : ""), state);
    }
}
