package thebetweenlands.common.entity.projectiles;

import java.util.List;

import javax.annotation.Nullable;

import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.init.MobEffects;
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.potion.PotionEffect;
import net.minecraft.util.EnumParticleTypes;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.EnumDifficulty;
import net.minecraft.world.World;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import thebetweenlands.api.capability.IRotSmellCapability;
import thebetweenlands.client.render.particle.BLParticles;
import thebetweenlands.client.render.particle.BatchedParticleRenderer;
import thebetweenlands.client.render.particle.DefaultParticleBatches;
import thebetweenlands.client.render.particle.ParticleFactory;
import thebetweenlands.client.render.particle.ParticleFactory.ParticleArgs;
import thebetweenlands.client.render.particle.entity.ParticleGasCloud;
import thebetweenlands.common.entity.EntitySplodeshroom;
import thebetweenlands.common.herblore.elixir.ElixirEffectRegistry;
import thebetweenlands.common.registries.CapabilityRegistry;
import thebetweenlands.common.registries.SoundRegistry;

public class EntityChiromawDroppings extends Entity {

	private static final DataParameter<Boolean> HAS_EXPLODED = EntityDataManager.createKey(EntitySplodeshroom.class, DataSerializers.BOOLEAN);
	private static final DataParameter<Float> AOE_SIZE_XZ = EntityDataManager.createKey(EntitySplodeshroom.class, DataSerializers.FLOAT);
	private static final DataParameter<Float> AOE_SIZE_Y = EntityDataManager.createKey(EntitySplodeshroom.class, DataSerializers.FLOAT);

	public float prevRotationTicks = 0;
	public float rotationTicks = 0;
	protected EntityLivingBase thrower;
    public Entity ignoreEntity;
    private int ignoreTime;

	public EntityChiromawDroppings(World world) {
		super(world);
		setSize(0.5F, 0.5F);
		setEntityInvulnerable(true);
	}

	public EntityChiromawDroppings(World world, EntityLivingBase throwerIn, double x, double y, double z) {
		super(world);
		setSize(0.5F, 0.5F);
		setPosition(x, y, z);
		setEntityInvulnerable(true);
		thrower = throwerIn;
	}

	@Override
	protected void entityInit() {
		dataManager.register(HAS_EXPLODED, false);
		dataManager.register(AOE_SIZE_XZ, 4F);
		dataManager.register(AOE_SIZE_Y, 0.5F);
	}

	@SideOnly(Side.CLIENT)
	private void spawnDroppingsParticles() {
		double d0 = this.posX - 0.075F;
		double d1 = this.posY + this.motionY;
		double d2 = this.posZ - 0.075F;
		double d3 = this.posX + 0.075F;
		double d4 = this.posZ + 0.075F;
		double d5 = this.posX;
		double d6 = this.posY + this.motionY + 0.25F;
		double d7 = this.posZ;

		BLParticles.CHIROMAW_DROPPINGS.spawn(world, d0, d1, d4, ParticleArgs.get().withMotion(0.08f * (d1) * (rand.nextFloat() - 0.5f), this.motionY + 0.1f * (rand.nextFloat() - 0.5f), 0.08f * (d1) * (rand.nextFloat() - 0.5f)).withScale(2.5F).withData(100)).setRBGColorF(0.4118F, 0.2745F, 0.1568F);
		BLParticles.CHIROMAW_DROPPINGS.spawn(world, d3, d1, d2, ParticleArgs.get().withMotion(0.08f * (d1) * (rand.nextFloat() - 0.5f), this.motionY + 0.1f * (rand.nextFloat() - 0.5f), 0.08f * (d1) * (rand.nextFloat() - 0.5f)).withScale(2.5F).withData(100)).setRBGColorF(0.4118F, 0.2745F, 0.1568F);
		BLParticles.CHIROMAW_DROPPINGS.spawn(world, d3, d1, d4, ParticleArgs.get().withMotion(0.08f * (d1) * (rand.nextFloat() - 0.5f), this.motionY + 0.1f * (rand.nextFloat() - 0.5f), 0.08f * (d1) * (rand.nextFloat() - 0.5f)).withScale(2.5F).withData(100)).setRBGColorF(0.4118F, 0.2745F, 0.1568F);
		BLParticles.CHIROMAW_DROPPINGS.spawn(world, d0, d1, d2, ParticleArgs.get().withMotion(0.08f * (d1) * (rand.nextFloat() - 0.5f), this.motionY + 0.1f * (rand.nextFloat() - 0.5f), 0.08f * (d1) * (rand.nextFloat() - 0.5f)).withScale(2.5F).withData(100)).setRBGColorF(0.4118F, 0.2745F, 0.1568F);
		BLParticles.CHIROMAW_DROPPINGS.spawn(world, d5, d6, d7, ParticleArgs.get().withMotion(0.08f * (d1) * (rand.nextFloat() - 0.5f), this.motionY + 0.1f * (rand.nextFloat() - 0.5f), 0.08f * (d1) * (rand.nextFloat() - 0.5f)).withScale(2.5F).withData(100)).setRBGColorF(0.4118F, 0.2745F, 0.1568F);
		BLParticles.CHIROMAW_DROPPINGS.spawn(world, d0, d1, d2, ParticleArgs.get().withMotion(0.08f * (d1) * (rand.nextFloat() - 0.5f), this.motionY + 0.1f * (rand.nextFloat() - 0.5f), 0.08f * (d1) * (rand.nextFloat() - 0.5f)).withScale(2.5F).withData(100)).setRBGColorF(0.4118F, 0.2745F, 0.1568F);
	}
	
	@Override
	public void onUpdate() {
		super.onUpdate();
		// TODO in case we want an animation grab smoothedAngle(float partialTicks) method for render class
		prevRotationTicks = rotationTicks;
		rotationTicks += 15;
		float wrap = MathHelper.wrapDegrees(rotationTicks) - rotationTicks;
		rotationTicks +=wrap;
		prevRotationTicks += wrap;

		if (!getEntityWorld().isRemote) {
			if(getHasExploded()) {
				if (getEntityWorld().getTotalWorldTime() % 5 == 0)
					checkAreaOfEffect();
				if (getAOESizeXZ() > 0.5F)
					setAOESizeXZ(getAOESizeXZ() - 0.01F);
				if (getAOESizeXZ() <= 0.5F)
					setDead();
			}
		} else {
			if (!getHasExploded() && this.ticksExisted % 4 == 0) {
				this.spawnDroppingsParticles();
			}
		}

		if (getHasExploded())
			setBoundingBoxSize();

		if (getEntityWorld().isRemote)
			if(getHasExploded())
				spawnCloudParticle();

		lastTickPosX = posX;
        lastTickPosY = posY;
        lastTickPosZ = posZ;

        Vec3d poopVector = new Vec3d(posX, posY, posZ);
        Vec3d poopMovementVector = new Vec3d(posX + motionX, posY + motionY, posZ + motionZ);
        RayTraceResult raytraceresult = world.rayTraceBlocks(poopVector, poopMovementVector);
        poopVector = new Vec3d(posX, posY, posZ);
        poopMovementVector = new Vec3d(posX + motionX, posY + motionY, posZ + motionZ);

        if (raytraceresult != null)
        	poopMovementVector = new Vec3d(raytraceresult.hitVec.x, raytraceresult.hitVec.y, raytraceresult.hitVec.z);

        Entity entityCollidedWith = null;
        List<Entity> list = world.getEntitiesWithinAABBExcludingEntity(this, getEntityBoundingBox().expand(motionX, motionY, motionZ).grow(1.0D));
        double collisionDistance = 0D;
        boolean miss = false;

        for (int entityCount = 0; entityCount < list.size(); ++entityCount)  {
            Entity entityInList = list.get(entityCount);

            if (entityInList.canBeCollidedWith()) {
                if (entityInList == ignoreEntity)
                	miss = true;
                else if (thrower != null && ticksExisted < 2 && ignoreEntity == null) {
                    ignoreEntity = entityInList;
                    miss = true;
                }
                else {
                	miss = false;
                    AxisAlignedBB axisalignedbb = entityInList.getEntityBoundingBox().grow(0.30000001192092896D);
                    RayTraceResult raytraceresult1 = axisalignedbb.calculateIntercept(poopVector, poopMovementVector);

                    if (raytraceresult1 != null) {
                        double inpactDistance = poopVector.squareDistanceTo(raytraceresult1.hitVec);

                        if (inpactDistance < collisionDistance || collisionDistance == 0.0D) {
                        	entityCollidedWith = entityInList;
                            collisionDistance = inpactDistance;
                        }
                    }
                }
            }
        }

        if (ignoreEntity != null)
            if (miss)
                ignoreTime = 2;
            else if (ignoreTime-- <= 0)
                ignoreEntity = null;

        if (entityCollidedWith != null)
            raytraceresult = new RayTraceResult(entityCollidedWith);

        if (raytraceresult != null)
            if (raytraceresult.typeOfHit == RayTraceResult.Type.BLOCK && world.getBlockState(raytraceresult.getBlockPos()).getBlock() == Blocks.PORTAL)
                setPortal(raytraceresult.getBlockPos());
            else if (!net.minecraftforge.event.ForgeEventFactory.onProjectileImpact(this, raytraceresult))
                onImpact(raytraceresult);

        posX += motionX;
        posY += motionY;
        posZ += motionZ;

        float fallAmount = 0.99F;
        float fallAmountWithGravity = getGravityVelocity();

        if (isInWater()) {
            for (int particleCount = 0; particleCount < 4; ++particleCount)
                world.spawnParticle(EnumParticleTypes.WATER_BUBBLE, posX - motionX * 0.25D, posY - motionY * 0.25D, posZ - motionZ * 0.25D, motionX, motionY, motionZ);
            fallAmount = 0.8F;
        }

        if(getHasExploded())
        	fallAmount = 0.0F;

        motionX *= (double)fallAmount;
        motionY *= (double)fallAmount;
        motionZ *= (double)fallAmount;

        if (!hasNoGravity())
            motionY -= (double)fallAmountWithGravity;

        if (raytraceresult != null)
        	if(getHasExploded())
        		if(posY < raytraceresult.hitVec.y)
        			posY = raytraceresult.hitVec.y;

        setPosition(posX, posY, posZ);
	}

	@Nullable
	protected Entity checkAreaOfEffect() {
		Entity entity = null;
		if (!getEntityWorld().isRemote && getEntityWorld().getDifficulty() != EnumDifficulty.PEACEFUL) {
			List<EntityPlayer> list = getEntityWorld().getEntitiesWithinAABB(EntityPlayer.class, getEntityBoundingBox());
			for (int entityCount = 0; entityCount < list.size(); entityCount++) {
				entity = list.get(entityCount);
				if (entity != null)
					if (entity instanceof EntityPlayer) {
						EntityPlayer player = (EntityPlayer) entity;
						if(!player.isSpectator() && !player.isCreative()) {
							player.addPotionEffect(new PotionEffect(MobEffects.BLINDNESS, 60));
							player.addPotionEffect(ElixirEffectRegistry.EFFECT_DECAY.createEffect(40, 1));
								IRotSmellCapability capSmell = player.getCapability(CapabilityRegistry.CAPABILITY_ROT_SMELL, null);
								if(capSmell != null)
									if(!capSmell.isSmellingBad())
										capSmell.setSmellingBad(Math.max(capSmell.getRemainingSmellyTicks(), 24000));
							}
						}
					}
				}
		return entity;
	}

    protected float getGravityVelocity() {
    	if(!getHasExploded())
    		return 0.03F;
    	return 0F;
    }

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

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

	@Override
    protected boolean canBeRidden(Entity entityIn) {
        return false;
    }

	protected void onImpact(RayTraceResult result) {
		if (!getHasExploded() && result.typeOfHit != null) {
			if(result.typeOfHit == RayTraceResult.Type.BLOCK || result.typeOfHit == RayTraceResult.Type.ENTITY && !(result.entityHit instanceof EntityChiromawDroppings) && result.entityHit != thrower)
			if (!getEntityWorld().isRemote) {
				setHasExploded(true);
				getEntityWorld().playSound(null, getPosition(), SoundRegistry.CHIROMAW_MATRIARCH_SPLAT, SoundCategory.HOSTILE, 1F, 1F + (getEntityWorld().rand.nextFloat() - getEntityWorld().rand.nextFloat()) * 0.8F);
			}
		}
	}

	protected void setBoundingBoxSize() {
		AxisAlignedBB axisalignedbb = new AxisAlignedBB(posX - getAOESizeXZ() * 0.5D, posY, posZ - getAOESizeXZ() * 0.5D, posX + getAOESizeXZ() * 0.5D, posY + getAOESizeY(), posZ + getAOESizeXZ() * 0.5D);
		setEntityBoundingBox(axisalignedbb);
	}

	@Override
    public void notifyDataManagerChange(DataParameter<?> key) {
        if (AOE_SIZE_XZ.equals(key)) 
            setAOESizeXZ(getAOESizeXZ());
        if (AOE_SIZE_Y.equals(key)) 
            setAOESizeY(getAOESizeY());
        super.notifyDataManagerChange(key);
    }

	private void setHasExploded(boolean hasExploded) {
		dataManager.set(HAS_EXPLODED, hasExploded);
	}

    public boolean getHasExploded() {
        return dataManager.get(HAS_EXPLODED);
    }

	private void setAOESizeXZ(float aoeSizeXZ) {
		dataManager.set(AOE_SIZE_XZ, aoeSizeXZ);
	}

	public float getAOESizeXZ() {
		return dataManager.get(AOE_SIZE_XZ);
	}

	private void setAOESizeY(float aoeSizeY) {
		dataManager.set(AOE_SIZE_Y, aoeSizeY);
	}

	public float getAOESizeY() {
		return dataManager.get(AOE_SIZE_Y);
	}

	@SideOnly(Side.CLIENT)
	private void spawnCloudParticle() {
		double x = posX + (world.rand.nextFloat() - 0.5F) / 2.0F;
		double y = posY + 0.1D;
		double z = posZ + (world.rand.nextFloat() - 0.5F) / 2.0F;
		double mx = (world.rand.nextFloat() - 0.5F) / 12.0F;
		double my = (world.rand.nextFloat() - 0.5F) / 16.0F * 0.1F;
		double mz = (world.rand.nextFloat() - 0.5F) / 12.0F;
		int[] color = {100, 100, 0, 255};

		ParticleGasCloud hazeParticle = (ParticleGasCloud) BLParticles.GAS_CLOUD
				.create(world, x, y, z, ParticleFactory.ParticleArgs.get()
						.withData(null)
						.withMotion(mx, my, mz)
						.withColor(color[0] / 255.0F, color[1] / 255.0F, color[2] / 255.0F, color[3] / 255.0F)
						.withScale(8f));
		
		BatchedParticleRenderer.INSTANCE.addParticle(DefaultParticleBatches.GAS_CLOUDS_HEAT_HAZE, hazeParticle);
		
		ParticleGasCloud particle = (ParticleGasCloud) BLParticles.GAS_CLOUD
				.create(world, x, y, z, ParticleFactory.ParticleArgs.get()
						.withData(null)
						.withMotion(mx, my, mz)
						.withColor(color[0] / 255.0F, color[1] / 255.0F, color[2] / 255.0F, color[3] / 255.0F)
						.withScale(4f));

		BatchedParticleRenderer.INSTANCE.addParticle(DefaultParticleBatches.GAS_CLOUDS_TEXTURED, particle);
	}

	@Override
	public boolean shouldRenderInPass(int pass) {
		return pass == 1;
	}

	@Override
	protected void readEntityFromNBT(NBTTagCompound compound) {
	}

	@Override
	protected void writeEntityToNBT(NBTTagCompound compound) {
	}
}
