package common.entity.item;

import java.util.List;

import common.block.Block;
import common.block.BlockFalling;
import common.block.tech.BlockAnvil;
import common.collect.Lists;
import common.entity.DamageSource;
import common.entity.Entity;
import common.entity.EntityType;
import common.entity.types.IObjectData;
import common.init.BlockRegistry;
import common.init.Blocks;
import common.item.ItemStack;
import common.tags.TagObject;
import common.util.BlockPos;
import common.util.ExtMath;
import common.util.Facing;
import common.vars.Vars;
import common.world.State;
import common.world.World;

public class EntityFalling extends Entity implements IObjectData
{
    private State fallTile;
    public int fallTime;
    public boolean shouldDropItem = true;
    private boolean canSetAsBlock;
    private boolean hurtEntities;
    private int fallHurtMax = 40;
    private float fallHurtAmount = 2.0F;

    public EntityFalling(World worldIn)
    {
        super(worldIn);
    }

    public EntityFalling(World worldIn, double x, double y, double z, State fallingBlockState)
    {
        super(worldIn);
        this.fallTile = fallingBlockState;
        this.preventSpawning = true;
        this.setSize(0.98F, 0.98F);
        this.setPosition(x, y, z);
        this.motionX = 0.0D;
        this.motionY = 0.0D;
        this.motionZ = 0.0D;
        this.prevX = x;
        this.prevY = y;
        this.prevZ = z;
    }

    public EntityFalling(World worldIn, double x, double y, double z, int data)
    {
    	this(worldIn, x, y, z, BlockRegistry.getStateById(data & 65535));
    }

    /**
     * returns if this entity triggers Block.onEntityWalking on the blocks they walk on. used for spiders and wolves to
     * prevent them from trampling crops
     */
    protected boolean canTriggerWalking()
    {
        return false;
    }

    protected void entityInit()
    {
    }

    /**
     * Returns true if other Entities should be prevented from moving through this Entity.
     */
    public boolean canBeCollidedWith()
    {
        return !this.dead;
    }

    /**
     * Called to update the entity's position/logic.
     */
    public void onUpdate()
    {
        Block block = this.fallTile.getBlock();

        if (block == Blocks.air)
        {
            this.setDead();
        }
        else
        {
            this.prevX = this.posX;
            this.prevY = this.posY;
            this.prevZ = this.posZ;

            if (this.fallTime++ == 0)
            {
                BlockPos blockpos = new BlockPos(this);

                if (this.worldObj.getState(blockpos).getBlock() == block)
                {
                    this.worldObj.setBlockToAir(blockpos);
                }
                else if (!this.worldObj.client)
                {
                    this.setDead();
                    return;
                }
            }

            this.motionY -= 0.03999999910593033D;
            this.moveEntity(this.motionX, this.motionY, this.motionZ);
            this.motionX *= 0.9800000190734863D;
            this.motionY *= 0.9800000190734863D;
            this.motionZ *= 0.9800000190734863D;

            if (!this.worldObj.client)
            {
                BlockPos blockpos1 = new BlockPos(this);

                if (this.onGround)
                {
                    this.motionX *= 0.699999988079071D;
                    this.motionZ *= 0.699999988079071D;
                    this.motionY *= -0.5D;

                    if (this.worldObj.getState(blockpos1).getBlock() != Blocks.piston_extension)
                    {
                        this.setDead();

                        if (!this.canSetAsBlock)
                        {
                            if (this.worldObj.canBlockBePlaced(block, blockpos1, true, Facing.UP, (Entity)null, (ItemStack)null) && !BlockFalling.canFallInto(this.worldObj, blockpos1.down()) && this.worldObj.setState(blockpos1, this.fallTile, 3))
                            {
                                if (block instanceof BlockFalling)
                                {
                                    ((BlockFalling)block).onEndFalling(this.worldObj, blockpos1);
                                }
                            }
                            else if (this.shouldDropItem && Vars.objectDrop)
                            {
                                this.entityDropItem(new ItemStack(block.getItem()), 0.0F);
                            }
                        }
                    }
                }
                else if (this.fallTime > 100 && !this.worldObj.client && (blockpos1.getY() < -World.MAX_SIZE_Y + 1 || blockpos1.getY() > World.MAX_SIZE_Y) || this.fallTime > 600)
                {
                    if (this.shouldDropItem && Vars.objectDrop)
                    {
                        this.entityDropItem(new ItemStack(block.getItem()), 0.0F);
                    }

                    this.setDead();
                }
            }
        }
    }

    public void fall(float distance, float damageMultiplier)
    {
        Block block = this.fallTile.getBlock();

        if (this.hurtEntities)
        {
            int i = ExtMath.ceilf(distance - 1.0F);

            if (i > 0)
            {
                List<Entity> list = Lists.newArrayList(this.worldObj.getEntitiesWithinAABBExcludingEntity(this, this.getEntityBoundingBox()));
                boolean flag = block instanceof BlockAnvil;
                DamageSource damagesource = flag ? DamageSource.anvil : DamageSource.fallingBlock;

                if(this.worldObj.client || (flag ? Vars.damageAcme : Vars.damageSquish)) {
	                for (Entity entity : list)
	                {
	                    entity.attackEntityFrom(damagesource, Math.min(ExtMath.floorf((float)i * this.fallHurtAmount), this.fallHurtMax));
	                }
                }

                if (flag && (this.worldObj.client || Vars.anvilFallDecay) && (double)this.rand.floatv() < 0.05000000074505806D + (double)i * 0.05D)
                {
                	BlockAnvil anvil = (BlockAnvil)block;
                    int j = anvil.getAnvilDamage();
                    ++j;

                    if (j >= BlockAnvil.ANVILS.length)
                    {
                        this.canSetAsBlock = true;
                    }
                    else
                    {
                        this.fallTile = BlockAnvil.getByDamage(j).getState().withProperty(BlockAnvil.FACING, this.fallTile.getValue(BlockAnvil.FACING));
                    }
                }
            }
        }
    }

    /**
     * (abstract) Protected helper method to write subclass entity data to NBT.
     */
    protected void writeEntity(TagObject tagCompound)
    {
        tagCompound.setString("Block", (this.fallTile != null ? this.fallTile : Blocks.air.getState()).getId());
        tagCompound.setByte("Time", (byte)this.fallTime);
        tagCompound.setBool("DropItem", this.shouldDropItem);
        tagCompound.setBool("HurtEntities", this.hurtEntities);
        tagCompound.setFloat("FallHurtAmount", this.fallHurtAmount);
        tagCompound.setInt("FallHurtMax", this.fallHurtMax);
    }

    /**
     * (abstract) Protected helper method to read subclass entity data from NBT.
     */
    protected void readEntity(TagObject tagCompund)
    {
        if (tagCompund.hasString("Block"))
        {
            this.fallTile = State.getState(tagCompund.getString("Block"), Blocks.sand.getState());
        }
        else
        {
            this.fallTile = Blocks.sand.getState();
        }

        this.fallTime = tagCompund.getByte("Time") & 255;
        Block block = this.fallTile.getBlock();

        this.hurtEntities = tagCompund.getBool("HurtEntities");
        this.fallHurtAmount = tagCompund.getFloat("FallHurtAmount");
        this.fallHurtMax = tagCompund.getInt("FallHurtMax");

        this.shouldDropItem = tagCompund.getBool("DropItem");

        if (block == null || block == Blocks.air)
        {
            this.fallTile = Blocks.sand.getState();
        }
    }

    public World getWorldObj()
    {
        return this.worldObj;
    }

    public void setHurtEntities(boolean p_145806_1_)
    {
        this.hurtEntities = p_145806_1_;
    }

    /**
     * Return whether this entity should be rendered as on fire.
     */
    public boolean canRenderOnFire()
    {
        return false;
    }

    public State getBlock()
    {
        return this.fallTile;
    }
    
    public int getTrackingRange() {
    	return 160;
    }
    
    public int getUpdateFrequency() {
    	return 20;
    }
    
    public boolean isSendingVeloUpdates() {
    	return true;
    }
	
	public int getPacketData() {
		return BlockRegistry.getStateId(this.fallTile);
	}
	
//	public boolean hasSpawnVelocity() {
//		return false;
//	}
    
    public EntityType getType() {
    	return EntityType.OBJECT;
    }
}
