package com.hbm.entity.projectile;

import com.hbm.items.weapon.sedna.BulletConfig;
import com.hbm.items.weapon.sedna.factory.XFactoryTool;
import com.hbm.util.BobMathUtil;
import com.hbm.util.TrackerUtil;
import com.hbm.util.Vec3NT;

import cpw.mods.fml.common.registry.IEntityAdditionalSpawnData;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import io.netty.buffer.ByteBuf;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.EntityTrackerEntry;
import net.minecraft.util.MathHelper;
import net.minecraft.util.MovingObjectPosition;
import net.minecraft.util.Vec3;
import net.minecraft.world.World;
import net.minecraft.world.WorldServer;

public class EntityBulletBaseMK4 extends EntityThrowableInterp implements IEntityAdditionalSpawnData {
	
	public BulletConfig config;
	//used for rendering tracers
	public double velocity;
	public double prevVelocity;
	public double accel;
	public float damage;
	public int ricochets = 0;
	public Entity lockonTarget = null;

	public EntityBulletBaseMK4(World world) {
		super(world);
		this.renderDistanceWeight = 10.0D;
		this.setSize(0.5F, 0.5F);
		this.isImmuneToFire = true;
	}
	
	/** For submunitions! */
	public EntityBulletBaseMK4(World world, EntityLivingBase entity, BulletConfig config, float damage, float gunSpread, double posX, double posY, double posZ, double motionX, double motionY, double motionZ) {
		this(world);
		
		this.thrower = entity;
		this.setBulletConfig(config);
		
		this.damage = damage;
		
		this.setLocationAndAngles(posX, posY, posZ, 0, 0);
		this.setPosition(this.posX, this.posY, this.posZ);
		
		this.motionX = motionX;
		this.motionY = motionY;
		this.motionZ = motionZ;
		
		this.setThrowableHeading(this.motionX, this.motionY, this.motionZ, 1.0F, this.config.spread + gunSpread);
	}
	
	/** For standard guns */
	public EntityBulletBaseMK4(EntityLivingBase entity, BulletConfig config, float baseDamage, float gunSpread, double sideOffset, double heightOffset, double frontOffset) {
		this(entity.worldObj);
		
		this.thrower = entity;
		this.setBulletConfig(config);
		
		this.damage = baseDamage * this.config.damageMult;
		
		this.setLocationAndAngles(thrower.posX, thrower.posY + thrower.getEyeHeight(), thrower.posZ, thrower.rotationYaw, thrower.rotationPitch);
		
		Vec3 offset = Vec3.createVectorHelper(sideOffset, heightOffset, frontOffset);
		offset.rotateAroundX(-this.rotationPitch / 180F * (float) Math.PI);
		offset.rotateAroundY(-this.rotationYaw / 180F * (float) Math.PI);

		this.posX += offset.xCoord;
		this.posY += offset.yCoord;
		this.posZ += offset.zCoord;
		
		this.setPosition(this.posX, this.posY, this.posZ);
		
		this.motionX = -MathHelper.sin(this.rotationYaw / 180.0F * (float) Math.PI) * MathHelper.cos(this.rotationPitch / 180.0F * (float) Math.PI);
		this.motionZ = MathHelper.cos(this.rotationYaw / 180.0F * (float) Math.PI) * MathHelper.cos(this.rotationPitch / 180.0F * (float) Math.PI);
		this.motionY = (-MathHelper.sin(this.rotationPitch / 180.0F * (float) Math.PI));
		
		this.setThrowableHeading(this.motionX, this.motionY, this.motionZ, 1.0F, gunSpread);
	}
	
	/** For turrets - angles are in radians, and pitch is negative! */
	public EntityBulletBaseMK4(World world, BulletConfig config, float baseDamage, float gunSpread, float yaw, float pitch) {
		this(world);
		
		this.setBulletConfig(config);
		this.damage = baseDamage * this.config.damageMult;
		
		this.prevRotationYaw = this.rotationYaw = yaw * 180F / (float) Math.PI;
		this.prevRotationPitch = this.rotationPitch = -pitch * 180F / (float) Math.PI;
		
		this.motionX = -MathHelper.sin(this.rotationYaw / 180.0F * (float) Math.PI) * MathHelper.cos(this.rotationPitch / 180.0F * (float) Math.PI);
		this.motionZ = MathHelper.cos(this.rotationYaw / 180.0F * (float) Math.PI) * MathHelper.cos(this.rotationPitch / 180.0F * (float) Math.PI);
		this.motionY = (-MathHelper.sin(this.rotationPitch / 180.0F * (float) Math.PI));
		this.setThrowableHeading(this.motionX, this.motionY, this.motionZ, 1.0F, gunSpread);
	}

	@Override
	protected void entityInit() {
		super.entityInit();
		this.dataWatcher.addObject(3, Integer.valueOf(0));
	}
	
	public void setBulletConfig(BulletConfig config) {
		this.config = config;
		this.dataWatcher.updateObject(3, config.id);
	}
	
	public BulletConfig getBulletConfig() {
		int id = this.dataWatcher.getWatchableObjectInt(3);
		if(id < 0 || id > BulletConfig.configs.size()) return null;
		return BulletConfig.configs.get(id);
	}
	
	@Override
	public void onUpdate() {
		
		if(config == null) config = this.getBulletConfig();

		if(config == null){
			this.setDead();
			return;
		}

		this.prevPosX = this.posX;
		this.prevPosY = this.posY;
		this.prevPosZ = this.posZ;
		
		super.onUpdate();

		double dX = this.posX - this.prevPosX;
		double dY = this.posY - this.prevPosY;
		double dZ = this.posZ - this.prevPosZ;
		
		if(!this.inGround && this.lockonTarget != null && !this.lockonTarget.isDead) {
			Vec3NT motion = new Vec3NT(motionX, motionY, motionZ);
			double vel = motion.lengthVector();
			Vec3NT delta = new Vec3NT(lockonTarget.posX - posX, lockonTarget.posY + lockonTarget.height / 2D - posY, lockonTarget.posZ - posZ);
			float turn = Math.min(0.005F * this.ticksExisted, 1F);
			Vec3NT newVec = new Vec3NT(
					BobMathUtil.interp(motion.xCoord, delta.xCoord, turn),
					BobMathUtil.interp(motion.yCoord, delta.yCoord, turn),
					BobMathUtil.interp(motion.zCoord, delta.zCoord, turn)).normalizeSelf().multiply(vel);
			this.motionX = newVec.xCoord;
			this.motionY = newVec.yCoord;
			this.motionZ = newVec.zCoord;
			EntityTrackerEntry entry = TrackerUtil.getTrackerEntry((WorldServer) worldObj, this.getEntityId());
			entry.lastYaw = MathHelper.floor_float(this.rotationYaw * 256.0F / 360.0F) + 10; //force-trigger rotation update
		}
		
		this.prevVelocity = this.velocity;
		this.velocity = Math.sqrt(dX * dX + dY * dY + dZ * dZ);
		
		// shitty hack
		if((this.config != XFactoryTool.ct_hook || !worldObj.isRemote) && !this.inGround && !this.onGround && velocity > 0) {
			
			float hyp = MathHelper.sqrt_double(dX * dX + dZ * dZ);
			this.rotationYaw = (float) (Math.atan2(dX, dZ) * 180.0D / Math.PI);
	
			for(this.rotationPitch = (float) (Math.atan2(dY, (double) hyp) * 180.0D / Math.PI); this.rotationPitch - this.prevRotationPitch < -180.0F; this.prevRotationPitch -= 360.0F);
	
			while(this.rotationPitch - this.prevRotationPitch >= 180.0F) this.prevRotationPitch += 360.0F;
			while(this.rotationYaw - this.prevRotationYaw < -180.0F) this.prevRotationYaw -= 360.0F;
			while(this.rotationYaw - this.prevRotationYaw >= 180.0F) this.prevRotationYaw += 360.0F;
		}
		
		if(!worldObj.isRemote && this.ticksExisted > config.expires) this.setDead();
		
		if(this.config.onUpdate != null) this.config.onUpdate.accept(this);
	}
	
	@Override
	public void setDead() {
		super.setDead();
		
		//send a teleport on collision so that the bullets are forced to move even if their lifetime is only 1 tick, letting them render
		if(worldObj instanceof WorldServer) TrackerUtil.sendTeleport((WorldServer) worldObj, this);
	}

	
	@Override
	protected void onImpact(MovingObjectPosition mop) {
		if(!worldObj.isRemote) {
			
			if(this.config.onImpact != null) this.config.onImpact.accept(this, mop);
			if(this.isDead || this.inGround) return;
			if(this.config.onRicochet != null) this.config.onRicochet.accept(this, mop);
			if(this.config.onEntityHit != null) this.config.onEntityHit.accept(this, mop);
		}
	}

	@Override protected double headingForceMult() { return 1D; }
	@Override public double getGravityVelocity() { return this.config.gravity; }
	@Override protected double motionMult() { return this.config.velocity + this.accel; }
	@Override protected float getAirDrag() { return 1F; }
	@Override protected float getWaterDrag() { return 1F; }
	
	@Override public boolean doesImpactEntities() { return this.config.impactsEntities; }
	@Override public boolean doesPenetrate() { return this.config.doesPenetrate; }
	@Override public boolean isSpectral() { return this.config.isSpectral; }
	@Override public int selfDamageDelay() { return this.config.selfDamageDelay; }
	
	@Override @SideOnly(Side.CLIENT) public boolean canRenderOnFire() { return false; }

	@Override public void writeSpawnData(ByteBuf buf) {
		buf.writeInt(this.thrower != null ? thrower.getEntityId() : -1);
	}
	@Override public void readSpawnData(ByteBuf buf) {
		Entity e = worldObj.getEntityByID(buf.readInt());
		if(e instanceof EntityLivingBase) this.thrower = (EntityLivingBase) e;
	}
}
