package com.stormarmory.entity.monsters.entity;

import java.util.Calendar;
import java.util.List;
import java.util.Random;
import java.util.UUID;

import javax.annotation.Nullable;

import com.google.common.base.Predicates;
import com.stormarmory.NaturesRevenge;
import com.stormarmory.MSounds;
import com.stormarmory.base.entity.BasicLandEntity;
import com.stormarmory.base.entity.model.AnimationMathHelper;
import com.stormarmory.capabilities.danger.CapabilityDangerLevel;
import com.stormarmory.capabilities.danger.IDangerLevel;
import com.stormarmory.config.ConfigEntity;
import com.stormarmory.entity.monsters.behavior.EntityAIHumanoidAttack;

import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityCreature;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.EnumCreatureAttribute;
import net.minecraft.entity.IEntityLivingData;
import net.minecraft.entity.SharedMonsterAttributes;
import net.minecraft.entity.ai.EntityAIBreakDoor;
import net.minecraft.entity.ai.EntityAIHurtByTarget;
import net.minecraft.entity.ai.EntityAILookIdle;
import net.minecraft.entity.ai.EntityAIMoveThroughVillage;
import net.minecraft.entity.ai.EntityAIMoveTowardsRestriction;
import net.minecraft.entity.ai.EntityAINearestAttackableTarget;
import net.minecraft.entity.ai.EntityAISwimming;
import net.minecraft.entity.ai.EntityAIWander;
import net.minecraft.entity.ai.EntityAIWatchClosest;
import net.minecraft.entity.ai.attributes.AttributeModifier;
import net.minecraft.entity.ai.attributes.IAttributeInstance;
import net.minecraft.entity.monster.EntityIronGolem;
import net.minecraft.entity.monster.EntityPigZombie;
import net.minecraft.entity.monster.EntityZombie;
import net.minecraft.entity.monster.EntityZombieVillager;
import net.minecraft.entity.passive.EntityChicken;
import net.minecraft.entity.passive.EntityCow;
import net.minecraft.entity.passive.EntityPig;
import net.minecraft.entity.passive.EntityRabbit;
import net.minecraft.entity.passive.EntitySheep;
import net.minecraft.entity.passive.EntityVillager;
import net.minecraft.entity.passive.IAnimals;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.init.Items;
import net.minecraft.init.MobEffects;
import net.minecraft.init.SoundEvents;
import net.minecraft.inventory.EntityEquipmentSlot;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.network.datasync.DataParameter;
import net.minecraft.network.datasync.DataSerializers;
import net.minecraft.network.datasync.EntityDataManager;
import net.minecraft.pathfinding.PathNavigateGround;
import net.minecraft.potion.PotionEffect;
import net.minecraft.util.EntitySelectors;
import net.minecraft.util.EnumParticleTypes;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.SoundEvent;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.world.DifficultyInstance;
import net.minecraft.world.EnumDifficulty;
import net.minecraft.world.World;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;

public class EntityUndead extends BasicLandEntity {

	static Random rand = new Random();
	private static final UUID BABY_SPEED_BOOST_ID = UUID.fromString("B9766B59-9566-4402-BC1F-2EE2A276D836");
	private static final AttributeModifier BABY_SPEED_BOOST = new AttributeModifier(BABY_SPEED_BOOST_ID,
			"Baby speed boost", 0.5D, 1);
	private static final DataParameter<Boolean> IS_CHILD = EntityDataManager.<Boolean>createKey(EntityZombie.class,
			DataSerializers.BOOLEAN);

	private final EntityAIBreakDoor breakDoor = new EntityAIBreakDoor(this);
	private boolean isBreakDoorsTaskSet;
	public static final ResourceLocation LOOT = new ResourceLocation(NaturesRevenge.MODID, "entity/undead");

	private static final DataParameter<Byte> TALK_SOUND = EntityDataManager.createKey(EntityUndead.class,
			DataSerializers.BYTE);
	private static final DataParameter<Boolean> SHOULD_JAW_MOVE = EntityDataManager.createKey(EntityUndead.class,
			DataSerializers.BOOLEAN);
	private static final DataParameter<Integer> LIVING_SOUND_TIMER = EntityDataManager.createKey(EntityUndead.class,
			DataSerializers.VARINT);

	public float jawFloat;
	public float breatheFloat;
	AnimationMathHelper animationTalk = new AnimationMathHelper();
	AnimationMathHelper animationBreathe = new AnimationMathHelper();
	private int animationTick;
	private float zombieWidth = -1.0F;
	private float zombieHeight;
	private int attackTimer;

	public EntityUndead(World worldIn) {
		super(worldIn, EntityUndead.class, 0.5F, 50, 4, 5);
		setSize(0.6F, 1.95F);
	}

	@Override
	protected void entityInit() {
		super.entityInit();
		this.getDataManager().register(IS_CHILD, Boolean.valueOf(false));
		dataManager.register(TALK_SOUND, (byte) 0);
		dataManager.register(SHOULD_JAW_MOVE, false);
		dataManager.register(LIVING_SOUND_TIMER, 0);
	}

	@Override
	protected void applyEntityAttributes() {
		super.applyEntityAttributes();
		this.getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE).setBaseValue(35.0D);
		this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(0.25D);
		this.getEntityAttribute(SharedMonsterAttributes.ATTACK_DAMAGE).setBaseValue(3.0D);
		this.getEntityAttribute(SharedMonsterAttributes.ARMOR).setBaseValue(2.0D);
	}

	public boolean attackEntityAsMob(Entity entityIn) {
		super.attackEntityAsMob(entityIn);
		this.attackTimer = 10;
		this.world.setEntityState(this, (byte) 4);
		this.playSound(MSounds.UNDEAD_HURT, 1.0F, 1.0F);
		return true;
	}

	@SideOnly(Side.CLIENT)
	public void handleStatusUpdate(byte id) {
		if (id == 4) {
			this.attackTimer = 10;
			this.playSound(MSounds.UNDEAD_HURT, 1.0F, 1.0F);
			setShouldJawMove(true);
		} else {
			super.handleStatusUpdate(id);
		}
	}

	@Override
	public boolean getCanSpawnHere() {
		IBlockState iblockstate = this.world.getBlockState((new BlockPos(this)).down());
		return this.world.getDifficulty() != EnumDifficulty.PEACEFUL && iblockstate.canEntitySpawn(this)
				&& !this.world.canBlockSeeSky(this.getPosition()) && this.getPosition().getY() < 42;
	}

	@SideOnly(Side.CLIENT)
	public int getAttackTimer() {
		return this.attackTimer;
	}

	public void onKillEntity(EntityLivingBase entityLivingIn) {
		super.onKillEntity(entityLivingIn);

		if ((this.world.getDifficulty() == EnumDifficulty.NORMAL || this.world.getDifficulty() == EnumDifficulty.HARD)
				&& entityLivingIn instanceof EntityVillager) {
			if (this.world.getDifficulty() != EnumDifficulty.HARD && this.rand.nextBoolean()) {
				return;
			}

			EntityVillager entityvillager = (EntityVillager) entityLivingIn;
			EntityUndeadVillager entityzombievillager = new EntityUndeadVillager(this.world);
			entityzombievillager.copyLocationAndAnglesFrom(entityvillager);
			this.world.removeEntity(entityvillager);
			entityzombievillager.onInitialSpawn(this.world.getDifficultyForLocation(new BlockPos(entityzombievillager)),
					new EntityUndead.GroupData(false));
			entityzombievillager.setChild(entityvillager.isChild());
			entityzombievillager.setNoAI(entityvillager.isAIDisabled());

			if (entityvillager.hasCustomName()) {
				entityzombievillager.setCustomNameTag(entityvillager.getCustomNameTag());
				entityzombievillager.setAlwaysRenderNameTag(entityvillager.getAlwaysRenderNameTag());
			}

			this.world.spawnEntity(entityzombievillager);
			this.world.playEvent((EntityPlayer) null, 1026, new BlockPos(this), 0);
		}
	}

	@Override
	protected void initEntityAI() {
		this.tasks.addTask(0, new EntityAISwimming(this));
		this.tasks.addTask(2, new EntityAIHumanoidAttack(this, 1.0D, false));
		this.tasks.addTask(5, new EntityAIMoveTowardsRestriction(this, 1.0D));
		this.tasks.addTask(7, new EntityAIWander(this, 1.0D));
		this.tasks.addTask(8, new EntityAIWatchClosest(this, EntityPlayer.class, 8.0F));
		this.tasks.addTask(9, new EntityAIWatchClosest(this, EntityVillager.class, 8.0F));
		this.tasks.addTask(8, new EntityAILookIdle(this));
		this.tasks.addTask(6, new EntityAIMoveThroughVillage(this, 1.0D, false));
		this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, false, new Class[0]));
		this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityPlayer.class, true));
		this.targetTasks.addTask(3, new EntityAINearestAttackableTarget(this, EntityVillager.class, false));
		this.targetTasks.addTask(3, new EntityAINearestAttackableTarget(this, EntityIronGolem.class, true));
	}

	protected SoundEvent getStepSound() {
		return SoundEvents.ENTITY_ZOMBIE_STEP;
	}

	protected void playStepSound(BlockPos pos, Block blockIn) {
		this.playSound(this.getStepSound(), 0.25F, 1.0F);
	}

	@Override
	protected SoundEvent getAmbientSound() {
		int randomSound = rand.nextInt(4) + 1;
		setTalkSound((byte) randomSound);
		switch (getTalkSound()) {
		case 1:
			return MSounds.UNDEAD_SAY1;
		case 2:
			return MSounds.UNDEAD_SAY2;
		default:
			return MSounds.UNDEAD_SAY3;
		}
	}

	@Override
	protected SoundEvent getHurtSound() {
		setTalkSound(4);
		setShouldJawMove(true);
		return MSounds.UNDEAD_HURT;
	}

	@Override
	protected SoundEvent getDeathSound() {
		setTalkSound(4);
		setShouldJawMove(true);
		return MSounds.UNDEAD_DEATH;
	}

	@Override
	public void onLivingUpdate() {
		breatheFloat = animationBreathe.swing(0.2F, 0.5F, false);

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

		if (!world.isRemote) {
			updateLivingSoundTime();
		}

		if (animationTick > 0) {
			animationTick--;
		}

		if (animationTick == 0) {
			setShouldJawMove(false);
			jawFloat = animationTalk.swing(0F, 0F, true);
		}

		if (getLivingSoundTime() == -getTalkInterval())
			setShouldJawMove(true);

		if (!shouldJawMove())
			jawFloat = animationTalk.swing(0F, 0F, true);
		else if (shouldJawMove() && getTalkSound() != 3 && getTalkSound() != 4)
			jawFloat = animationTalk.swing(2.0F, 0.1F, false);
		else if (shouldJawMove() && getTalkSound() == 3 || shouldJawMove() && getTalkSound() == 4)
			jawFloat = animationTalk.swing(0.4F, 1.2F, false);
		super.onLivingUpdate();
	}

	private byte getTalkSound() {
		return dataManager.get(TALK_SOUND);
	}

	private void setTalkSound(int soundIndex) {
		dataManager.set(TALK_SOUND, (byte) soundIndex);
	}

	public void setShouldJawMove(boolean jawState) {
		dataManager.set(SHOULD_JAW_MOVE, jawState);
		if (jawState)
			animationTick = 20;
	}

	public boolean shouldJawMove() {
		return dataManager.get(SHOULD_JAW_MOVE);
	}

	private void updateLivingSoundTime() {
		dataManager.set(LIVING_SOUND_TIMER, livingSoundTime);
	}

	private int getLivingSoundTime() {
		return dataManager.get(LIVING_SOUND_TIMER);
	}

	@Override
	@Nullable
	protected ResourceLocation getLootTable() {
		return LOOT;
	}

	@Override
	public int getMaxSpawnedInChunk() {
		return 5;
	}

	public EnumCreatureAttribute getCreatureAttribute() {
		return EnumCreatureAttribute.UNDEAD;
	}

	public boolean isBreakDoorsTaskSet() {
		return this.isBreakDoorsTaskSet;
	}

	public boolean isChild() {
		return ((Boolean) this.getDataManager().get(IS_CHILD)).booleanValue();
	}

	public void setChild(boolean childZombie) {
		this.getDataManager().set(IS_CHILD, Boolean.valueOf(childZombie));

		if (this.world != null && !this.world.isRemote) {
			IAttributeInstance iattributeinstance = this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED);
			iattributeinstance.removeModifier(BABY_SPEED_BOOST);

			if (childZombie) {
				iattributeinstance.applyModifier(BABY_SPEED_BOOST);
			}
		}

		this.setChildSize(childZombie);
	}

	public void notifyDataManagerChange(DataParameter<?> key) {
		if (IS_CHILD.equals(key)) {
			this.setChildSize(this.isChild());
		}

		super.notifyDataManagerChange(key);
	}

	protected int getExperiencePoints(EntityPlayer player) {
		if (this.isChild()) {
			this.experienceValue = (int) ((float) this.experienceValue * 2.5F);
		}

		return super.getExperiencePoints(player);
	}

	public void setBreakDoorsAItask(boolean enabled) {
		if (this.isBreakDoorsTaskSet != enabled) {
			this.isBreakDoorsTaskSet = enabled;
			((PathNavigateGround) this.getNavigator()).setBreakDoors(enabled);

			if (enabled) {
				this.tasks.addTask(1, this.breakDoor);
			} else {
				this.tasks.removeTask(this.breakDoor);
			}
		}
	}

	public void writeEntityToNBT(NBTTagCompound compound) {
		super.writeEntityToNBT(compound);
		if (this.isChild()) {
			compound.setBoolean("IsBaby", true);
		}
		compound.setBoolean("CanBreakDoors", this.isBreakDoorsTaskSet());
	}

	public void readEntityFromNBT(NBTTagCompound compound) {
		super.readEntityFromNBT(compound);
		if (compound.getBoolean("IsBaby")) {
			this.setChild(true);
		}
		this.setBreakDoorsAItask(compound.getBoolean("CanBreakDoors"));
	}

	public void setChildSize(boolean isChild) {
		this.multiplySize(isChild ? 0.5F : 1.0F);
	}

	protected final void setSize(float width, float height) {
		boolean flag = this.zombieWidth > 0.0F && this.zombieHeight > 0.0F;
		this.zombieWidth = width;
		this.zombieHeight = height;

		if (!flag) {
			this.multiplySize(1.0F);
		}
	}

	protected final void multiplySize(float size) {
		super.setSize(this.zombieWidth * size, this.zombieHeight * size);
	}

	public double getYOffset() {
		return this.isChild() ? 0.0D : -0.45D;
	}

	@Override
	public float getEyeHeight() {
		float f = 1.35F;
		if (this.isChild()) {
			f = (float) ((double) f - 0.81D);
		}
		return f;
	}

	@Nullable
	public IEntityLivingData onInitialSpawn(DifficultyInstance difficulty, @Nullable IEntityLivingData livingdata) {
		livingdata = super.onInitialSpawn(difficulty, livingdata);
		float f = difficulty.getClampedAdditionalDifficulty();
		this.setBreakDoorsAItask(this.rand.nextFloat() < f * 0.25F);
		if (rand.nextInt(20) == 1) {
			this.setChild(true);
		}
		if (danger != null && danger.getDangerLevel() >= 2) {
			this.setBreakDoorsAItask(true);
		}
		return livingdata;
	}

	class GroupData implements IEntityLivingData {
		public boolean isChild;

		private GroupData(boolean p_i47328_2_) {
			this.isChild = p_i47328_2_;
		}
	}
}