package thebetweenlands.common.entity.mobs;

import java.util.List;

import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.IEntityMultiPart;
import net.minecraft.entity.MultiPartEntityPart;
import net.minecraft.entity.SharedMonsterAttributes;
import net.minecraft.entity.ai.EntityAIAttackMelee;
import net.minecraft.entity.ai.EntityAILookIdle;
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.monster.EntityMob;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.network.datasync.DataParameter;
import net.minecraft.network.datasync.DataSerializers;
import net.minecraft.network.datasync.EntityDataManager;
import net.minecraft.util.DamageSource;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.SoundEvent;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import thebetweenlands.api.entity.IEntityBL;
import thebetweenlands.common.entity.ai.EntityAIHurtByTargetImproved;
import thebetweenlands.common.registries.LootTableRegistry;
import thebetweenlands.common.registries.SoundRegistry;
import thebetweenlands.util.PlayerUtil;

public class EntityShambler extends EntityMob implements IEntityMultiPart, IEntityBL {

	private static final DataParameter<Boolean> JAWS_OPEN = EntityDataManager.createKey(EntityShambler.class, DataSerializers.BOOLEAN);
	private static final DataParameter<Boolean> TONGUE_EXTEND = EntityDataManager.createKey(EntityShambler.class, DataSerializers.BOOLEAN);
	private static final DataParameter<Integer> JAW_ANGLE = EntityDataManager.createKey(EntityShambler.class, DataSerializers.VARINT);
	private static final DataParameter<Integer> TONGUE_LENGTH = EntityDataManager.createKey(EntityShambler.class, DataSerializers.VARINT);

	private int prevJawAngle;
	private int prevTongueLength;
	
	public MultiPartEntityPart[] tongue_array; // we may want to make more tongue parts
	public MultiPartEntityPart tongue_end = new MultiPartEntityPart(this, "tongue_end", 0.5F, 0.5F);

	public EntityShambler(World world) {
		super(world);
		this.setSize(0.95F, 1.25F);
		tongue_array = new MultiPartEntityPart[16];
		for(int i = 0; i < tongue_array.length - 1; i++) {
			tongue_array[i] = new MultiPartEntityPart(this, "tongue_" + i, 0.125F, 0.125F);
		}
		tongue_array[tongue_array.length - 1] = tongue_end;
	}

	@Override
	public World getWorld() {
		return getEntityWorld();
	}

	@Override
	protected void initEntityAI() {
		this.tasks.addTask(0, new EntityAISwimming(this));
		this.tasks.addTask(1, new EntityAIAttackMelee(this, 0.8D, true));
		this.tasks.addTask(2, new EntityAIMoveTowardsRestriction(this, 1.0D));
		this.tasks.addTask(3, new EntityAIWander(this, 0.75D));
		this.tasks.addTask(4, new EntityAIWatchClosest(this, EntityPlayer.class, 8.0F));
		this.tasks.addTask(5, new EntityAILookIdle(this));

		this.targetTasks.addTask(0, new EntityAIHurtByTargetImproved(this, true));
		this.targetTasks.addTask(1, new EntityAINearestAttackableTarget<>(this, EntityPlayer.class, 3, true, true, null).setUnseenMemoryTicks(120));
		this.targetTasks.addTask(2, new EntityAINearestAttackableTarget<>(this, EntityFrog.class, 3, true, true, null).setUnseenMemoryTicks(120));
		this.targetTasks.addTask(3, new EntityAINearestAttackableTarget<>(this, EntityChiromaw.class, 3, true, true, null).setUnseenMemoryTicks(120));
		this.targetTasks.addTask(4, new EntityAINearestAttackableTarget<>(this, EntityMireSnail.class, 3, true, true, null).setUnseenMemoryTicks(120));
		this.targetTasks.addTask(5, new EntityAINearestAttackableTarget<>(this, EntityBloodSnail.class, 3, true, true, null).setUnseenMemoryTicks(120));
		this.targetTasks.addTask(6, new EntityAINearestAttackableTarget<>(this, EntityDragonFly.class, 3, true, true, null).setUnseenMemoryTicks(120));
	}

	@Override
	protected void entityInit() {
		super.entityInit();
		dataManager.register(JAWS_OPEN, false);
		dataManager.register(TONGUE_EXTEND, false);
		dataManager.register(JAW_ANGLE, 0);
		dataManager.register(TONGUE_LENGTH, 0);
	}
	
	@Override
	protected ResourceLocation getLootTable() {
		return LootTableRegistry.SHAMBLER;
	}

	public boolean jawsAreOpen() {
		return dataManager.get(JAWS_OPEN);
	}

	public void setOpenJaws(boolean jawState) {
		dataManager.set(JAWS_OPEN, jawState);
	}

	public boolean isExtendingTongue() {
		return dataManager.get(TONGUE_EXTEND);
	}

	public void setExtendingTongue(boolean tongueState) {
		dataManager.set(TONGUE_EXTEND, tongueState);
	}

	public void setJawAngle(int count) {
		dataManager.set(JAW_ANGLE, count);
	}

	public void setJawAnglePrev(int count) {
		prevJawAngle = count;
	}

	public void setTongueLength(int count) {
		dataManager.set(TONGUE_LENGTH, count);
	}

	public void setTongueLengthPrev(int count) {
		prevTongueLength = count;
	}

	public int getJawAngle() {
		return dataManager.get(JAW_ANGLE);
	}

	public int getJawAnglePrev() {
		return prevJawAngle;
	}

	public int getTongueLength() {
		return dataManager.get(TONGUE_LENGTH);
	}

	public int getTongueLengthPrev() {
		return prevTongueLength;
	}

	@Override
	protected void applyEntityAttributes() {
		super.applyEntityAttributes();
		getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(0.3D);
		getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(20.0D);
		getEntityAttribute(SharedMonsterAttributes.ATTACK_DAMAGE).setBaseValue(2.0D);
		getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE).setBaseValue(20.0D);
	}

	@Override
	public boolean getCanSpawnHere() {
		return super.getCanSpawnHere();
	}

	@Override
	public int getMaxSpawnedInChunk() {
		return 3;
	}
	
	@Override
	protected float getSoundPitch() {
		return super.getSoundPitch() * 1.5F;
	}

	@Override
	protected SoundEvent getAmbientSound() {
		return SoundRegistry.SHAMBLER_LIVING;
	}

	@Override
	protected SoundEvent getHurtSound(DamageSource damageSource) {
		return SoundRegistry.SHAMBLER_HURT;
	}

	@Override
	protected SoundEvent getDeathSound() {
		return SoundRegistry.SHAMBLER_DEATH;
	}

	@Override
	public void onLivingUpdate() {
		setJawAnglePrev(getJawAngle());
		setTongueLengthPrev(getTongueLength());
		
		if (!getEntityWorld().isRemote) {
			if (getAttackTarget() != null && canEntityBeSeen(getAttackTarget())) {
				faceEntity(getAttackTarget(), 10.0F, 20.0F);
				double distance = getDistance(getAttackTarget().posX, getAttackTarget().getEntityBoundingBox().minY, getAttackTarget().posZ);

				if (distance > 5.0D) {
					if(jawsAreOpen()) {
						setOpenJaws(false);
						if (isExtendingTongue())
							setExtendingTongue(false);
					}
				}

				if (distance <= 5.0D && distance >= 1) {
					if (!jawsAreOpen()) {
						setOpenJaws(true);
						if (!isExtendingTongue()) {
							setExtendingTongue(true);
							playSound(SoundRegistry.SHAMBLER_LICK, 1F, 1F + this.rand.nextFloat() * 0.3F);
						}
					}
				}
			}

			if (getAttackTarget() == null) {
				if(jawsAreOpen())
					setOpenJaws(false);
				if (isExtendingTongue())
					setExtendingTongue(false);
			}

			if (getJawAngle() > 0 && !jawsAreOpen())
				setJawAngle(getJawAngle() - 1);

			if (jawsAreOpen() && getJawAngle() <= 10)
				setJawAngle(getJawAngle() + 1);

			if (getJawAngle() < 0 && !jawsAreOpen())
				setJawAngle(0);

			if (jawsAreOpen() && getJawAngle() > 10)
				setJawAngle(10);

			if (getTongueLength() > 0 && !isExtendingTongue())
				setTongueLength(getTongueLength() - 1);

			if (isExtendingTongue() && getTongueLength() <= 9)
				setTongueLength(getTongueLength() + 1);

			if (getTongueLength() < 0 && !isExtendingTongue())
				setTongueLength(0);

			if (isExtendingTongue() && getTongueLength() > 9) {
				setTongueLength(9);
				setExtendingTongue(false);
			}
		}
		super.onLivingUpdate();
	}

	@Override
    public void onUpdate() {
		super.onUpdate();
		
		Vec3d vector = getLook(1);
		
		double offSetX = vector.x * -0.25D;
		double offsetY = vector.y * -0.25D;
		double offSetZ = vector.z * -0.25D;
		
		double lengthIncrement = 0.5D / tongue_array.length;
		
		double tongueLength = lengthIncrement;
		
		for(MultiPartEntityPart part : tongue_array) {
			part.prevRotationYaw = part.rotationYaw;
			part.prevRotationPitch = part.rotationPitch;
			
			part.lastTickPosX = part.prevPosX = part.posX;
			part.lastTickPosY = part.prevPosY = part.posY;
			part.lastTickPosZ = part.prevPosZ = part.posZ;
			
			part.setPosition(posX + offSetX + ((double) vector.x * getTongueLength() * tongueLength), posY + this.getEyeHeight() - 0.32 + offsetY + ((double) vector.y * getTongueLength() * tongueLength), posZ + offSetZ + ((double) vector.z * getTongueLength() * tongueLength));
			part.rotationYaw = this.rotationYaw;
			part.rotationPitch = this.rotationPitch;
		
			tongueLength += lengthIncrement;
		}
		
		checkCollision();
    }
	
	@Override
	public void updatePassenger(Entity entity) {
		PlayerUtil.resetFloating(entity);
		
		if (entity instanceof EntityLivingBase) {
			double a = Math.toRadians(rotationYaw);
			double offSetX = Math.sin(a) * getTongueLength() > 0 ? -0.125D : -0.35D;
			double offSetZ = -Math.cos(a) * getTongueLength() > 0 ? -0.125D : -0.35D;
			entity.setPosition(tongue_end.posX + offSetX, tongue_end.posY - entity.height * 0.3D, tongue_end.posZ + offSetZ);
			if (entity.isSneaking())
				entity.setSneaking(false);
		}
	}

	@Override
	public boolean canRiderInteract() {
		return true;
	}

	@Override
	public boolean shouldRiderSit() {
		return false;
	}

	protected Entity checkCollision() {
		List<EntityLivingBase> list = getEntityWorld().getEntitiesWithinAABB(EntityLivingBase.class, tongue_end.getEntityBoundingBox());
		for (int i = 0; i < list.size(); i++) {
			Entity entity = list.get(i);
			if (entity != null && entity == getAttackTarget() && !(entity instanceof IEntityMultiPart) && !(entity instanceof MultiPartEntityPart)) {
				if (entity instanceof EntityLivingBase)
					if (!isBeingRidden()) {
						entity.startRiding(this, true);
						if (!getEntityWorld().isRemote)
							if (isExtendingTongue())
								setExtendingTongue(false); //eeeeeh!
					}
			}
		}
		return null;
	}

    @SideOnly(Side.CLIENT)
    public float smoothedAngle(float partialTicks) {
        return getJawAnglePrev() + (getJawAngle() - getJawAnglePrev()) * partialTicks;
    }

	@Override
	public boolean attackEntityAsMob(Entity entity) {
		return canEntityBeSeen(entity) ? super.attackEntityAsMob(entity) : false;
	}

	@Override
	public boolean attackEntityFromPart(MultiPartEntityPart part, DamageSource source, float dmg) {
		damageShambler(source, dmg); // we may want seperate tongue damage - dunno
		return true;
	}

	protected boolean damageShambler(DamageSource source, float ammount) {
		return super.attackEntityFrom(source, ammount);
	}
}
