package thebetweenlands.common.entity.projectiles;

import java.util.List;

import javax.annotation.Nullable;

import com.google.common.base.Predicate;
import com.google.common.base.Predicates;

import net.minecraft.block.state.IBlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.particle.Particle;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.IEntityOwnable;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.projectile.EntityArrow;
import net.minecraft.init.Blocks;
import net.minecraft.init.MobEffects;
import net.minecraft.init.SoundEvents;
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.potion.PotionEffect;
import net.minecraft.util.EntitySelectors;
import net.minecraft.util.SoundEvent;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import net.minecraftforge.fml.common.registry.IThrowableEntity;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import thebetweenlands.client.render.particle.BLParticles;
import thebetweenlands.client.render.particle.BatchedParticleRenderer;
import thebetweenlands.client.render.particle.DefaultParticleBatches;
import thebetweenlands.client.render.particle.ParticleFactory.ParticleArgs;
import thebetweenlands.common.entity.EntityShock;
import thebetweenlands.common.entity.mobs.EntityChiromawMatriarch;
import thebetweenlands.common.entity.mobs.EntityTinySludgeWormHelper;
import thebetweenlands.common.herblore.elixir.ElixirEffectRegistry;
import thebetweenlands.common.item.tools.bow.EnumArrowType;
import thebetweenlands.common.registries.ItemRegistry;
import thebetweenlands.common.registries.SoundRegistry;

public class EntityBLArrow extends EntityArrow implements IThrowableEntity /*for shooter sync*/ {
	@SuppressWarnings("unchecked")
	private static final Predicate<Entity> ARROW_TARGETS = Predicates.and(EntitySelectors.NOT_SPECTATING, EntitySelectors.IS_ALIVE, new Predicate<Entity>() {
		@Override
		public boolean apply(@Nullable Entity entity) {
			return entity.canBeCollidedWith();
		}
	});

	private static final DataParameter<String> DW_TYPE = EntityDataManager.<String>createKey(EntityBLArrow.class, DataSerializers.STRING);

	private int ticksSpentInAir = 0;
	private int ticksSpentInGround = 0;

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

	public EntityBLArrow(World worldIn, EntityLivingBase shooter) {
		super(worldIn, shooter);
	}

	@Override
	public Entity getThrower() {
		return this.shootingEntity;
	}

	@Override
	public void setThrower(Entity entity) {
		this.shootingEntity = entity;
	}

	@Override
	public void entityInit() {
		super.entityInit();
		this.dataManager.register(DW_TYPE, "");
	}

	@Override
	public void writeEntityToNBT(NBTTagCompound nbt) {
		super.writeEntityToNBT(nbt);
		nbt.setString("arrowType", this.getArrowType().getName());
	}

	@Override
	public void readEntityFromNBT(NBTTagCompound nbt) {
		super.readEntityFromNBT(nbt);
		this.setType(EnumArrowType.getEnumFromString(nbt.getString("arrowType")));
	}

	@Override
	@Nullable
	protected Entity findEntityOnPath(Vec3d start, Vec3d end) {
		List<Entity> list = this.world.getEntitiesInAABBexcluding(this, this.getEntityBoundingBox().expand(this.motionX, this.motionY, this.motionZ).grow(1.0D), ARROW_TARGETS);

		Entity hit = null;
		double minDstSq = 0.0D;

		for(Entity entity : list) {
			if(this.isNotShootingEntity(entity) || this.ticksSpentInAir >= 5) {
				AxisAlignedBB checkBox = entity.getEntityBoundingBox().grow(0.3D);
				RayTraceResult rayTrace = checkBox.calculateIntercept(start, end);

				if(rayTrace != null) {
					double dstSq = start.squareDistanceTo(rayTrace.hitVec);

					if(dstSq < minDstSq || minDstSq == 0.0D) {
						hit = entity;
						minDstSq = dstSq;
					}
				}
			}
		}

		return hit;
	}

	private boolean isNotShootingEntity(Entity entity) {
		if(entity == this.shootingEntity) {
			return false;
		} else if(this.shootingEntity instanceof EntityPlayer == false && this.shootingEntity != null && this.shootingEntity.getRidingEntity() == entity) {
			return false;
		} else if(this.shootingEntity instanceof EntityPlayer && this.shootingEntity != null && entity instanceof IEntityOwnable &&
				((IEntityOwnable) entity).getOwner() == this.shootingEntity && this.shootingEntity.getRecursivePassengers().contains(entity)) {
			return false;
		}
		return true;
	}

	@Override
	public void onUpdate() {
		super.onUpdate();

		if(this.inGround) {
			this.ticksSpentInAir = 0;
			this.ticksSpentInGround++;
		} else {
			this.ticksSpentInAir++;
			this.ticksSpentInGround = 0;
		}

		if(!this.world.isRemote && (this.getArrowType() == EnumArrowType.CHIROMAW_BARB || this.getArrowType() == EnumArrowType.CHIROMAW_SHOCK_BARB) && this.pickupStatus != PickupStatus.ALLOWED && this.ticksSpentInGround > 100) {
			this.setDead();
		}
		
		if(this.world.isRemote && (this.getArrowType() == EnumArrowType.SHOCK || this.getArrowType() == EnumArrowType.CHIROMAW_SHOCK_BARB)) {
			this.spawnLightningArcs();
		}
	}

	@SideOnly(Side.CLIENT)
	private void spawnLightningArcs() {
		Entity view = Minecraft.getMinecraft().getRenderViewEntity();
		if(view != null && view.getDistance(this) < 16 && this.world.rand.nextInt(!this.inGround ? 2 : 20) == 0) {
			float ox = this.world.rand.nextFloat() - 0.5f + (!this.inGround ? (float)this.motionX : 0);
			float oy = this.world.rand.nextFloat() - 0.5f + (!this.inGround ? (float)this.motionY : 0);
			float oz = this.world.rand.nextFloat() - 0.5f + (!this.inGround ? (float)this.motionZ : 0);

			Particle particle = BLParticles.LIGHTNING_ARC.create(this.world, this.posX, this.posY, this.posZ, 
					ParticleArgs.get()
					.withMotion(!this.inGround ? this.motionX : 0, !this.inGround ? this.motionY : 0, !this.inGround ? this.motionZ : 0)
					.withColor(0.3f, 0.5f, 1.0f, 0.9f)
					.withData(new Vec3d(this.posX + ox, this.posY + oy, this.posZ + oz)));

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

	@Override
	protected void arrowHit(EntityLivingBase living) {
		switch(this.getArrowType()) {
		case ANGLER_POISON:
			living.addPotionEffect(new PotionEffect(MobEffects.POISON, 200, 2));
			break;
		case OCTINE:
			if(living.isBurning()) {
				living.setFire(9);
			} else {
				living.setFire(5);
			}
			break;
		case BASILISK:
			if(living instanceof EntityChiromawMatriarch)
				break;
			if(living.isNonBoss()) {
				living.addPotionEffect(ElixirEffectRegistry.EFFECT_PETRIFY.createEffect(100, 1));
			} else {
				living.addPotionEffect(ElixirEffectRegistry.EFFECT_PETRIFY.createEffect(40, 1));
			}
			break;
		case WORM:
			if (!getEntityWorld().isRemote) {
				EntityTinySludgeWormHelper worm = new EntityTinySludgeWormHelper(getEntityWorld());
				worm.setLocationAndAngles(this.posX, this.posY, this.posZ, this.rotationYaw, this.rotationPitch);
				worm.setAttackTarget(living);
				if(this.shootingEntity instanceof EntityPlayer) {
					worm.setOwnerId(this.shootingEntity.getUniqueID());
				}
				getEntityWorld().spawnEntity(worm);
				this.setDead();
			}
			break;
		case SHOCK:
			if(!this.world.isRemote) {
				this.world.spawnEntity(new EntityShock(this.world, this, living, 0, this.isWet() || this.isInWater() || this.world.isRainingAt(this.getPosition().up())));
			}
			break;
		case CHIROMAW_BARB:
			if(living instanceof EntityChiromawMatriarch)
				break;
			if(living.isNonBoss()) {
				living.addPotionEffect(ElixirEffectRegistry.EFFECT_PETRIFY.createEffect(40, 1));
			} 
			break;
		case CHIROMAW_SHOCK_BARB:
			if(!this.world.isRemote) {
				this.world.spawnEntity(new EntityShock(this.world, this, living, 0, this.isWet() || this.isInWater() || this.world.isRainingAt(this.getPosition().up())));
			}
			if(living instanceof EntityChiromawMatriarch)
				break;
			if(living.isNonBoss()) {
				living.addPotionEffect(ElixirEffectRegistry.EFFECT_PETRIFY.createEffect(40, 1));
			} 
			break;
		default:
		}
	}

	@Override
	protected void onHit(RayTraceResult raytrace) {
		super.onHit(raytrace);

		if(raytrace.entityHit == null && raytrace.getBlockPos() != null && raytrace.sideHit != null && this.getArrowType() == EnumArrowType.OCTINE) {
			BlockPos pos = raytrace.getBlockPos().offset(raytrace.sideHit);
			IBlockState state = this.world.getBlockState(pos);

			if(ItemRegistry.OCTINE_INGOT.isTinder(new ItemStack(ItemRegistry.OCTINE_INGOT), ItemStack.EMPTY, state)) {
				this.world.setBlockState(pos, Blocks.FIRE.getDefaultState());
			}
		}
	}

	@Override
	public void playSound(SoundEvent soundIn, float volume, float pitch) {
		if (!this.isSilent()) {
			if(getArrowType() == EnumArrowType.CHIROMAW_BARB || getArrowType() == EnumArrowType.CHIROMAW_SHOCK_BARB) {
				if(soundIn == SoundEvents.ENTITY_ARROW_HIT)
					soundIn = SoundRegistry.CHIROMAW_MATRIARCH_BARB_HIT;
			}	
		}
		super.playSound(soundIn, volume, pitch);
	}

	/**
	 * Sets the arrow type
	 * @param type
	 */
	public void setType(EnumArrowType type) {
		this.dataManager.set(DW_TYPE, type.getName());
	}

	/**
	 * Returns the arrow type
	 * @return
	 */
	public EnumArrowType getArrowType(){
		return EnumArrowType.getEnumFromString(this.dataManager.get(DW_TYPE));
	}

	@Override
	protected ItemStack getArrowStack() {
		switch(this.getArrowType()) {
		case ANGLER_POISON:
			return new ItemStack(ItemRegistry.POISONED_ANGLER_TOOTH_ARROW);
		case OCTINE:
			return new ItemStack(ItemRegistry.OCTINE_ARROW);
		case BASILISK:
			return new ItemStack(ItemRegistry.BASILISK_ARROW);
		case WORM:
			return new ItemStack(ItemRegistry.SLUDGE_WORM_ARROW);
		case SHOCK:
			return new ItemStack(ItemRegistry.SHOCK_ARROW);
		case CHIROMAW_BARB:
			return new ItemStack(ItemRegistry.CHIROMAW_BARB);
		case DEFAULT:
			return new ItemStack(ItemRegistry.ANGLER_TOOTH_ARROW);
		default:
			return ItemStack.EMPTY;
		}
	}
}
