package common.entity.types;

import java.util.Set;

import common.block.Block;
import common.collect.Sets;
import common.entity.DamageSource;
import common.entity.EntityType;
import common.entity.npc.Alignment;
import common.entity.npc.EntityNPC;
import common.init.Blocks;
import common.init.Items;
import common.item.ItemStack;
import common.model.ParticleType;
import common.nbt.NBTTagCompound;
import common.util.BlockPos;
import common.util.ExtMath;
import common.world.World;

public abstract class EntityAnimal extends EntityLiving
{
    protected final Set<Block> spawnableBlocks = Sets.newHashSet(Blocks.grass, Blocks.tian_soil);
    
    private int inLove;
    private EntityNPC playerInLove;

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

	protected void entityInit() {
		super.entityInit();
		this.dataWatcher.addObject(30, Byte.valueOf((byte)0));
	}

    protected void updateAITasks()
    {
        if (this.getGrowingAge() != 0)
        {
            this.resetInLove();
        }

        super.updateAITasks();
    }

    /**
     * Called frequently so the entity can update its state every tick as required. For example, zombies and skeletons
     * use this to react to sunlight and start to burn.
     */
    public void onLivingUpdate()
    {
        super.onLivingUpdate();

        if (this.getGrowingAge() != 0)
        {
        	this.resetInLove();
        }

        if (this.isInLove())
        {
        	if(!this.worldObj.client) {
        		this.setInLove(this.inLove - 1, false);
        	}
        	else {
        		this.inLove += 1;
	            if (this.inLove == 10)
	            {
	            	this.inLove = 0;
	                double d0 = this.rand.gaussian() * 0.02D;
	                double d1 = this.rand.gaussian() * 0.02D;
	                double d2 = this.rand.gaussian() * 0.02D;
	                this.worldObj.spawnParticle(ParticleType.HEART, this.posX + (double)(this.rand.floatv() * this.width * 2.0F) - (double)this.width, this.posY + 0.5D + (double)(this.rand.floatv() * this.height), this.posZ + (double)(this.rand.floatv() * this.width * 2.0F) - (double)this.width, d0, d1, d2);
	            }
        	}
        }
    }

    /**
     * Called when the entity is attacked.
     */
    public boolean attackEntityFrom(DamageSource source, int amount)
    {
//        if (this.isEntityInvulnerable(source))
//        {
//            return false;
//        }
//        else
//        {
    	this.resetInLove();
        return super.attackEntityFrom(source, amount);
//        }
    }

    public float getBlockPathWeight(BlockPos pos)
    {
        return this.worldObj.getState(pos.down()).getBlock() == Blocks.grass ? 10.0F : this.worldObj.getLightBrightness(pos) - 0.5F;
    }

    /**
     * (abstract) Protected helper method to write subclass entity data to NBT.
     */
    public void writeEntityToNBT(NBTTagCompound tagCompound)
    {
        super.writeEntityToNBT(tagCompound);
        tagCompound.setInteger("InLove", this.inLove);
    }

    /**
     * (abstract) Protected helper method to read subclass entity data from NBT.
     */
    public void readEntityFromNBT(NBTTagCompound tagCompund)
    {
        super.readEntityFromNBT(tagCompund);
        this.setInLove(tagCompund.getInteger("InLove"), true);
    }

    /**
     * Checks if the entity's current position is a valid location to spawn this entity.
     */
    public boolean getCanSpawnHere()
    {
        int i = ExtMath.floord(this.posX);
        int j = ExtMath.floord(this.getEntityBoundingBox().minY);
        int k = ExtMath.floord(this.posZ);
        BlockPos blockpos = new BlockPos(i, j, k);
        return this.spawnableBlocks.contains(this.worldObj.getState(blockpos.down()).getBlock()) && this.isValidLightLevel(this.worldObj.getLight(blockpos)) && super.getCanSpawnHere();
    }
    
    protected boolean isValidLightLevel(int level)
    {
    	return level > 8;
    }

    /**
     * Get number of ticks, at least during which the living entity will be silent.
     */
    public int getTalkInterval()
    {
        return 120;
    }

//    /**
//     * Determines if an entity can be despawned, used on idle far away entities
//     */
//    protected boolean canDespawn()
//    {
//        return false;
//    }

    /**
     * Get the experience points the entity currently has.
     */
    protected int getExperiencePoints(EntityNPC player)
    {
        return this.worldObj.rand.roll(3);
    }

    /**
     * Checks if the parameter is an item which this animal can be fed to breed it (wheat, carrots or seeds depending on
     * the animal type)
     */
    public boolean isBreedingItem(ItemStack stack)
    {
        return stack == null ? false : stack.getItem() == Items.wheats;
    }

    /**
     * Called when a player interacts with a mob. e.g. gets milk from a cow, gets into the saddle on a pig.
     */
    public boolean interact(EntityNPC player)
    {
        ItemStack itemstack = player.inventory.getCurrentItem();

        if (itemstack != null)
        {
            if (this.isBreedingItem(itemstack) && this.getGrowingAge() == 0 && !this.isInLove())
            {
                this.consumeItemFromStack(player, itemstack);
                this.setInLove(player);
                return true;
            }

            if (this.isChild() && this.isBreedingItem(itemstack))
            {
                this.consumeItemFromStack(player, itemstack);
                if(!this.worldObj.client)
                	this.grow((int)((float)(-this.getGrowingAge() / 20) * 0.1F));
                return true;
            }
        }

        return super.interact(player);
    }

    /**
     * Decreases ItemStack size by one
     */
    protected void consumeItemFromStack(EntityNPC player, ItemStack stack)
    {
//        if (!player.creative)
//        {
        --stack.stackSize;

        if (stack.stackSize <= 0)
        {
            player.inventory.setInventorySlotContents(player.inventory.currentItem, (ItemStack)null);
        }
//        }
    }

    public void setInLove(EntityNPC player)
    {
    	if(!this.worldObj.client) {
    		this.setInLove(600, true);
	        this.playerInLove = player;
	        this.worldObj.setEntityState(this, (byte)18);
    	}
    }

    public EntityNPC getPlayerInLove()
    {
        return this.playerInLove;
    }

    /**
     * Returns if the entity is currently in 'love mode'.
     */
    public boolean isInLove()
    {
    	return this.worldObj.client ? (this.dataWatcher.getWatchableObjectByte(30) != 0) : (this.inLove > 0);
//        return this.inLove > 0;
    }

    public void resetInLove()
    {
    	if(!this.worldObj.client) {
	        this.inLove = 0;
	        this.dataWatcher.updateObject(30, (byte)0);
    	}
    }

    public void setInLove(int loveTicks, boolean update)
    {
		boolean last = this.isInLove();
        this.inLove = loveTicks;
        if(update || (last != this.isInLove()))
        	this.dataWatcher.updateObject(30, (byte)(this.isInLove() ? 1 : 0));
    }

    /**
     * Returns true if the mob is currently able to mate with the specified mob.
     */
    public boolean canMateWith(EntityAnimal otherAnimal)
    {
        return otherAnimal == this ? false : (otherAnimal.getClass() != this.getClass() ? false : this.isInLove() && otherAnimal.isInLove());
    }

    public void handleStatusUpdate(byte id)
    {
        if (id == 18)
        {
            for (int i = 0; i < 7; ++i)
            {
                double d0 = this.rand.gaussian() * 0.02D;
                double d1 = this.rand.gaussian() * 0.02D;
                double d2 = this.rand.gaussian() * 0.02D;
                this.worldObj.spawnParticle(ParticleType.HEART, this.posX + (double)(this.rand.floatv() * this.width * 2.0F) - (double)this.width, this.posY + 0.5D + (double)(this.rand.floatv() * this.height), this.posZ + (double)(this.rand.floatv() * this.width * 2.0F) - (double)this.width, d0, d1, d2);
            }
        }
        else
        {
            super.handleStatusUpdate(id);
        }
    }
    
    /**
     * If Animal, checks if the age timer is negative
     */
    public boolean isChild()
    {
        return this.getGrowingAge() < 0;
    }

    /**
     * "Sets the scale for an ageable entity according to the boolean parameter, which says if it's a child."
     */
    public void setScaleForAge()
    {
        this.setScale(this.isChild() ? 0.5F : 1.0F);
    }
    
    protected boolean canDropLoot()
    {
        return !this.isChild();
    }
    
//    protected float getSoundPitch()
//    {
//        return this.isChild() ? (this.rand.floatv() - this.rand.floatv()) * 0.2F + 1.5F : super.getSoundPitch();
//    }
    
    public int getTrackingRange() {
    	return 80;
    }
    
    public int getUpdateFrequency() {
    	return 3;
    }
    
    public boolean isSendingVeloUpdates() {
    	return true;
    }
	
	public Alignment getAlignment() {
		return Alignment.LAWFUL;
	}
    
    public EntityType getType() {
    	return EntityType.ANIMAL;
    }
}
