/**
 * Copyright (c) 2011-2014, SpaceToad and the BuildCraft Team
 * http://www.mod-buildcraft.com
 *
 * BuildCraft is distributed under the terms of the Minecraft Mod Public
 * License 1.0, or MMPL. Please check the contents of the license located in
 * http://www.mod-buildcraft.com/MMPL-1.0.txt
 */
package buildcraft.core;

import io.netty.buffer.ByteBuf;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import net.minecraft.entity.Entity;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.world.World;
import buildcraft.api.blueprints.BptSlotInfo;
import buildcraft.api.core.Position;
import buildcraft.core.blueprints.BptBuilderBase;
import buildcraft.core.blueprints.BptContext;
import buildcraft.core.blueprints.BptSlot;
import buildcraft.core.blueprints.BptSlot.Mode;
import buildcraft.core.proxy.CoreProxy;
import buildcraft.core.utils.BCLog;
import buildcraft.core.utils.BlockUtil;

import com.google.common.io.ByteArrayDataInput;
import com.google.common.io.ByteArrayDataOutput;

import cpw.mods.fml.common.registry.IEntityAdditionalSpawnData;

public class EntityRobot extends Entity implements IEntityAdditionalSpawnData {

	private Box box;
	private int destX, destY, destZ;

	EntityEnergyLaser laser;

	public LinkedList<Action> targets = new LinkedList<Action>();
	public static int MAX_TARGETS = 20;
	public int wait = 0;

	private class Action {

		public Action(BptSlot slot, BptContext context) {
			this.slot = slot;
			this.context = context;
		}

		public Action(BptBuilderBase builder) {
			this.builder = builder;
		}

		BptSlot slot;
		BptBuilderBase builder;
		BptContext context;
	}

	public EntityRobot(World world) {
		super(world);
	}

	public EntityRobot(World world, Box box) {

		super(world);

		this.box = box;
		init();
	}

	protected void init() {

		destX = (int) box.centerX();
		destY = (int) box.centerY();
		destZ = (int) box.centerZ();

		motionX = 0;
		motionY = 0;
		motionZ = 0;

		setLocationAndAngles(destX, destY, destZ, 0, 0);

		laser = new EntityEnergyLaser(worldObj, new Position(posX, posY, posZ), new Position(posX, posY, posZ));
		worldObj.spawnEntityInWorld(laser);
	}

	@Override
	public void writeSpawnData(ByteBuf data) {

		if (box == null) {
			box = new Box();
		}
		
		data.writeInt(box.xMin);
		data.writeInt(box.yMin);
		data.writeInt(box.zMin);
		data.writeInt(box.xMax);
		data.writeInt(box.yMax);
		data.writeInt(box.zMax);
	}

	@Override
	public void readSpawnData(ByteBuf data) {

		box = new Box();
		box.xMin = data.readInt();
		box.yMin = data.readInt();
		box.zMin = data.readInt();
		box.xMax = data.readInt();
		box.yMax = data.readInt();
		box.zMax = data.readInt();

		init();
	}

	@Override
	protected void entityInit() {
	}

	@Override
	protected void readEntityFromNBT(NBTTagCompound nbttagcompound) {
	}

	@Override
	protected void writeEntityToNBT(NBTTagCompound nbttagcompound) {
	}

	@Override
	public void onUpdate() {

		move();
		build();
		updateLaser();
	}

	protected void move() {

		setPosition(posX + motionX, posY + motionY, posZ + motionZ);

		if (reachedDesination()) {

			BlockIndex newDesination = getNewDestination();
			if (newDesination != null) {
				setDestination(newDesination.x, newDesination.y, newDesination.z);
			}

		}

	}

	protected BlockIndex getNewDestination() {

		Box movementBoundary = new Box();
		movementBoundary.initialize(box);
		movementBoundary.expand(1);

		Box moveArea = new Box();
		moveArea.initialize(destX, destY, destZ, 1);

		List<BlockIndex> potentialDestinations = new ArrayList<BlockIndex>();
		for (BlockIndex blockIndex : moveArea.getBlocksInArea()) {

			if (BlockUtil.isSoftBlock(worldObj, blockIndex.x, blockIndex.y, blockIndex.z) && movementBoundary.contains(blockIndex)) {
				potentialDestinations.add(blockIndex);
			}
		}

		if (!potentialDestinations.isEmpty()) {

			int i = worldObj.rand.nextInt(potentialDestinations.size());
			return potentialDestinations.get(i);
		}

		return null;
	}

	protected void setDestination(int x, int y, int z) {

		destX = x;
		destY = y;
		destZ = z;

		motionX = (destX - posX) / 75 * (laser.getPowerAverage() / 2 + 1);
		motionY = (destY - posY) / 75 * (laser.getPowerAverage() / 2 + 1);
		motionZ = (destZ - posZ) / 75 * (laser.getPowerAverage() / 2 + 1);
	}

	protected boolean reachedDesination() {

		if (getDistance(destX, destY, destZ) <= .2)
			return true;

		return false;
	}

	protected void build() {
		updateWait();

		if (targets.size() > 0) {
			Action a = targets.getFirst();
			
			if (a.slot != null) {
				BptSlot target = a.slot;
				//System.out.printf("RobotChanging %d %d %d %s\n",target.x, target.y, target.z, target.mode);
				
				if (wait <= 0 && BlockUtil.canChangeBlock(worldObj, target.x, target.y, target.z)) {
					if (!worldObj.isRemote) {
						if (target.mode == Mode.ClearIfInvalid) {
							if (!target.isValid(a.context)) {
								worldObj.setBlockToAir(target.x, target.y, target.z);
							}
						} else if (target.stackToUse != null) {
							worldObj.setBlockToAir(target.x, target.y, target.z);
							throw new RuntimeException("NOT IMPLEMENTED");
							// target.stackToUse.getItem().onItemUse(target.stackToUse,
							// CoreProxy.getBuildCraftPlayer(worldObj), worldObj, target.x, target.y - 1,
							// target.z, 1);
						} else {
							try {
								target.buildBlock(a.context);
							} catch (Throwable t) {
								// Defensive code against errors in implementers
								t.printStackTrace();
								BCLog.logger.throwing("EntityRobot", "update", t);
							}
						}
					}

					targets.pop();
				}
			} else if (a.builder != null) {
				a.builder.postProcessing(worldObj);
				targets.pop();
			}
		}
	}

	public void updateWait() {

		if (targets.size() > 0)
			if (wait == 0) {
				wait = MAX_TARGETS - targets.size() + 2;
			} else {
				wait--;
			}
	}

	private void updateLaser() {

		if (laser == null)
			return;

		if (targets.size() > 0) {

			Action a = targets.getFirst();
			BptSlotInfo target = a.slot;

			if (target != null) {

				laser.setPositions(new Position(posX, posY, posZ), new Position(target.x + 0.5, target.y + 0.5, target.z + 0.5));
				laser.show();
			}
		} else {
			laser.hide();
		}

		laser.pushPower(((float) targets.size()) / ((float) MAX_TARGETS) * 4F);
	}

	public void scheduleContruction(BptSlot slot, BptContext context) {
		if (slot != null) {
			targets.add(new Action(slot, context));
		}
	}

	public void markEndOfBlueprint(BptBuilderBase builder) {
		targets.add(new Action(builder));
	}

	public boolean readyToBuild() {
		return targets.size() < MAX_TARGETS;
	}

	public boolean done() {
		return targets.isEmpty();
	}

	public void setBox(Box box) {

		this.box = box;
		setDestination((int) box.centerX(), (int) box.centerY(), (int) box.centerZ());
	}

	@Override
	public void setDead() {

		if (laser != null) {
			laser.setDead();
		}

		super.setDead();
	}

}
