package codechicken.wirelessredstone.addons;

import java.util.Iterator;
import java.util.List;
import java.util.TreeSet;

import net.minecraft.block.Block;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.MathHelper;
import net.minecraft.util.MovingObjectPosition;
import net.minecraft.util.Vec3;
import net.minecraft.world.World;

import codechicken.core.CommonUtils;
import codechicken.lib.vec.BlockCoord;
import codechicken.lib.vec.Vector3;
import codechicken.wirelessredstone.core.*;

public class EntityREP extends Entity
{
    public EntityREP(World world) {
        super(world);
        xTileREP = -1;
        yTileREP = -1;
        zTileREP = -1;
        setSize(0.25F, 0.25F);
    }

    protected void entityInit() {}

    public boolean isInRangeToRenderDist(double d) {
        return true;
    }

    public EntityREP(World world, EntityLivingBase entityliving) {
        super(world);
        xTileREP = -1;
        yTileREP = -1;
        zTileREP = -1;
        shootingEntity = entityliving;
        setSize(0.25F, 0.25F);
        setLocationAndAngles(entityliving.posX, entityliving.posY + entityliving.getEyeHeight(), entityliving.posZ, entityliving.rotationYaw, entityliving.rotationPitch);
        posX -= MathHelper.cos((rotationYaw / 180F) * 3.141593F) * 0.16F;
        posY -= 0.10000000149011612D;
        posZ -= MathHelper.sin((rotationYaw / 180F) * 3.141593F) * 0.16F;
        setPosition(posX, posY, posZ);
        float f = 0.4F;
        motionX = -MathHelper.sin((rotationYaw / 180F) * 3.141593F) * MathHelper.cos((rotationPitch / 180F) * 3.141593F) * f;
        motionZ = MathHelper.cos((rotationYaw / 180F) * 3.141593F) * MathHelper.cos((rotationPitch / 180F) * 3.141593F) * f;
        motionY = -MathHelper.sin((rotationPitch / 180F) * 3.141593F) * f;
        setREPHeading(motionX, motionY, motionZ, 1.5F, 1.0F);
    }

    public EntityREP(World world, double d, double d1, double d2) {
        super(world);
        xTileREP = -1;
        yTileREP = -1;
        zTileREP = -1;
        setSize(0.25F, 0.25F);
        setPosition(d, d1, d2);
    }

    public void setREPHeading(double d, double d1, double d2, float f,
                              float f1) {
        float f2 = MathHelper.sqrt_double(d * d + d1 * d1 + d2 * d2);
        d /= f2;
        d1 /= f2;
        d2 /= f2;
        d += rand.nextGaussian() * 0.0074999998323619366D * f1;
        d1 += rand.nextGaussian() * 0.0074999998323619366D * f1;
        d2 += rand.nextGaussian() * 0.0074999998323619366D * f1;
        d *= f;
        d1 *= f;
        d2 *= f;
        motionX = d;
        motionY = d1;
        motionZ = d2;
        float f3 = MathHelper.sqrt_double(d * d + d2 * d2);
        prevRotationYaw = rotationYaw = (float) ((Math.atan2(d, d2) * 180D) / 3.1415927410125732D);
        prevRotationPitch = rotationPitch = (float) ((Math.atan2(d1, f3) * 180D) / 3.1415927410125732D);
        ticksInGroundREP = 0;
    }

    public void setVelocity(double d, double d1, double d2) {
        motionX = d;
        motionY = d1;
        motionZ = d2;
        if (prevRotationPitch == 0.0F && prevRotationYaw == 0.0F) {
            float f = MathHelper.sqrt_double(d * d + d2 * d2);
            prevRotationYaw = rotationYaw = (float) ((Math.atan2(d, d2) * 180D) / 3.1415927410125732D);
            prevRotationPitch = rotationPitch = (float) ((Math.atan2(d1, f) * 180D) / 3.1415927410125732D);
        }
    }

    public void onUpdate() {
        lastTickPosX = posX;
        lastTickPosY = posY;
        lastTickPosZ = posZ;
        super.onUpdate();
        if (shakeREP > 0) {
            shakeREP--;
        }
        if (inGroundREP) {
            Block block = worldObj.getBlock(xTileREP, yTileREP, zTileREP);
            if (block != inTileREP) {
                inGroundREP = false;
                motionX *= rand.nextFloat() * 0.2F;
                motionY *= rand.nextFloat() * 0.2F;
                motionZ *= rand.nextFloat() * 0.2F;
                ticksInGroundREP = 0;
                ticksInAirREP = 0;
            } else {
                ticksInGroundREP++;
                if (ticksInGroundREP == 1200) {
                    setDead();
                }
                return;
            }
        } else {
            ticksInAirREP++;
        }
        Vec3 vec3d = Vec3.createVectorHelper(posX, posY, posZ);
        Vec3 vec3d1 = Vec3.createVectorHelper(posX + motionX, posY + motionY, posZ + motionZ);
        MovingObjectPosition movingobjectposition = worldObj.rayTraceBlocks(vec3d, vec3d1);
        vec3d = Vec3.createVectorHelper(posX, posY, posZ);
        vec3d1 = Vec3.createVectorHelper(posX + motionX, posY + motionY, posZ + motionZ);
        if (movingobjectposition != null)
            vec3d1 = Vec3.createVectorHelper(movingobjectposition.hitVec.xCoord, movingobjectposition.hitVec.yCoord, movingobjectposition.hitVec.zCoord);

        if (!worldObj.isRemote) {
            Entity entity = null;
            List<Entity> list = worldObj.getEntitiesWithinAABBExcludingEntity(this, boundingBox.addCoord(motionX, motionY, motionZ).expand(1.0D, 1.0D, 1.0D));
            double d = 0.0D;
            for (Entity entity1 : list) {
                if (!entity1.canBeCollidedWith() || entity1 == shootingEntity && ticksInAirREP < 5)
                    continue;

                float f4 = 0.3F;
                AxisAlignedBB axisalignedbb = entity1.boundingBox.expand(f4, f4, f4);
                MovingObjectPosition movingobjectposition1 = axisalignedbb.calculateIntercept(vec3d, vec3d1);
                if (movingobjectposition1 == null)
                    continue;

                double d1 = vec3d.distanceTo(movingobjectposition1.hitVec);
                if (d1 < d || d == 0.0D) {
                    entity = entity1;
                    d = d1;
                }
            }

            if (entity != null) {
                movingobjectposition = new MovingObjectPosition(entity);
            }
        }
        if (movingobjectposition != null) {
            detonate();
            setDead();
        }
        posX += motionX;
        posY += motionY;
        posZ += motionZ;
        float f = MathHelper.sqrt_double(motionX * motionX + motionZ * motionZ);
        rotationYaw = (float) ((Math.atan2(motionX, motionZ) * 180D) / 3.1415927410125732D);
        for (rotationPitch = (float) ((Math.atan2(motionY, f) * 180D) / 3.1415927410125732D); rotationPitch - prevRotationPitch < -180F; prevRotationPitch -= 360F) { }
        for (; rotationPitch - prevRotationPitch >= 180F; prevRotationPitch += 360F) { }
        for (; rotationYaw - prevRotationYaw < -180F; prevRotationYaw -= 360F) { }
        for (; rotationYaw - prevRotationYaw >= 180F; prevRotationYaw += 360F) { }
        rotationPitch = prevRotationPitch + (rotationPitch - prevRotationPitch) * 0.2F;
        rotationYaw = prevRotationYaw + (rotationYaw - prevRotationYaw) * 0.2F;
        float f1 = 0.99F;
        float f2 = 0.03F;
        if (isInWater()) {
            for (int k = 0; k < 4; k++) {
                float f3 = 0.25F;
                worldObj.spawnParticle("bubble", posX - motionX * f3, posY - motionY * f3, posZ - motionZ * f3, motionX, motionY, motionZ);
            }

            f1 = 0.8F;
        }
        motionX *= f1;
        motionY *= f1;
        motionZ *= f1;
        motionY -= f2;
        setPosition(posX, posY, posZ);
    }

    public void detonate() {
        if (worldObj.isRemote)
            return;

        int boltsgen = 0;
        List<Entity> entities = worldObj.getEntitiesWithinAABBExcludingEntity(this, AxisAlignedBB.getBoundingBox(posX - 10, posY - 10, posZ - 10, posX + 10, posY + 10, posZ + 10));
        for (Iterator<Entity> iterator = entities.iterator(); iterator.hasNext(); ) {
            if (boltsgen > maxbolts) {
                break;
            }
            Entity target = iterator.next();

            if (!(target instanceof EntityLivingBase) || Vector3.fromEntity(this).subtract(Vector3.fromEntity(target)).magSquared() > 100) {
                continue;
            }

            WirelessBolt bolt = new WirelessBolt(worldObj, Vector3.fromEntity(this), Vector3.fromEntity(target), worldObj.rand.nextLong());
            bolt.defaultFractal();
            bolt.finalizeBolt();
            bolt = new WirelessBolt(worldObj, Vector3.fromEntity(this), Vector3.fromEntity(target), worldObj.rand.nextLong());
            bolt.defaultFractal();
            bolt.finalizeBolt();
            boltsgen += 2;
        }

        TreeSet<BlockCoord> nodes = RedstoneEther.server().getNodesInRangeofPoint(CommonUtils.getDimension(worldObj), Vector3.fromEntity(this), RedstoneEther.jammerrange, true);
        for (Iterator<BlockCoord> iterator = nodes.iterator(); iterator.hasNext(); ) {
            if (boltsgen > maxbolts) {
                break;
            }
            BlockCoord node = iterator.next();
            ITileWireless tile = (ITileWireless) RedstoneEther.getTile(worldObj, node);

            WirelessBolt bolt = new WirelessBolt(worldObj, Vector3.fromEntity(this), tile, worldObj.rand.nextLong());
            bolt.defaultFractal();
            bolt.finalizeBolt();
            boltsgen++;
        }

        for (int i = 0; i < 16; i++) {
            if (boltsgen > maxbolts) {
                break;
            }
            WirelessBolt bolt = new WirelessBolt(worldObj, Vector3.fromEntity(this), new Vector3(
                    posX + 20 * worldObj.rand.nextFloat() - 10,
                    posY + 20 * worldObj.rand.nextFloat() - 10,
                    posZ + 20 * worldObj.rand.nextFloat() - 10), worldObj.rand.nextLong());
            bolt.defaultFractal();
            bolt.finalizeBolt();
            boltsgen++;
        }
    }

    @Override
    public void setDead() {
        super.setDead();
        RedstoneEtherAddons.get(worldObj.isRemote).invalidateREP((EntityPlayer) shootingEntity);
        if (!worldObj.isRemote)
            WRAddonSPH.sendKillREP(this);
    }

    public void writeEntityToNBT(NBTTagCompound tag) {
        tag.setShort("xTile", (short) xTileREP);
        tag.setShort("yTile", (short) yTileREP);
        tag.setShort("zTile", (short) zTileREP);
        tag.setShort("inTile", (short) Block.getIdFromBlock(inTileREP));
        tag.setByte("shake", (byte) shakeREP);
        tag.setByte("inGround", (byte) (inGroundREP ? 1 : 0));
    }

    public void readEntityFromNBT(NBTTagCompound tag) {
        xTileREP = tag.getShort("xTile");
        yTileREP = tag.getShort("yTile");
        zTileREP = tag.getShort("zTile");
        inTileREP = Block.getBlockById(tag.getShort("inTile") & 0xFFFF);
        shakeREP = tag.getByte("shake") & 0xff;
        inGroundREP = tag.getByte("inGround") == 1;
    }

    public float getShadowSize() {
        return 0.0F;
    }

    private int xTileREP;
    private int yTileREP;
    private int zTileREP;
    private Block inTileREP;
    private boolean inGroundREP;
    public int shakeREP;
    public EntityLivingBase shootingEntity;
    private int ticksInGroundREP;
    private int ticksInAirREP;

    public final int maxbolts = 50;
}