package twilightforest.entity;

import java.util.List;

import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.Entity;
import net.minecraft.init.SoundEvents;
import net.minecraft.item.EnumDyeColor;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.math.Vec3d;
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
import twilightforest.TFPacketHandler;
import twilightforest.block.BlockTFCastleMagic;
import twilightforest.block.TFBlocks;
import twilightforest.item.ItemTFCubeOfAnnihilation;
import net.minecraftforge.fml.common.network.NetworkRegistry;
import net.minecraft.block.Block;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.projectile.EntityThrowable;
import net.minecraft.init.Blocks;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.DamageSource;
import net.minecraft.world.World;
import twilightforest.network.PacketAnnihilateBlock;


public class EntityTFCubeOfAnnihilation extends EntityThrowable  {
	
	private boolean hasHitObstacle = false;

	public EntityTFCubeOfAnnihilation(World par1World) {
		super(par1World);
		this.setSize(1F, 1F);
		this.isImmuneToFire = true;
	}

	public EntityTFCubeOfAnnihilation(World world, EntityLivingBase thrower) {
		super(world, thrower);
		this.setSize(1F, 1F);
		this.isImmuneToFire = true;
		this.setHeadingFromThrower(thrower, thrower.rotationPitch, thrower.rotationYaw, 0F, 1.5F, 1F);
	}

	@Override
    protected float getGravityVelocity()
    {
        return 0F;
    }

	@Override
	protected void onImpact(RayTraceResult mop) {
		if (world.isRemote)
			return;

		// only hit living things
        if (mop.entityHit != null && mop.entityHit instanceof EntityLivingBase
				&& mop.entityHit.attackEntityFrom(DamageSource.causePlayerDamage((EntityPlayer) this.getThrower()), 10))
        {
			this.ticksExisted += 60;
		}
        
        if (mop.getBlockPos() != null && !this.world.isAirBlock(mop.getBlockPos())) {
			this.affectBlocksInAABB(this.getEntityBoundingBox().expand(0.2F, 0.2F, 0.2F));
        }
	}
	
    private void affectBlocksInAABB(AxisAlignedBB par1AxisAlignedBB) {
        int minX = MathHelper.floor(par1AxisAlignedBB.minX);
        int minY = MathHelper.floor(par1AxisAlignedBB.minY);
        int minZ = MathHelper.floor(par1AxisAlignedBB.minZ);
        int maxX = MathHelper.floor(par1AxisAlignedBB.maxX);
        int maxY = MathHelper.floor(par1AxisAlignedBB.maxY);
        int maxZ = MathHelper.floor(par1AxisAlignedBB.maxZ);
        for (int dx = minX; dx <= maxX; ++dx) {
            for (int dy = minY; dy <= maxY; ++dy) {
                for (int dz = minZ; dz <= maxZ; ++dz) {
					BlockPos pos = new BlockPos(dx, dy, dz);
					IBlockState state = world.getBlockState(pos);

                	if (state.getBlock() != Blocks.AIR) {
                		if (canAnnihilate(pos, state)) {
                			this.world.setBlockToAir(pos);
                			this.playSound(SoundEvents.ENTITY_GENERIC_EXTINGUISH_FIRE, 0.125f, this.rand.nextFloat() * 0.25F + 0.75F);
                    		this.sendAnnihilateBlockPacket(world, pos);
                		} else {
                			this.hasHitObstacle = true;
                		}
                	}
                }
            }
        }
    }


	private boolean canAnnihilate(BlockPos pos, IBlockState state) {
		// whitelist many castle blocks
		Block block = state.getBlock();
		if (block == TFBlocks.deadrock || block == TFBlocks.castleBlock || (block == TFBlocks.castleMagic && state.getValue(BlockTFCastleMagic.COLOR) != EnumDyeColor.PURPLE) || block == TFBlocks.forceField || block == TFBlocks.thorns) {
			return true;
		}
		
		return block.getExplosionResistance(this) < 8F && state.getBlockHardness(world, pos) >= 0;
	}

    

	private void sendAnnihilateBlockPacket(World world, BlockPos pos) {
		// send packet
		IMessage message = new PacketAnnihilateBlock(pos);

		NetworkRegistry.TargetPoint targetPoint = new NetworkRegistry.TargetPoint(world.provider.getDimension(), pos.getX(), pos.getY(), pos.getZ(), 64);
		
		TFPacketHandler.CHANNEL.sendToAllAround(message, targetPoint);
	}
	
    @Override
    public void onUpdate() {
    	super.onUpdate();

    	if (!this.world.isRemote) {
    		if (this.getThrower() == null) {
    			this.setDead();
    			return;
    		}

			// always head towards either the point or towards the player
			Vec3d destPoint = new Vec3d(this.getThrower().posX, this.getThrower().posY + this.getThrower().getEyeHeight(), this.getThrower().posZ);

    		if (this.isReturning()) {
    			// if we are returning, and are near enough to the player, then we are done
    			List<EntityLivingBase> list = this.world.getEntitiesWithinAABB(EntityLivingBase.class, this.getEntityBoundingBox().addCoord(this.motionX, this.motionY, this.motionZ).expand(1.0D, 1.0D, 1.0D));

    			if (list.contains(this.getThrower())) {
    				this.setDead();
    			}
    		} else {
				destPoint = destPoint.add(getThrower().getLookVec().scale(16F));
    		}
    		
    		// set motions
    		Vec3d velocity = new Vec3d(this.posX - destPoint.xCoord, (this.posY + this.height / 2F) - destPoint.yCoord, this.posZ - destPoint.zCoord);
    		
    		this.motionX -= velocity.xCoord;
    		this.motionY -= velocity.yCoord;
    		this.motionZ -= velocity.zCoord;
    		
    		// normalize speed
    		float currentSpeed = MathHelper.sqrt(this.motionX * this.motionX + this.motionY * this.motionY + this.motionZ * this.motionZ);
    		
    		float maxSpeed = 0.5F;
    		
    		
    		if (currentSpeed > maxSpeed) {
	    		this.motionX /= currentSpeed / maxSpeed;
	    		this.motionY /= currentSpeed / maxSpeed;
	    		this.motionZ /= currentSpeed / maxSpeed;
    		} else {
    			float slow = 0.5F;
	    		this.motionX *= slow;
	    		this.motionY *= slow;
	    		this.motionZ *= slow;    		
	    	}
    		
        	// demolish some blocks
        	this.affectBlocksInAABB(this.getEntityBoundingBox().expand(0.2F, 0.2F, 0.2F));
    	}
    }
    
    private boolean isReturning() {
    	if (this.hasHitObstacle || this.getThrower() == null || !(this.getThrower() instanceof EntityPlayer)) {
    		return true;
    	} else {
    		EntityPlayer player = (EntityPlayer) this.getThrower();
    		return !player.isHandActive();
    	}
    }
}
