package common.entity.animal;

import java.util.function.Predicate;

import common.ai.EntityAIAttackOnCollide;
import common.ai.EntityAIAvoidEntity;
import common.ai.EntityAIHurtByTarget;
import common.ai.EntityAIMate;
import common.ai.EntityAIMoveToBlock;
import common.ai.EntityAINearestAttackableTarget;
import common.ai.EntityAIPanic;
import common.ai.EntityAISwimming;
import common.ai.EntityAITempt;
import common.ai.EntityAIWander;
import common.ai.EntityAIWatchClosest;
import common.ai.EntityJumpHelper;
import common.ai.EntityMoveHelper;
import common.attributes.Attributes;
import common.block.Block;
import common.block.BlockTallGrass;
import common.entity.DamageSource;
import common.entity.Entity;
import common.entity.npc.Alignment;
import common.entity.npc.EntityNPC;
import common.entity.types.EntityAnimal;
import common.entity.types.EntityLiving;
import common.init.BlockRegistry;
import common.init.Blocks;
import common.init.Config;
import common.init.ItemRegistry;
import common.init.Items;
import common.init.SoundEvent;
import common.item.Item;
import common.item.ItemStack;
import common.model.ParticleType;
import common.nbt.NBTTagCompound;
import common.pathfinding.PathEntity;
import common.pathfinding.PathNavigateGround;
import common.potion.Potion;
import common.potion.PotionEffect;
import common.util.BlockPos;
import common.util.ExtMath;
import common.util.Vec3;
import common.world.State;
import common.world.World;

public class EntityRabbit extends EntityAnimal {
	private static final int TYPES = 10;
	
	private EntityRabbit.AIAvoidEntity<EntityWolf> aiAvoidWolves;
	private int jumpRotTimer = 0;
	private int jumpRotFactor = 0;
	private boolean jumped = false;
	private boolean attacking = false;
	private int moveDuration = 0;
	private EntityRabbit.EnumMoveType moveType = EntityRabbit.EnumMoveType.HOP;
	private int foodCooldown = 0;

	public EntityRabbit(World world) {
		super(world);
		this.setSize(0.6F, 0.7F);
		this.jumpHelper = new EntityRabbit.RabbitJumpHelper(this);
		this.moveHelper = new EntityRabbit.RabbitMoveHelper(this);
		((PathNavigateGround)this.getNavigator()).setAvoidsWater(true);
		this.navigator.setHeightRequirement(2.5F);
		this.tasks.addTask(1, new EntityAISwimming(this));
		this.tasks.addTask(1, new EntityRabbit.AIPanic(this, 1.33D));
		this.tasks.addTask(2, new EntityAITempt(this, 1.0D, Items.carrot, false));
		this.tasks.addTask(2, new EntityAITempt(this, 1.0D, Items.golden_carrot, false));
		this.tasks.addTask(2, new EntityAITempt(this, 1.0D, ItemRegistry.getItemFromBlock(Blocks.flower), false));
		this.tasks.addTask(3, new EntityAIMate(this, 0.8D) {
		    protected int getMatingCooldown() {
		    	return EntityRabbit.this.rand.excl(50, 200);
		    }
		});
		this.tasks.addTask(4, new EntityRabbit.AIRaidFarm(this));
		this.tasks.addTask(5, new EntityAIWander(this, 0.6D));
		this.tasks.addTask(11, new EntityAIWatchClosest(this, null, 10.0F));
		this.aiAvoidWolves = new EntityRabbit.AIAvoidEntity(this, EntityWolf.class, 16.0F, 1.33D, 1.33D);
		this.tasks.addTask(4, this.aiAvoidWolves);
		this.setMovementSpeed(0.0D);
	}

	protected float getJumpUpwardsMotion() {
		return this.moveHelper.isUpdating() && this.moveHelper.getY() > this.posY + 0.5D ? 0.5F : this.moveType.getUpwardsMotion();
	}

	public void setMoveType(EntityRabbit.EnumMoveType type) {
		this.moveType = type;
	}

	public float getJumpRotation(float delta) {
		return this.jumpRotFactor == 0 ? 0.0F : ((float)this.jumpRotTimer + delta) / (float)this.jumpRotFactor;
	}

	public void setMovementSpeed(double newSpeed) {
		this.getNavigator().setSpeed(newSpeed);
		this.moveHelper.setMoveTo(this.moveHelper.getX(), this.moveHelper.getY(), this.moveHelper.getZ(), newSpeed);
	}

	public void setJumping(boolean jump, EntityRabbit.EnumMoveType moveTypeIn) {
		super.setJumping(jump);
		if(!jump) {
			if(this.moveType == EntityRabbit.EnumMoveType.ATTACK)
				this.moveType = EntityRabbit.EnumMoveType.HOP;
		}
		else {
			this.setMovementSpeed(1.5D * (double)moveTypeIn.getSpeed());
			this.playSound(SoundEvent.RABBIT_JUMP, this.getSoundVolume());
		}
		this.jumped = jump;
	}

	public void doMovementAction(EntityRabbit.EnumMoveType movetype) {
		this.setJumping(true, movetype);
		this.jumpRotFactor = movetype.getRotationFactor();
		this.jumpRotTimer = 0;
	}

	public boolean hasJumped() {
		return this.jumped;
	}

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

	public void updateAITasks() {
		if(this.moveHelper.getSpeed() > 0.8D)
			this.setMoveType(EntityRabbit.EnumMoveType.SPRINT);
		else if(this.moveType != EntityRabbit.EnumMoveType.ATTACK)
			this.setMoveType(EntityRabbit.EnumMoveType.HOP);
		if(this.moveDuration > 0)
			--this.moveDuration;
		if(this.foodCooldown > 0) {
			this.foodCooldown -= this.rand.zrange(3);
			if(this.foodCooldown < 0)
				this.foodCooldown = 0;
		}
		if(this.onGround) {
			if(!this.attacking) {
				this.setJumping(false, EntityRabbit.EnumMoveType.NONE);
				this.moveDuration = this.getMoveTypeDuration();
				((EntityRabbit.RabbitJumpHelper)this.jumpHelper).setWasJumping(false);
			}
			if(this.getRabbitType() == 99 && this.moveDuration == 0) {
				EntityLiving target = this.getAttackTarget();
				if(target != null && this.getDistanceSqToEntity(target) < 16.0D) {
					this.calculateRotationYaw(target.posX, target.posZ);
					this.moveHelper.setMoveTo(target.posX, target.posY, target.posZ, this.moveHelper.getSpeed());
					this.doMovementAction(EntityRabbit.EnumMoveType.ATTACK);
					this.attacking = true;
				}
			}
			EntityRabbit.RabbitJumpHelper jumpHelper = (EntityRabbit.RabbitJumpHelper)this.jumpHelper;
			if(!jumpHelper.getIsJumping()) {
				if(this.moveHelper.isUpdating() && this.moveDuration == 0) {
					PathEntity pathentity = this.navigator.getPath();
					Vec3 vec3 = new Vec3(this.moveHelper.getX(), this.moveHelper.getY(), this.moveHelper.getZ());
					if(pathentity != null && pathentity.getCurrentPathIndex() < pathentity.getCurrentPathLength()) {
						vec3 = pathentity.getPosition(this);
					}
					this.calculateRotationYaw(vec3.xCoord, vec3.zCoord);
					this.doMovementAction(this.moveType);
				}
			}
			else if(!jumpHelper.getWasJumping()) {
				((EntityRabbit.RabbitJumpHelper)this.jumpHelper).setWasJumping(true);
			}
		}
		this.attacking = this.onGround;
	}

	public void spawnRunningParticles() {
	}

	private void calculateRotationYaw(double x, double z) {
		this.rotYaw = (float)(ExtMath.atan2(z - this.posZ, x - this.posX) * 180.0D / Math.PI) - 90.0F;
	}

	public void onLivingUpdate() {
		super.onLivingUpdate();
		if(this.jumpRotTimer != this.jumpRotFactor) {
			if(this.jumpRotTimer == 0 && !this.worldObj.client)
				this.worldObj.setEntityState(this, (byte)1);
			++this.jumpRotTimer;
		}
		else if(this.jumpRotFactor != 0) {
			this.jumpRotTimer = 0;
			this.jumpRotFactor = 0;
		}
	}

	protected void applyEntityAttributes() {
		super.applyEntityAttributes();
		this.setMaxHealth(5);
		this.getEntityAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(0.30000001192092896D);
	}

	public void writeEntityToNBT(NBTTagCompound tagCompound) {
		super.writeEntityToNBT(tagCompound);
		tagCompound.setInteger("RabbitType", this.getRabbitType());
		tagCompound.setInteger("MoreCarrotTicks", this.foodCooldown);
	}

	public void readEntityFromNBT(NBTTagCompound tagCompund) {
		super.readEntityFromNBT(tagCompund);
		this.setRabbitType(tagCompund.getInteger("RabbitType"));
		this.foodCooldown = tagCompund.getInteger("MoreCarrotTicks");
	}

//	protected String getJumpingSound() {
//		return "mob.rabbit.hop";
//	}

	protected SoundEvent getLivingSound() {
		return SoundEvent.RABBIT_IDLE;
	}

	protected SoundEvent getHurtSound() {
		return SoundEvent.RABBIT_HIT;
	}

	protected SoundEvent getDeathSound() {
		return SoundEvent.RABBIT_DEATH;
	}

	public boolean attackEntityAsMob(Entity entityIn) {
    	if(!this.worldObj.client && !Config.damageMobs)
    		return false;
		if(this.getRabbitType() == 99) {
//			this.playSound("mob.attack", 1.0F, (this.rand.floatv() - this.rand.floatv()) * 0.2F + 1.0F);
			return entityIn.attackEntityFrom(DamageSource.causeMobDamage(this), 8);
		}
		else {
			return entityIn.attackEntityFrom(DamageSource.causeMobDamage(this), 3);
		}
	}

	public int getTotalArmorValue() {
		return this.getRabbitType() == 99 ? 8 : super.getTotalArmorValue();
	}

//	public boolean attackEntityFrom(DamageSource source, int amount) {
//		return this.isEntityInvulnerable(source) ? false : super.attackEntityFrom(source, amount);
//	}

//	protected void addRandomDrop() {
//		this.entityDropItem(new ItemStack(Items.rabbit_foot, 1), 0.0F);
//	}

	protected void dropFewItems(boolean wasRecentlyHit, int lootingModifier) {
		int amt = this.rand.zrange(2) + this.rand.zrange(1 + lootingModifier);
		for(int j = 0; j < amt; ++j) {
			this.dropItem(Items.carrot, 1);
		}
//		amt = this.rand.zrange(2);
//		for(int k = 0; k < amt; ++k) {
//			if(this.isBurning())
//				this.dropItem(Items.cooked_rabbit, 1);
//			else
//				this.dropItem(Items.rabbit, 1);
//		}
	}

	private boolean isRabbitBreedingItem(Item itemIn) {
		return itemIn == Items.carrot || itemIn == Items.golden_carrot || itemIn == ItemRegistry.getItemFromBlock(Blocks.flower);
	}

	public EntityRabbit createChild(EntityLiving ageable) {
		EntityRabbit rabbit = new EntityRabbit(this.worldObj);
		if(ageable instanceof EntityRabbit)
			rabbit.setRabbitType(this.rand.chance() ? this.getRabbitType() : ((EntityRabbit)ageable).getRabbitType());
		return rabbit;
	}

	public boolean isBreedingItem(ItemStack stack) {
		return stack != null && this.isRabbitBreedingItem(stack.getItem());
	}

	public int getRabbitType() {
		return this.dataWatcher.getWatchableObjectByte(18);
	}

	public void setRabbitType(int type) {
		if(type == 99) {
			this.tasks.removeTask(this.aiAvoidWolves);
			this.tasks.addTask(4, new EntityRabbit.AIEvilAttack(this));
			this.targets.addTask(1, new EntityAIHurtByTarget(this, false));
			this.targets.addTask(2, new EntityAINearestAttackableTarget(this, EntityNPC.class, true));
			this.targets.addTask(2, new EntityAINearestAttackableTarget(this, EntityWolf.class, true));
//			if(!this.hasCustomName())
//				this.setCustomNameTag(I18n.formatKey("entity.KillerBunny.name"));
		}
		this.dataWatcher.updateObject(18, Byte.valueOf((byte)type));
	}
	
	public String getTypeName() {
        return !this.hasCustomName() && this.getRabbitType() == 99 ? "Killer-Kaninchen" : super.getTypeName();
    }

	public Object onInitialSpawn(Object livingdata) {
		livingdata = super.onInitialSpawn(livingdata);
		int type = Config.killerBunnyChance > 0 && this.rand.chance(Config.killerBunnyChance) ? 99 : this.rand.zrange(TYPES);
		boolean typeSet = false;
		if(livingdata instanceof EntityRabbit.RabbitTypeData) {
			type = ((EntityRabbit.RabbitTypeData)livingdata).typeData;
			typeSet = true;
		}
		else {
			livingdata = new EntityRabbit.RabbitTypeData(type);
		}
		this.setRabbitType(type);
		if(type == 99) {
			this.setMaxHealth(15);
	        this.setHealth(this.getMaxHealth());
		}
		if(typeSet && this.rand.chance())
			this.setGrowingAge(-24000);
		return livingdata;
	}

	private boolean canEatMore() {
		return this.foodCooldown == 0;
	}

	protected int getMoveTypeDuration() {
		return this.moveType.getDuration();
	}

	protected void consumeBlock(BlockPos pos, State state) {
		this.worldObj.setEntityState(this, (byte)19);
		this.foodCooldown = this.rand.excl(10, this.isChild() ? 20 : 50);
		if(this.isChild())
			this.grow(this.rand.range(250, 350));
		if(Config.rabbitMateChance > 0 && this.rand.chance(Config.rabbitMateChance) && this.getGrowingAge() == 0 && !this.isInLove() && 
				!this.worldObj.getEntitiesWithinAABB(EntityRabbit.class, this.getEntityBoundingBox().expand(15.0f, 15.0f, 15.0f), new Predicate<EntityRabbit>() {
					public boolean test(EntityRabbit entity) {
						return entity != EntityRabbit.this && entity.getGrowingAge() == 0;
					}
				}).isEmpty())
			this.setInLove(null);
		if(state.getBlock() == Blocks.blue_mushroom)
			this.addEffect(new PotionEffect(Potion.SPEED, this.rand.range(400, 2600), this.rand.chance(0, 1, 5)));
		this.worldObj.destroyBlock(pos, false);
		this.worldObj.setState(pos, Blocks.air.getState(), 2);
	}

	public void handleStatusUpdate(byte id) {
		if(id == 1) {
			this.createRunningParticles();
			this.jumpRotFactor = 10;
			this.jumpRotTimer = 0;
		}
		else if(id == 19) {
			this.worldObj.spawnParticle(ParticleType.BLOCK_DUST,
					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, 0.0D, 0.0D, 0.0D,
					BlockRegistry.getStateId(this.worldObj.getState(this.getPosition())));
		}
		else {
			super.handleStatusUpdate(id);
		}
	}

	public int getColor() {
		return 0xd4d499;
	}
	
	public Alignment getAlignment() {
		return this.getRabbitType() == 99 ? Alignment.CHAOTIC_EVIL : super.getAlignment();
	}

	static class AIAvoidEntity<T extends Entity> extends EntityAIAvoidEntity<T> {
		private EntityRabbit entity;

		public AIAvoidEntity(EntityRabbit rabbit, Class<T> toAvoid, float distance, double farSpeed, double nearSpeed) {
			super(rabbit, toAvoid, distance, farSpeed, nearSpeed);
			this.entity = rabbit;
		}

//		public void updateTask() {
//			super.updateTask();
//		}
	}

	static class AIEvilAttack extends EntityAIAttackOnCollide {
		public AIEvilAttack(EntityRabbit rabbit) {
			super(rabbit, EntityLiving.class, 1.4D, true);
		}

		protected double getAttackRange(EntityLiving attackTarget) {
			return (double)(4.0F + attackTarget.width);
		}
	}

	static class AIPanic extends EntityAIPanic {
		private EntityRabbit entity;

		public AIPanic(EntityRabbit rabbit, double speedIn) {
			super(rabbit, speedIn);
			this.entity = rabbit;
		}

		public void updateTask() {
			super.updateTask();
			this.entity.setMovementSpeed(this.speed);
		}
	}

	static class AIRaidFarm extends EntityAIMoveToBlock {
		private final EntityRabbit entity;
		private boolean isHungry;
		private boolean foodFound = false;

		public AIRaidFarm(EntityRabbit rabbitIn) {
			super(rabbitIn, 0.699999988079071D, 16);
			this.entity = rabbitIn;
            this.runDelay = this.entity.getRNG().excl(10, 40);
		}

		public boolean shouldExecute() {
			if(this.runDelay <= 0) {
				if(!Config.mobGrief)
					return false;
				this.foodFound = false;
				this.isHungry = this.entity.canEatMore();
			}
			if(this.runDelay > 0) {
	            --this.runDelay;
	            return false;
	        }
	        else {
	            this.runDelay = this.entity.getRNG().excl(10, 40);
	            return this.searchForDestination();
	        }
		}

		public boolean continueExecuting() {
			return this.foodFound && super.continueExecuting();
		}

		public void startExecuting() {
			super.startExecuting();
		}

		public void resetTask() {
			super.resetTask();
		}

		public void updateTask() {
			super.updateTask();
			this.entity.getLookHelper().setLookPosition((double)this.destinationBlock.getX() + 0.5D, (double)(this.destinationBlock.getY() + 1),
					(double)this.destinationBlock.getZ() + 0.5D, 10.0F, (float)this.entity.getVerticalFaceSpeed());
			if(this.getIsAboveDestination()) {
				World world = this.entity.worldObj;
				BlockPos pos = Config.rabidRabbits ? this.destinationBlock : this.destinationBlock.up();
				State state = world.getState(pos);
//				Block block = iblockstate.getBlock();
				if(this.foodFound && canEat(state) && Config.mobGrief) // block instanceof BlockCarrot && ((Integer)iblockstate.getValue(BlockCarrot.AGE)).intValue() == 7) {
					this.entity.consumeBlock(pos, state);
				this.foodFound = false;
				this.runDelay = 10;
			}
		}

		protected boolean shouldMoveTo(World worldIn, BlockPos pos) {
//			Block block = worldIn.getBlockState(pos).getBlock();
//			if(block == Blocks.farmland) {
				pos = pos.up();
				State state = worldIn.getState(pos);
//				block = state.getBlock();
				if(/* block instanceof BlockCarrot && ((Integer)state.getValue(BlockCarrot.AGE)).intValue() == 7 
				 	*/ canEat(state) && this.isHungry && !this.foodFound) {
					this.foodFound = true;
					return true;
				}
//			}
			return false;
		}
		
		private static boolean canEat(State state) {
			Block block = state.getBlock();
			if(Config.rabidRabbits)
				return block != Blocks.bedrock;
			return block == Blocks.carrot || block == Blocks.potato || block == Blocks.wheat || block == Blocks.brown_mushroom || 
					block == Blocks.flower || block == Blocks.blue_mushroom || 
					(block == Blocks.tallgrass && state.getValue(BlockTallGrass.TYPE) == BlockTallGrass.EnumType.GRASS);
		}
	}

	static enum EnumMoveType {
		NONE(0.0F, 0.0F, 30, 1), 
		HOP(0.8F, 0.2F, 20, 10), 
		STEP(1.0F, 0.45F, 14, 14), 
		SPRINT(1.75F, 0.4F, 1, 8), 
		ATTACK(2.0F, 0.7F, 7, 8);

		private final float speed;
		private final float upMotion;
		private final int duration;
		private final int rotFactor;

		private EnumMoveType(float typeSpeed, float upwardsMotion, int typeDuration, int rotationFactor) {
			this.speed = typeSpeed;
			this.upMotion = upwardsMotion;
			this.duration = typeDuration;
			this.rotFactor = rotationFactor;
		}

		public float getSpeed() {
			return this.speed;
		}

		public float getUpwardsMotion() {
			return this.upMotion;
		}

		public int getDuration() {
			return this.duration;
		}

		public int getRotationFactor() {
			return this.rotFactor;
		}
	}

	public class RabbitJumpHelper extends EntityJumpHelper {
		private EntityRabbit entity;
		private boolean wasJumping = false;

		public RabbitJumpHelper(EntityRabbit rabbit) {
			super(rabbit);
			this.entity = rabbit;
		}

		public boolean getIsJumping() {
			return this.isJumping;
		}

		public boolean getWasJumping() {
			return this.wasJumping;
		}

		public void setWasJumping(boolean jumped) {
			this.wasJumping = jumped;
		}

		public void doJump() {
			if(this.isJumping) {
				this.entity.doMovementAction(EntityRabbit.EnumMoveType.STEP);
				this.isJumping = false;
			}
		}
	}

	static class RabbitMoveHelper extends EntityMoveHelper {
		private EntityRabbit entity;

		public RabbitMoveHelper(EntityRabbit rabbit) {
			super(rabbit);
			this.entity = rabbit;
		}

		public void onUpdateMoveHelper() {
			if(this.entity.onGround && !this.entity.hasJumped())
				this.entity.setMovementSpeed(0.0D);
			super.onUpdateMoveHelper();
		}
	}

	public static class RabbitTypeData {
		public int typeData;

		public RabbitTypeData(int type) {
			this.typeData = type;
		}
	}
}
