package common.entity.npc;

import common.ai.EntityAIBase;
import common.ai.EntityMoveHelper;
import common.attributes.Attributes;
import common.biome.BaseBiome;
import common.entity.DamageSource;
import common.entity.Entity;
import common.entity.types.EntityLiving;
import common.init.Config;
import common.init.SoundEvent;
import common.model.ParticleType;
import common.nbt.NBTTagCompound;
import common.pathfinding.PathNavigateGround;
import common.rng.Random;
import common.util.BlockPos;
import common.util.ExtMath;
import common.world.Chunk;
import common.world.World;
import common.world.AWorldServer;

public class EntitySlime extends EntityNPC
{
    public float squishAmount;
    public float squishFactor;
    public float prevSquishFactor;
    private boolean wasOnGround;
    private double jumpHeight;

    public EntitySlime(World worldIn)
    {
        super(worldIn);
//        this.setSize(1.0f, 1.0f);
        this.moveHelper = new EntitySlime.SlimeMoveHelper(this);
        this.tasks.addTask(1, new EntitySlime.AISlimeFloat(this));
        this.tasks.addTask(2, new EntitySlime.AISlimeAttack(this));
        this.tasks.addTask(3, new EntitySlime.AISlimeFaceRandom(this));
        this.tasks.addTask(5, new EntitySlime.AISlimeHop(this));
//        this.targets.addTask(1, new EntityAIFindEntityNearestPlayer(this));
////        this.targets.addTask(3, new EntityAIFindEntityNearest(this, EntityIronGolem.class));
//        this.targets.addTask(3, new EntityAIFindEntityNearest(this, EntityNPC.class));
        this.noPickup = true;
    }

//	public boolean isAggressive() {
//		return true;
//	}
//
//	public boolean isPeaceful() {
//		return false;
//	}

//	public boolean isAggressive(Class<? extends EntityNPC> clazz) {
//		return false;
//	}
//
//	public boolean isPeaceful(Class<? extends EntityNPC> clazz) {
//		return false;
//	}

//	public boolean canInfight(Alignment align) {
//		return false;
//	}
//
//	public boolean canMurder(Alignment align) {
//		return false;
//	}

//	public boolean canUseMagic() {
//		return false;
//	}

	public int getBaseHealth(Random rand) {
		return rand.range(6, 7);
	}

//	public int getBaseEnergy(Random rand) {
//		return 0;
//	}

    public float getHeightDeviationMin() {
    	return -0.7f;
    }

    public float getHeightDeviationMax() {
    	return 3.0f;
    }
    
    public float getSpeciesBaseSize() {
    	return 1.0f;
    }

	public Alignment getNaturalAlign() {
		return rand.pick(Alignment.LAWFUL_EVIL, Alignment.EVIL);
	}

    public void updateEntityActionState()
    {
    	super.updateEntityActionState();
    	if(this.isPlayer() && this.onGround) {
            this.moveStrafe = 0.0f;
            this.moveForward = 0.0f;
    	}
    }
    
//    public boolean canAmbush(EntityLiving entity) {
//    	return true;
//    }

//    protected void entityInit()
//    {
//        super.entityInit();
//        this.dataWatcher.addObject(16, Byte.valueOf((byte)1));
//    }

//    protected void setSlimeSize(int size)
//    {
//        this.dataWatcher.updateObject(16, Byte.valueOf((byte)size));
//        this.setSize(0.51000005F * (float)size, 0.51000005F * (float)size);
//        this.setPosition(this.posX, this.posY, this.posZ);
//        this.setMaxHealth(size * size);
//        this.getEntityAttribute(Attributes.MOVEMENT_SPEED).setBaseValue((double)(0.2F + 0.1F * (float)size));
//        this.setHealth(this.getMaxHealth());
//        this.xpValue = size;
//    }

//    /**
//     * Returns the size of the slime.
//     */
//    public int getSlimeSize()
//    {
//        return this.dataWatcher.getWatchableObjectByte(16);
//    }

    /**
     * (abstract) Protected helper method to write subclass entity data to NBT.
     */
    public void writeEntityToNBT(NBTTagCompound tagCompound)
    {
        super.writeEntityToNBT(tagCompound);
//        tagCompound.setInteger("Size", this.getSlimeSize() - 1);
        tagCompound.setBoolean("wasOnGround", this.wasOnGround);
    }

    /**
     * (abstract) Protected helper method to read subclass entity data from NBT.
     */
    public void readEntityFromNBT(NBTTagCompound tagCompund)
    {
        super.readEntityFromNBT(tagCompund);
//        int i = tagCompund.getInteger("Size");
//
//        if (i < 0)
//        {
//            i = 0;
//        }
//
//        this.setSlimeSize(i + 1);
        this.wasOnGround = tagCompund.getBoolean("wasOnGround");
    }

    protected ParticleType getParticleType()
    {
        return ParticleType.SLIME;
    }

    /**
     * Called to update the entity's position/logic.
     */
    public void onUpdate()
    {
//        if (!this.worldObj.client && this.worldObj.getDifficulty() == Difficulty.PEACEFUL && this.getSlimeSize() > 0)
//        {
//            this.dead = true;
//        }

        this.squishFactor += (this.squishAmount - this.squishFactor) * 0.5F;
        this.prevSquishFactor = this.squishFactor;
        super.onUpdate();

        if (this.onGround && !this.wasOnGround)
        {
            int i = (int)(this.width * 16.0f);

            for (int j = 0; j < i; ++j)
            {
                float f = this.rand.floatv() * (float)Math.PI * 2.0F;
                float f1 = this.rand.floatv() * 0.5F + 0.5F;
                float f2 = ExtMath.sin(f) * this.width * f1;
                float f3 = ExtMath.cos(f) * this.width * f1;
                World world = this.worldObj;
                ParticleType enumparticletypes = this.getParticleType();
                double d0 = this.posX + (double)f2;
                double d1 = this.posZ + (double)f3;
                world.spawnParticle(enumparticletypes, d0, this.getEntityBoundingBox().minY, d1, 0.0D, 0.0D, 0.0D);
            }

//            if (!this.isChild())
//            {
            this.playSound(this.jumpHeight < 0.5 ? SoundEvent.SLIME_SMALL : SoundEvent.SLIME_BIG, this.getSoundVolume());
//            }
            this.jumpHeight = 0.0;

            this.squishAmount = -0.5F;
        }
        else if (!this.onGround && this.wasOnGround)
        {
            this.squishAmount = 1.0F;
        }

        this.wasOnGround = this.onGround;
        this.squishAmount *= 0.6F;
    }

//    protected void alterSquishAmount()
//    {
//        this.squishAmount *= 0.6F;
//    }

    public void fall(float distance, float damageMultiplier)
    {
    }

//    /**
//     * Gets the amount of time the slime needs to wait between jumps.
//     */
//    protected int getJumpDelay()
//    {
//        return this.rand.excl(10, 30);
//    }

    protected EntitySlime createInstance()
    {
        return new EntitySlime(this.worldObj);
    }

//    public void onDataWatcherUpdate(int dataID)
//    {
//        if (dataID == 16)
//        {
//            int i = this.getSlimeSize();
//            this.setSize(0.51000005F * (float)i, 0.51000005F * (float)i);
//            this.rotYaw = this.headYaw;
//            this.yawOffset = this.headYaw;
//
//            if (this.isInLiquid() && this.rand.chance(20))
//            {
//                this.onEnterLiquid();
//            }
//        }
//
//        super.onDataWatcherUpdate(dataID);
//    }

    /**
     * Will get destroyed next tick.
     */
    public void setDead()
    {
        int i = 1;

        if (!this.worldObj.client && this.getHeight() >= 0.5f && this.getHealth() <= 0 && Config.mobs && Config.spawnSplitSlime)
        {
            int j = this.rand.range(2, 4);
            float size = this.getHeight() / 2.0f;

            for (int k = 0; k < j; ++k)
            {
                float f = ((float)(k % 2) - 0.5F) * (float)i / 4.0F;
                float f1 = ((float)(k / 2) - 0.5F) * (float)i / 4.0F;
                EntitySlime entityslime = this.createInstance();

                if (this.hasCustomName())
                {
                    entityslime.setCustomNameTag(this.getCustomNameTag());
                }

//                if (this.isNotDespawning())
//                {
//                    entityslime.disableDespawn();
//                }

                entityslime.setLocationAndAngles(this.posX + (double)f, this.posY + 0.5D, this.posZ + (double)f1, this.rand.floatv() * 360.0F, 0.0F);
                entityslime.onInitialSpawn(null);
                entityslime.setHeight(this.rand.frange(size * 0.8f, size * 1.2f));
                entityslime.setMaxHealth(Math.max(1, (int)((float)this.getMaxHealth() / this.getHeight() * entityslime.getHeight())));
                entityslime.setHealth(entityslime.getMaxHealth());
                entityslime.setGrowingAge(this.getGrowingAge());
                this.worldObj.spawnEntityInWorld(entityslime);
            }
        }

        super.setDead();
    }

    /**
     * Applies a velocity to each of the entities pushing them away from each other. Args: entity
     */
    public void applyEntityCollision(Entity entityIn)
    {
        super.applyEntityCollision(entityIn);

        if (entityIn instanceof EntityNPC && !(entityIn.isPlayer())) // && this.canDamagePlayer())
        {
            this.damageEntity((EntityLiving)entityIn);
        }
    }

    /**
     * Called by a player entity when they collide with an entity
     */
    public void onCollideWithPlayer(EntityNPC entityIn)
    {
//        if (this.canDamagePlayer())
//        {
        this.damageEntity(entityIn);
//        }
    }

    protected void damageEntity(EntityLiving p_175451_1_)
    {
    	double i = this.width * 2.0f;

        if ((this.worldObj.client || Config.damageMobs) && this.canEntityBeSeen(p_175451_1_) 
        		&& this.getDistanceSqToEntity(p_175451_1_) < 0.6D * i * 0.6D * i 
        		&& this.canAttack(p_175451_1_) && p_175451_1_.attackEntityFrom(DamageSource.causeMobDamage(this), this.rand.range(1, 3)))
        {
            this.playSound(SoundEvent.SLIME_ATTACK, 1.0F);
            this.applyEnchantments(this, p_175451_1_);
        }
    }

    public float getEyeHeight()
    {
        return 0.625F * this.height;
    }

//    /**
//     * Indicates weather the slime is able to damage the player (based upon the slime's size)
//     */
//    public boolean canDamagePlayer()
//    {
//        return this.getSlimeSize() > 1;
//    }

//    /**
//     * Gets the amount of damage dealt to the player when "attacked" by the slime.
//     */
//    protected int getAttackStrength()
//    {
//        return this.getSlimeSize();
//    }

//    /**
//     * Returns the sound this mob makes when it is hurt.
//     */
//    protected String getHurtSound()
//    {
//        return "mob.slime." + (this.getSlimeSize() > 1 ? "big" : "small");
//    }
//
//    /**
//     * Returns the sound this mob makes on death.
//     */
//    protected String getDeathSound()
//    {
//        return "mob.slime." + (this.getSlimeSize() > 1 ? "big" : "small");
//    }
//
//    protected Item getDropItem()
//    {
//        return this.getSlimeSize() == 1 ? Items.slime_ball : null;
//    }


    public static Random getRandomWithSeed(AWorldServer world, int xPosition, int zPosition, long seed)
    {
        return new Random(world.getSeed() + 
        		(long)(xPosition * xPosition * 4987142) + (long)(xPosition * 5947611) + 
        		(long)(zPosition * zPosition) * 4392871L + (long)(zPosition * 389711) ^ seed);
    }

    /**
     * Checks if the entity's current position is a valid location to spawn this entity.
     */
    public boolean getCanSpawnHere()
    {
        BlockPos blockpos = new BlockPos(ExtMath.floord(this.posX), 0, ExtMath.floord(this.posZ));
        Chunk chunk = this.worldObj.getChunk(blockpos);

//        if (this.worldObj.getWorldInfo().getTerrainType() == WorldType.FLAT && this.rand.nextInt(4) != 1)
//        {
//            return false;
//        }
//        else
//        {
//            if (this.worldObj.getDifficulty() != Difficulty.PEACEFUL)
//            {
        	BaseBiome biomegenbase = this.worldObj.getBiomeGenForCoords(blockpos);

            if (biomegenbase == BaseBiome.SWAMPLAND && this.posY > 50.0D && this.posY < 70.0D && this.rand.floatv() < 0.5F && this.rand.floatv() < this.worldObj.getCurrentMoonPhaseFactor() && this.worldObj.getLightFromNeighbors(new BlockPos(this)) <= this.rand.zrange(8))
            {
                return super.getCanSpawnHere();
            }

            if (this.rand.zrange(10) == 0 && getRandomWithSeed((AWorldServer)this.worldObj, chunk.xPos, chunk.zPos, 987234911L).zrange(10) == 0 && this.posY < 40.0D)
            {
                return super.getCanSpawnHere();
            }
//            }

            return false;
//        }
    }

    /**
     * Returns the volume for the sounds this mob makes.
     */
    protected float getSoundVolume()
    {
        return 0.8f;
    }

    /**
     * The speed it takes to move the entityliving's rotationPitch through the faceEntity method. This is only currently
     * use in wolves.
     */
    public int getVerticalFaceSpeed()
    {
        return 0;
    }

//    /**
//     * Returns true if the slime makes a sound when it jumps (based upon the slime's size)
//     */
//    protected boolean makesSoundOnJump()
//    {
//        return this.getSlimeSize() > 0;
//    }

//    /**
//     * Returns true if the slime makes a sound when it lands after a jump (based upon the slime's size)
//     */
//    protected boolean makesSoundOnLand()
//    {
//        return this.getSlimeSize() > 2;
//    }

    /**
     * Causes this entity to do an upwards motion (jumping).
     */
    public void jump()
    {
    	if(this.jumpHeight == 0.0)
    		this.motionY = this.jumpHeight = this.getJumpMovement();
    	else
    		this.motionY = this.jumpHeight;
        this.isAirBorne = true;
    }
    
    protected double getJumpMovement() {
    	return ExtMath.clampd((this.isPlayer() ? (this.isSneaking() ? 0.3 : (this.isSprinting() ? 0.8 : 0.55)) : this.rand.drange(0.3, 0.8)) * this.height, 0.2, 1.0);
    }

    /**
     * Called only once on an entity when first time spawned, via egg, mob spawner, natural spawning etc, but not called
     * when entity is reloaded from nbt. Mainly used for initializing attributes and inventory
     */
    public Object onInitialSpawn(Object livingdata)
    {
//        if(this.rand.chance(5))
//        	this.setGrowingAge(-24000);
        livingdata = super.onInitialSpawn(livingdata);
        this.setMaxHealth(Math.max(1, (int)((float)this.getMaxHealth() * this.getHeight())));
        this.setHealth(this.getMaxHealth());
        return livingdata;
    }
    
//    public int getTrackingRange() {
//    	return 80;
//    }
//    
//    public int getUpdateFrequency() {
//    	return 3;
//    }
//    
//    public boolean isSendingVeloUpdates() {
//    	return true;
//    }
	
    public boolean isSneakingVisually() {
        return false;
    }
    
//    public float getRenderScale()
//    {
//        return 2.0f * this.height;
//    }
    
	public int getColor() {
		return 0x6dbf40;
	}

	public boolean canPlay() {
		return false;
	}

//	public boolean allowLeashing() {
//		return false;
//	}

    static class AISlimeAttack extends EntityAIBase
    {
        private EntitySlime slime;
        private int attackTimer;

        public AISlimeAttack(EntitySlime slimeIn)
        {
            this.slime = slimeIn;
            this.setMutexBits(2);
        }

        public boolean shouldExecute()
        {
            EntityLiving entitylivingbase = this.slime.getAttackTarget();
            return entitylivingbase == null ? false : (!entitylivingbase.isEntityAlive() ? false : Config.mobAttacks);
        }

        public void startExecuting()
        {
            this.attackTimer = 300;
            super.startExecuting();
        }

        public boolean continueExecuting()
        {
            EntityLiving entitylivingbase = this.slime.getAttackTarget();
            return entitylivingbase == null ? false : (!entitylivingbase.isEntityAlive() ? false : (!Config.mobAttacks ? false : --this.attackTimer > 0));
        }

        public void updateTask()
        {
            this.slime.faceEntity(this.slime.getAttackTarget(), 10.0F, 10.0F);
            ((EntitySlime.SlimeMoveHelper)this.slime.getMoveHelper()).setYawAccel(this.slime.rotYaw, true); // this.slime.canDamagePlayer());
        }
    }

    static class AISlimeFaceRandom extends EntityAIBase
    {
        private EntitySlime slime;
        private float destYaw;
        private int faceTimer;

        public AISlimeFaceRandom(EntitySlime slimeIn)
        {
            this.slime = slimeIn;
            this.setMutexBits(2);
        }

        public boolean shouldExecute()
        {
            return this.slime.getAttackTarget() == null && (this.slime.onGround || this.slime.isInLiquid() || this.slime.isInMolten());
        }

        public void updateTask()
        {
            if (--this.faceTimer <= 0)
            {
                this.faceTimer = this.slime.getRNG().excl(40, 100);
                this.destYaw = (float)this.slime.getRNG().zrange(360);
            }

            ((EntitySlime.SlimeMoveHelper)this.slime.getMoveHelper()).setYawAccel(this.destYaw, false);
        }
    }

    static class AISlimeFloat extends EntityAIBase
    {
        private EntitySlime slime;

        public AISlimeFloat(EntitySlime slimeIn)
        {
            this.slime = slimeIn;
            this.setMutexBits(5);
            ((PathNavigateGround)slimeIn.getNavigator()).setCanSwim(true);
        }

        public boolean shouldExecute()
        {
            return this.slime.isInLiquid() || this.slime.isInMolten();
        }

        public void updateTask()
        {
            if (this.slime.getRNG().floatv() < 0.8F)
            {
                this.slime.getJumpHelper().setJumping();
            }

            ((EntitySlime.SlimeMoveHelper)this.slime.getMoveHelper()).setSpeed(1.2D);
        }
    }

    static class AISlimeHop extends EntityAIBase
    {
        private EntitySlime slime;

        public AISlimeHop(EntitySlime slimeIn)
        {
            this.slime = slimeIn;
            this.setMutexBits(5);
        }

        public boolean shouldExecute()
        {
            return true;
        }

        public void updateTask()
        {
            ((EntitySlime.SlimeMoveHelper)this.slime.getMoveHelper()).setSpeed(1.0D);
        }
    }

    static class SlimeMoveHelper extends EntityMoveHelper
    {
        private float currentYaw;
        private int jumpDelay;
        private EntitySlime slime;
        private boolean accelerate;

        public SlimeMoveHelper(EntitySlime slimeIn)
        {
            super(slimeIn);
            this.slime = slimeIn;
        }

        public void setYawAccel(float yaw, boolean accel)
        {
            this.currentYaw = yaw;
            this.accelerate = accel;
        }

        public void setSpeed(double speedIn)
        {
            this.speed = speedIn;
            this.update = true;
        }

        public void onUpdateMoveHelper()
        {
            this.entity.rotYaw = this.limitAngle(this.entity.rotYaw, this.currentYaw, 30.0F);
            this.entity.headYaw = this.entity.rotYaw;
            this.entity.yawOffset = this.entity.rotYaw;

            if (!this.update)
            {
                this.entity.setMoveForward(0.0F);
            }
            else
            {
                this.update = false;

                if (this.entity.onGround)
                {
                    this.entity.setAIMoveSpeed((float)(this.speed * this.entity.getEntityAttribute(Attributes.MOVEMENT_SPEED).getAttributeValue()));

                    if (this.jumpDelay-- <= 0)
                    {
                        this.jumpDelay = this.slime.rand.excl(10, 30);

                        if (this.accelerate)
                        {
                            this.jumpDelay /= 3;
                        }

                        this.slime.getJumpHelper().setJumping();
                        this.slime.jumpHeight = this.slime.getJumpMovement();

//                        if (!this.slime.isChild())
//                        {
                        this.slime.playSound(this.slime.jumpHeight < 0.5 ? SoundEvent.SLIME_SMALL : SoundEvent.SLIME_BIG, this.slime.getSoundVolume());
//                        }
                    }
                    else
                    {
                        this.slime.moveStrafe = this.slime.moveForward = 0.0F;
                        this.entity.setAIMoveSpeed(0.0F);
                    }
                }
                else
                {
                    this.entity.setAIMoveSpeed((float)(this.speed * this.entity.getEntityAttribute(Attributes.MOVEMENT_SPEED).getAttributeValue()));
                }
            }
        }
    }
}
