package common.entity.types;

import java.util.List;

import common.block.Block;
import common.block.BlockPortal;
import common.entity.Entity;
import common.entity.EntityType;
import common.init.BlockRegistry;
import common.init.Blocks;
import common.model.ParticleType;
import common.nbt.NBTTagCompound;
import common.util.BlockPos;
import common.util.BoundingBox;
import common.util.ExtMath;
import common.util.HitPosition;
import common.util.Vec3;
import common.world.State;
import common.world.World;

public abstract class EntityThrowable extends Entity implements IProjectile
{
    private int xTile = -1;
    private int yTile = -1;
    private int zTile = -1;
    private Block inTile;
    protected boolean inGround;
    public int throwableShake;
    private EntityLiving thrower;
    private int ticksInGround;
    private int ticksInAir;

    public EntityThrowable(World worldIn)
    {
        super(worldIn);
        this.setSize(0.25F, 0.25F);
    }

    protected void entityInit()
    {
    }

    /**
     * Checks if the entity is in range to render by using the past in distance and comparing it to its average edge
     * length * 64 * renderDistanceWeight Args: distance
     */
    public boolean isInRangeToRenderDist(double distance)
    {
        double d0 = this.getEntityBoundingBox().getAverageEdgeLength() * 4.0D;

        if (Double.isNaN(d0))
        {
            d0 = 4.0D;
        }

        d0 = d0 * 64.0D;
        return distance < d0 * d0;
    }

    public EntityThrowable(World worldIn, EntityLiving throwerIn)
    {
        super(worldIn);
        this.thrower = throwerIn;
        this.setSize(0.25F, 0.25F);
        this.setLocationAndAngles(throwerIn.posX, throwerIn.posY + (double)throwerIn.getEyeHeight(), throwerIn.posZ, throwerIn.rotYaw, throwerIn.rotPitch);
        this.posX -= (double)(ExtMath.cos(this.rotYaw / 180.0F * (float)Math.PI) * 0.16F);
        this.posY -= 0.10000000149011612D;
        this.posZ -= (double)(ExtMath.sin(this.rotYaw / 180.0F * (float)Math.PI) * 0.16F);
        this.setPosition(this.posX, this.posY, this.posZ);
        float f = 0.4F;
        this.motionX = (double)(-ExtMath.sin(this.rotYaw / 180.0F * (float)Math.PI) * ExtMath.cos(this.rotPitch / 180.0F * (float)Math.PI) * f);
        this.motionZ = (double)(ExtMath.cos(this.rotYaw / 180.0F * (float)Math.PI) * ExtMath.cos(this.rotPitch / 180.0F * (float)Math.PI) * f);
        this.motionY = (double)(-ExtMath.sin((this.rotPitch + this.getInaccuracy()) / 180.0F * (float)Math.PI) * f);
        this.setThrowableHeading(this.motionX, this.motionY, this.motionZ, this.getVelocity(), 1.0F);
    }

    public EntityThrowable(World worldIn, double x, double y, double z)
    {
        super(worldIn);
        this.ticksInGround = 0;
        this.setSize(0.25F, 0.25F);
        this.setPosition(x, y, z);
    }

    protected float getVelocity()
    {
        return 1.5F;
    }

    protected float getInaccuracy()
    {
        return 0.0F;
    }

    /**
     * Similar to setArrowHeading, it's point the throwable entity to a x, y, z direction.
     */
    public void setThrowableHeading(double x, double y, double z, float velocity, float inaccuracy)
    {
        float f = ExtMath.sqrtd(x * x + y * y + z * z);
        x = x / (double)f;
        y = y / (double)f;
        z = z / (double)f;
        x = x + this.rand.gaussian() * 0.007499999832361937D * (double)inaccuracy;
        y = y + this.rand.gaussian() * 0.007499999832361937D * (double)inaccuracy;
        z = z + this.rand.gaussian() * 0.007499999832361937D * (double)inaccuracy;
        x = x * (double)velocity;
        y = y * (double)velocity;
        z = z * (double)velocity;
        this.motionX = x;
        this.motionY = y;
        this.motionZ = z;
        float f1 = ExtMath.sqrtd(x * x + z * z);
        this.prevYaw = this.rotYaw = (float)(ExtMath.atan2(x, z) * 180.0D / Math.PI);
        this.prevPitch = this.rotPitch = (float)(ExtMath.atan2(y, (double)f1) * 180.0D / Math.PI);
        this.ticksInGround = 0;
    }

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

        if (this.prevPitch == 0.0F && this.prevYaw == 0.0F)
        {
            float f = ExtMath.sqrtd(x * x + z * z);
            this.prevYaw = this.rotYaw = (float)(ExtMath.atan2(x, z) * 180.0D / Math.PI);
            this.prevPitch = this.rotPitch = (float)(ExtMath.atan2(y, (double)f) * 180.0D / Math.PI);
        }
    }

    /**
     * Called to update the entity's position/logic.
     */
    public void onUpdate()
    {
        this.lastTickPosX = this.posX;
        this.lastTickPosY = this.posY;
        this.lastTickPosZ = this.posZ;
        super.onUpdate();

        if (this.throwableShake > 0)
        {
            --this.throwableShake;
        }

        if (this.inGround)
        {
            if (this.worldObj.getState(new BlockPos(this.xTile, this.yTile, this.zTile)).getBlock() == this.inTile)
            {
                ++this.ticksInGround;

                if (this.ticksInGround == 1200)
                {
                    this.setDead();
                }

                return;
            }

            this.inGround = false;
            this.motionX *= (double)(this.rand.floatv() * 0.2F);
            this.motionY *= (double)(this.rand.floatv() * 0.2F);
            this.motionZ *= (double)(this.rand.floatv() * 0.2F);
            this.ticksInGround = 0;
            this.ticksInAir = 0;
        }
        else
        {
            ++this.ticksInAir;
        }

        Vec3 vec3 = new Vec3(this.posX, this.posY, this.posZ);
        Vec3 vec31 = new Vec3(this.posX + this.motionX, this.posY + this.motionY, this.posZ + this.motionZ);
        HitPosition movingobjectposition = this.worldObj.rayTraceBlocks(vec3, vec31);
        vec3 = new Vec3(this.posX, this.posY, this.posZ);
        vec31 = new Vec3(this.posX + this.motionX, this.posY + this.motionY, this.posZ + this.motionZ);

        if (movingobjectposition != null)
        {
            vec31 = new Vec3(movingobjectposition.vec.xCoord, movingobjectposition.vec.yCoord, movingobjectposition.vec.zCoord);
        }

        if (!this.worldObj.client)
        {
            Entity entity = null;
            List<Entity> list = this.worldObj.getEntitiesWithinAABBExcludingEntity(this, this.getEntityBoundingBox().addCoord(this.motionX, this.motionY, this.motionZ).expand(1.0D, 1.0D, 1.0D));
            double d0 = 0.0D;
            EntityLiving entitylivingbase = this.getThrower();

            for (int j = 0; j < list.size(); ++j)
            {
                Entity entity1 = (Entity)list.get(j);

                if (entity1.canBeCollidedWith() && (entity1 != entitylivingbase || this.ticksInAir >= 5))
                {
                    float f = 0.3F;
                    BoundingBox axisalignedbb = entity1.getEntityBoundingBox().expand((double)f, (double)f, (double)f);
                    HitPosition movingobjectposition1 = axisalignedbb.calculateIntercept(vec3, vec31);

                    if (movingobjectposition1 != null)
                    {
                        double d1 = vec3.squareDistanceTo(movingobjectposition1.vec);

                        if (d1 < d0 || d0 == 0.0D)
                        {
                            entity = entity1;
                            d0 = d1;
                        }
                    }
                }
            }

            if (entity != null)
            {
                movingobjectposition = new HitPosition(entity);
            }
        }

        if (movingobjectposition != null)
        {
        	State state;
            if (movingobjectposition.type == HitPosition.ObjectType.BLOCK && (state = this.worldObj.getState(movingobjectposition.block)).getBlock() == Blocks.portal)
            {
                this.setPortal(state.getValue(BlockPortal.DIM));
            }
            else if (movingobjectposition.type == HitPosition.ObjectType.BLOCK && this.worldObj.getState(movingobjectposition.block).getBlock() == Blocks.floor_portal)
            {
                this.setFlatPortal();
            }
            else
            {
                this.onImpact(movingobjectposition);
            }
        }

        this.posX += this.motionX;
        this.posY += this.motionY;
        this.posZ += this.motionZ;
        float f1 = ExtMath.sqrtd(this.motionX * this.motionX + this.motionZ * this.motionZ);
        this.rotYaw = (float)(ExtMath.atan2(this.motionX, this.motionZ) * 180.0D / Math.PI);

        for (this.rotPitch = (float)(ExtMath.atan2(this.motionY, (double)f1) * 180.0D / Math.PI); this.rotPitch - this.prevPitch < -180.0F; this.prevPitch -= 360.0F)
        {
            ;
        }

        while (this.rotPitch - this.prevPitch >= 180.0F)
        {
            this.prevPitch += 360.0F;
        }

        while (this.rotYaw - this.prevYaw < -180.0F)
        {
            this.prevYaw -= 360.0F;
        }

        while (this.rotYaw - this.prevYaw >= 180.0F)
        {
            this.prevYaw += 360.0F;
        }

        this.rotPitch = this.prevPitch + (this.rotPitch - this.prevPitch) * 0.2F;
        this.rotYaw = this.prevYaw + (this.rotYaw - this.prevYaw) * 0.2F;
        float f2 = 0.99F;
        float f3 = this.getGravityVelocity();

        if (this.isInLiquid())
        {
            for (int i = 0; i < 4; ++i)
            {
                float f4 = 0.25F;
                this.worldObj.spawnParticle(ParticleType.WATER_BUBBLE, this.posX - this.motionX * (double)f4, this.posY - this.motionY * (double)f4, this.posZ - this.motionZ * (double)f4, this.motionX, this.motionY, this.motionZ);
            }

            f2 = 0.8F;
        }

        this.motionX *= (double)f2;
        this.motionY *= (double)f2;
        this.motionZ *= (double)f2;
        this.motionY -= (double)f3;
        this.setPosition(this.posX, this.posY, this.posZ);
    }

    /**
     * Gets the amount of gravity to apply to the thrown entity with each tick.
     */
    protected float getGravityVelocity()
    {
        return 0.03F;
    }

    /**
     * Called when this EntityThrowable hits a block or entity.
     */
    protected abstract void onImpact(HitPosition p_70184_1_);

    /**
     * (abstract) Protected helper method to write subclass entity data to NBT.
     */
    public void writeEntityToNBT(NBTTagCompound tagCompound)
    {
        tagCompound.setShort("xTile", (short)this.xTile);
        tagCompound.setShort("yTile", (short)this.yTile);
        tagCompound.setShort("zTile", (short)this.zTile);
        String resourcelocation = BlockRegistry.REGISTRY.getNameForObject(this.inTile);
        tagCompound.setString("inTile", resourcelocation == null ? "" : resourcelocation.toString());
        tagCompound.setByte("shake", (byte)this.throwableShake);
        tagCompound.setByte("inGround", (byte)(this.inGround ? 1 : 0));

//        if ((this.throwerName == null || this.throwerName.length() == 0) && this.thrower.isPlayer())
//        {
//            this.throwerName = ((EntityNPC)this.thrower).getUser();
//        }
//
//        tagCompound.setString("ownerName", this.throwerName == null ? "" : this.throwerName);
    }

    /**
     * (abstract) Protected helper method to read subclass entity data from NBT.
     */
    public void readEntityFromNBT(NBTTagCompound tagCompund)
    {
        this.xTile = tagCompund.getShort("xTile");
        this.yTile = tagCompund.getShort("yTile");
        this.zTile = tagCompund.getShort("zTile");

        if (tagCompund.hasKey("inTile", 8))
        {
            this.inTile = BlockRegistry.getByIdFallback(tagCompund.getString("inTile"));
        }
        else
        {
            this.inTile = BlockRegistry.getBlockById(tagCompund.getByte("inTile") & 255);
        }

        this.throwableShake = tagCompund.getByte("shake") & 255;
        this.inGround = tagCompund.getByte("inGround") == 1;
//        this.thrower = null;
//        this.throwerName = tagCompund.getString("ownerName");
//
//        if (this.throwerName != null && this.throwerName.length() == 0)
//        {
//            this.throwerName = null;
//        }

//        this.thrower = this.getThrower();
    }

    public EntityLiving getThrower()
    {
//        if (this.thrower == null && this.throwerName != null && this.throwerName.length() > 0)
//        {
//            this.thrower = this.worldObj.getPlayer(this.throwerName);

//            if (this.thrower == null && this.worldObj instanceof IWorldServer)
//            {
//                try
//                {
//                    Entity entity = ((IWorldServer)this.worldObj).getEntityFromUuid(UUID.fromString(this.throwerName));
//
//                    if (entity instanceof EntityLivingBase)
//                    {
//                        this.thrower = (EntityLivingBase)entity;
//                    }
//                }
//                catch (Throwable var2)
//                {
//                    this.thrower = null;
//                }
//            }
//        }

        return this.thrower;
    }
    
    public int getTrackingRange() {
    	return 64;
    }
    
    public int getUpdateFrequency() {
    	return 10;
    }
    
    public boolean isSendingVeloUpdates() {
    	return true;
    }
    
    public EntityType getType() {
    	return EntityType.PROJECTILE;
    }
}
