package common.entity.item;

import java.util.List;

import common.block.Block;
import common.entity.DamageSource;
import common.entity.Entity;
import common.entity.EntityDamageSourceIndirect;
import common.entity.EntityType;
import common.entity.npc.EntityNPC;
import common.entity.types.EntityLiving;
import common.init.Blocks;
import common.init.Items;
import common.item.Item;
import common.model.ParticleType;
import common.tags.TagObject;
import common.util.BlockPos;
import common.util.BoundingBox;
import common.util.ExtMath;
import common.vars.Vars;
import common.world.World;

public class EntityBoat extends Entity
{
    /** true if no player in boat */
    private boolean isBoatEmpty;
    private double speedMultiplier;
    private int boatPosRotationIncrements;
    private double boatX;
    private double boatY;
    private double boatZ;
    private double boatYaw;
    private double boatPitch;
    private double velocityX;
    private double velocityY;
    private double velocityZ;

    public EntityBoat(World worldIn)
    {
        super(worldIn);
        this.isBoatEmpty = true;
        this.speedMultiplier = 0.07D;
        this.preventSpawning = true;
        this.setSize(1.5F, 0.6F);
    }

    /**
     * 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()
    {
        this.dataWatcher.addObject(17, 0);
        this.dataWatcher.addObject(18, 1);
        this.dataWatcher.addObject(19, 0);
    }

    /**
     * Returns a boundingBox used to collide the entity with other entities and blocks. This enables the entity to be
     * pushable on contact, like boats or minecarts.
     */
    public BoundingBox getCollisionBox(Entity entityIn)
    {
        return entityIn.getEntityBoundingBox();
    }

    /**
     * Returns the collision bounding box for this entity
     */
    public BoundingBox getCollisionBoundingBox()
    {
        return this.getEntityBoundingBox();
    }

    /**
     * Returns true if this entity should push and be pushed by other entities when colliding.
     */
    public boolean canBePushed()
    {
        return true;
    }

    public EntityBoat(World worldIn, double p_i1705_2_, double p_i1705_4_, double p_i1705_6_)
    {
        this(worldIn);
        this.setPosition(p_i1705_2_, p_i1705_4_, p_i1705_6_);
        this.motionX = 0.0D;
        this.motionY = 0.0D;
        this.motionZ = 0.0D;
        this.prevX = p_i1705_2_;
        this.prevY = p_i1705_4_;
        this.prevZ = p_i1705_6_;
    }

    /**
     * Returns the Y offset from the entity's position for any entity riding this one.
     */
    public double getMountedYOffset()
    {
        return -0.3D;
    }

    /**
     * Called when the entity is attacked.
     */
    public boolean attackEntityFrom(DamageSource source, int amount)
    {
//        if (this.isEntityInvulnerable(source))
//        {
//            return false;
//        }
//        else
        if (!this.worldObj.client && !this.dead)
        {
            if (this.passenger != null && this.passenger == source.getEntity() && source instanceof EntityDamageSourceIndirect)
            {
                return false;
            }
            else
            {
                this.setForwardDirection(-this.getForwardDirection());
                this.setTimeSinceHit(10);
                this.setDamageTaken(this.getDamageTaken() + (int)amount * 10);
                this.setBeenAttacked();
//                boolean flag = source.getEntity().isPlayer() && ((EntityNPC)source.getEntity()).creative;

                if (/* flag || */ this.getDamageTaken() > 40)
                {
                    if (this.passenger != null)
                    {
                        this.passenger.mountEntity(this);
                    }

                    if (/* !flag && */ Vars.objectDrop)
                    {
                        this.dropItemWithOffset(Items.boat, 1, 0.0F);
                    }

                    this.setDead();
                }

                return true;
            }
        }
        else
        {
            return true;
        }
    }

    /**
     * Setups the entity to do the hurt animation. Only used by packets in multiplayer.
     */
    public void performHurtAnimation()
    {
        this.setForwardDirection(-this.getForwardDirection());
        this.setTimeSinceHit(10);
        this.setDamageTaken(this.getDamageTaken() * 11);
    }

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

    public void setClientPosition(double x, double y, double z, float yaw, float pitch, boolean teleport)
    {
        if (teleport && this.passenger != null)
        {
            this.prevX = this.posX = x;
            this.prevY = this.posY = y;
            this.prevZ = this.posZ = z;
            this.rotYaw = yaw;
            this.rotPitch = pitch;
            this.boatPosRotationIncrements = 0;
            this.setPosition(x, y, z);
            this.motionX = this.velocityX = 0.0D;
            this.motionY = this.velocityY = 0.0D;
            this.motionZ = this.velocityZ = 0.0D;
        }
        else
        {
            if (this.isBoatEmpty)
            {
                this.boatPosRotationIncrements = 8;
            }
            else
            {
                double d0 = x - this.posX;
                double d1 = y - this.posY;
                double d2 = z - this.posZ;
                double d3 = d0 * d0 + d1 * d1 + d2 * d2;

                if (d3 <= 1.0D)
                {
                    return;
                }

                this.boatPosRotationIncrements = 3;
            }

            this.boatX = x;
            this.boatY = y;
            this.boatZ = z;
            this.boatYaw = (double)yaw;
            this.boatPitch = (double)pitch;
            this.motionX = this.velocityX;
            this.motionY = this.velocityY;
            this.motionZ = this.velocityZ;
        }
    }

    /**
     * Sets the velocity to the args. Args: x, y, z
     */
    public void setVelocity(double x, double y, double z)
    {
        this.velocityX = this.motionX = x;
        this.velocityY = this.motionY = y;
        this.velocityZ = this.motionZ = z;
    }

    /**
     * Called to update the entity's position/logic.
     */
    public void onUpdate()
    {
        super.onUpdate();

        if (this.getTimeSinceHit() > 0)
        {
            this.setTimeSinceHit(this.getTimeSinceHit() - 1);
        }

        if (this.getDamageTaken() > 0)
        {
            this.setDamageTaken(this.getDamageTaken() - 1);
        }

        this.prevX = this.posX;
        this.prevY = this.posY;
        this.prevZ = this.posZ;
        int i = 5;
        double d0 = 0.0D;

        for (int j = 0; j < i; ++j)
        {
            double d1 = this.getEntityBoundingBox().minY + (this.getEntityBoundingBox().maxY - this.getEntityBoundingBox().minY) * (double)(j + 0) / (double)i - 0.125D;
            double d3 = this.getEntityBoundingBox().minY + (this.getEntityBoundingBox().maxY - this.getEntityBoundingBox().minY) * (double)(j + 1) / (double)i - 0.125D;
            BoundingBox axisalignedbb = new BoundingBox(this.getEntityBoundingBox().minX, d1, this.getEntityBoundingBox().minZ, this.getEntityBoundingBox().maxX, d3, this.getEntityBoundingBox().maxZ);

            if (this.worldObj.isAABBInLiquid(axisalignedbb))
            {
                d0 += 1.0D / (double)i;
            }
        }

        double d9 = Math.sqrt(this.motionX * this.motionX + this.motionZ * this.motionZ);

        if (d9 > 0.2975D)
        {
            double d2 = Math.cos((double)this.rotYaw * Math.PI / 180.0D);
            double d4 = Math.sin((double)this.rotYaw * Math.PI / 180.0D);

            for (int k = 0; (double)k < 1.0D + d9 * 60.0D; ++k)
            {
                double d5 = (double)(this.rand.floatv() * 2.0F - 1.0F);
                double d6 = (double)(this.rand.zrange(2) * 2 - 1) * 0.7D;

                if (this.rand.chance())
                {
                    double d7 = this.posX - d2 * d5 * 0.8D + d4 * d6;
                    double d8 = this.posZ - d4 * d5 * 0.8D - d2 * d6;
                    this.worldObj.spawnParticle(ParticleType.WATER_SPLASH, d7, this.posY - 0.125D, d8, this.motionX, this.motionY, this.motionZ);
                }
                else
                {
                    double d24 = this.posX + d2 + d4 * d5 * 0.7D;
                    double d25 = this.posZ + d4 - d2 * d5 * 0.7D;
                    this.worldObj.spawnParticle(ParticleType.WATER_SPLASH, d24, this.posY - 0.125D, d25, this.motionX, this.motionY, this.motionZ);
                }
            }
        }

        if (this.worldObj.client && this.isBoatEmpty)
        {
            if (this.boatPosRotationIncrements > 0)
            {
                double d12 = this.posX + (this.boatX - this.posX) / (double)this.boatPosRotationIncrements;
                double d16 = this.posY + (this.boatY - this.posY) / (double)this.boatPosRotationIncrements;
                double d19 = this.posZ + (this.boatZ - this.posZ) / (double)this.boatPosRotationIncrements;
                double d22 = ExtMath.wrapd(this.boatYaw - (double)this.rotYaw);
                this.rotYaw = (float)((double)this.rotYaw + d22 / (double)this.boatPosRotationIncrements);
                this.rotPitch = (float)((double)this.rotPitch + (this.boatPitch - (double)this.rotPitch) / (double)this.boatPosRotationIncrements);
                --this.boatPosRotationIncrements;
                this.setPosition(d12, d16, d19);
                this.setRotation(this.rotYaw, this.rotPitch);
            }
            else
            {
                double d13 = this.posX + this.motionX;
                double d17 = this.posY + this.motionY;
                double d20 = this.posZ + this.motionZ;
                this.setPosition(d13, d17, d20);

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

                this.motionX *= 0.9900000095367432D;
                this.motionY *= 0.949999988079071D;
                this.motionZ *= 0.9900000095367432D;
            }
        }
        else
        {
            if (d0 < 1.0D)
            {
                double d10 = d0 * 2.0D - 1.0D;
                this.motionY += 0.03999999910593033D * d10;
            }
            else
            {
                if (this.motionY < 0.0D)
                {
                    this.motionY /= 2.0D;
                }

                this.motionY += 0.007000000216066837D;
            }

            if (this.passenger instanceof EntityLiving)
            {
                EntityLiving entitylivingbase = (EntityLiving)this.passenger;
                float f = this.passenger.rotYaw + -entitylivingbase.moveStrafe * 90.0F;
                this.motionX += -Math.sin((double)(f * (float)Math.PI / 180.0F)) * this.speedMultiplier * (double)entitylivingbase.moveForward * 0.05000000074505806D;
                this.motionZ += Math.cos((double)(f * (float)Math.PI / 180.0F)) * this.speedMultiplier * (double)entitylivingbase.moveForward * 0.05000000074505806D;
            }

            double d11 = Math.sqrt(this.motionX * this.motionX + this.motionZ * this.motionZ);

            if (d11 > 0.35D)
            {
                double d14 = 0.35D / d11;
                this.motionX *= d14;
                this.motionZ *= d14;
                d11 = 0.35D;
            }

            if (d11 > d9 && this.speedMultiplier < 0.35D)
            {
                this.speedMultiplier += (0.35D - this.speedMultiplier) / 35.0D;

                if (this.speedMultiplier > 0.35D)
                {
                    this.speedMultiplier = 0.35D;
                }
            }
            else
            {
                this.speedMultiplier -= (this.speedMultiplier - 0.07D) / 35.0D;

                if (this.speedMultiplier < 0.07D)
                {
                    this.speedMultiplier = 0.07D;
                }
            }

            for (int i1 = 0; i1 < 4; ++i1)
            {
                int l1 = ExtMath.floord(this.posX + ((double)(i1 % 2) - 0.5D) * 0.8D);
                int i2 = ExtMath.floord(this.posZ + ((double)(i1 / 2) - 0.5D) * 0.8D);

                for (int j2 = 0; j2 < 2; ++j2)
                {
                    int l = ExtMath.floord(this.posY) + j2;
                    BlockPos blockpos = new BlockPos(l1, l, i2);
                    Block block = this.worldObj.getState(blockpos).getBlock();

                    if (block == Blocks.snow_layer)
                    {
                        this.worldObj.setBlockToAir(blockpos);
                        this.collidedHorizontally = false;
                    }
                    else if (block == Blocks.waterlily)
                    {
                        this.worldObj.destroyBlock(blockpos, true);
                        this.collidedHorizontally = false;
                    }
                }
            }

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

            this.moveEntity(this.motionX, this.motionY, this.motionZ);

            if (this.collidedHorizontally && d9 > 0.2975D)
            {
                if (!this.worldObj.client && !this.dead)
                {
                    this.setDead();

                    if (Vars.objectDrop)
                    {
                        for (int j1 = 0; j1 < 3; ++j1)
                        {
                            this.dropItemWithOffset(Items.oak_planks, 1, 0.0F);
                        }

                        for (int k1 = 0; k1 < 2; ++k1)
                        {
                            this.dropItemWithOffset(Items.stick, 1, 0.0F);
                        }
                    }
                }
            }
            else
            {
                this.motionX *= 0.9900000095367432D;
                this.motionY *= 0.949999988079071D;
                this.motionZ *= 0.9900000095367432D;
            }

            this.rotPitch = 0.0F;
            double d15 = (double)this.rotYaw;
            double d18 = this.prevX - this.posX;
            double d21 = this.prevZ - this.posZ;

            if (d18 * d18 + d21 * d21 > 0.001D)
            {
                d15 = (double)((float)(ExtMath.atan2(d21, d18) * 180.0D / Math.PI));
            }

            double d23 = ExtMath.wrapd(d15 - (double)this.rotYaw);

            if (d23 > 20.0D)
            {
                d23 = 20.0D;
            }

            if (d23 < -20.0D)
            {
                d23 = -20.0D;
            }

            this.rotYaw = (float)((double)this.rotYaw + d23);
            this.setRotation(this.rotYaw, this.rotPitch);

            if (!this.worldObj.client)
            {
                List<Entity> list = this.worldObj.getEntitiesWithinAABBExcludingEntity(this, this.getEntityBoundingBox().expand(0.20000000298023224D, 0.0D, 0.20000000298023224D));

                if (list != null && !list.isEmpty())
                {
                    for (int k2 = 0; k2 < list.size(); ++k2)
                    {
                        Entity entity = (Entity)list.get(k2);

                        if (entity != this.passenger && entity.canBePushed() && entity instanceof EntityBoat)
                        {
                            entity.applyEntityCollision(this);
                        }
                    }
                }

                if (this.passenger != null && this.passenger.dead)
                {
                    this.passenger = null;
                }
            }
        }
    }

    public void updateRiderPosition()
    {
        if (this.passenger != null)
        {
            double d0 = Math.cos((double)this.rotYaw * Math.PI / 180.0D) * 0.4D;
            double d1 = Math.sin((double)this.rotYaw * Math.PI / 180.0D) * 0.4D;
            this.passenger.setPosition(this.posX + d0, this.posY + this.getMountedYOffset() + this.passenger.getYOffset(), this.posZ + d1);
        }
    }

    /**
     * (abstract) Protected helper method to write subclass entity data to NBT.
     */
    protected void writeEntity(TagObject tagCompound)
    {
    }

    /**
     * (abstract) Protected helper method to read subclass entity data from NBT.
     */
    protected void readEntity(TagObject tagCompund)
    {
    }

    /**
     * First layer of player interaction
     */
    public boolean interactFirst(EntityNPC playerIn)
    {
        if (this.passenger != null && /* this.passenger.isPlayer() && */ this.passenger != playerIn)
        {
            return false;
        }
        else
        {
            if (!this.worldObj.client)
            {
                playerIn.mountEntity(this);
            }

            return true;
        }
    }

    protected void updateFallState(double y, boolean onGroundIn, Block blockIn, BlockPos pos)
    {
        if (onGroundIn)
        {
            if (this.fallDistance > 3.0F)
            {
            	if(this.ignoreFall) {
            		this.ignoreFall = false;
            	}
            	else {
            		this.fall(this.fallDistance, 1.0F);

	                if (!this.worldObj.client && !this.dead)
	                {
	                    this.setDead();
	
	                    if (Vars.objectDrop)
	                    {
	                        for (int i = 0; i < 3; ++i)
	                        {
	                            this.dropItemWithOffset(Items.oak_planks, 1, 0.0F);
	                        }
	
	                        for (int j = 0; j < 2; ++j)
	                        {
	                            this.dropItemWithOffset(Items.stick, 1, 0.0F);
	                        }
	                    }
	                }
            	}

                this.fallDistance = 0.0F;
            }
        }
        else if (!this.worldObj.getState((new BlockPos(this)).down()).getBlock().getMaterial().isColdLiquid() && y < 0.0D)
        {
            this.fallDistance = (float)((double)this.fallDistance - y);
        }
    }

    /**
     * Sets the damage taken from the last hit.
     */
    public void setDamageTaken(int p_70266_1_)
    {
        this.dataWatcher.updateObject(19, Integer.valueOf(p_70266_1_));
    }

    /**
     * Gets the damage taken from the last hit.
     */
    public int getDamageTaken()
    {
        return this.dataWatcher.getWatchableObjectInt(19);
    }

    /**
     * Sets the time to count down from since the last time entity was hit.
     */
    public void setTimeSinceHit(int p_70265_1_)
    {
        this.dataWatcher.updateObject(17, Integer.valueOf(p_70265_1_));
    }

    /**
     * Gets the time since the last hit.
     */
    public int getTimeSinceHit()
    {
        return this.dataWatcher.getWatchableObjectInt(17);
    }

    /**
     * Sets the forward direction of the entity.
     */
    public void setForwardDirection(int p_70269_1_)
    {
        this.dataWatcher.updateObject(18, Integer.valueOf(p_70269_1_));
    }

    /**
     * Gets the forward direction of the entity.
     */
    public int getForwardDirection()
    {
        return this.dataWatcher.getWatchableObjectInt(18);
    }

    /**
     * true if no player in boat
     */
    public void setIsBoatEmpty(boolean p_70270_1_)
    {
        this.isBoatEmpty = p_70270_1_;
    }
    
    public int getTrackingRange() {
    	return 80;
    }
    
    public int getUpdateFrequency() {
    	return 3;
    }
    
    public boolean isSendingVeloUpdates() {
    	return true;
    }
	
    public Item getItem() {
        return Items.boat;
    }
    
    public EntityType getType() {
    	return EntityType.VEHICLE;
    }
}
