/**
 * Copyright (c) 2011-2017, SpaceToad and the BuildCraft Team
 * http://www.mod-buildcraft.com
 *
 * The BuildCraft API is distributed under the terms of the MIT License.
 * Please check the contents of the license, which should be located
 * as "LICENSE.API" in the BuildCraft source code distribution.
 */
package buildcraft.api.blueprints;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;

import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityList;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagDouble;
import net.minecraft.nbt.NBTTagFloat;
import net.minecraft.nbt.NBTTagList;
import net.minecraftforge.common.util.Constants;

import buildcraft.api.core.Position;

public class SchematicEntity extends Schematic {
	public Class<? extends Entity> entity;

	/**
	 * This tree contains additional data to be stored in the blueprint. By
	 * default, it will be initialized from Schematic.readFromWord with the
	 * standard readNBT function of the corresponding tile (if any) and will be
	 * loaded from BptBlock.writeToWorld using the standard writeNBT function.
	 */
	public NBTTagCompound entityNBT = new NBTTagCompound();

	/**
	 * This field contains requirements for a given block when stored in the
	 * blueprint. Modders can either rely on this list or compute their own int
	 * Schematic.
	 */
	public ItemStack[] storedRequirements = new ItemStack[0];
	public BuildingPermission defaultPermission = BuildingPermission.ALL;

	@Override
	public void getRequirementsForPlacement(IBuilderContext context, LinkedList<ItemStack> requirements) {
		Collections.addAll(requirements, storedRequirements);
	}

	public void writeToWorld(IBuilderContext context) {
		Entity e = EntityList.createEntityFromNBT(entityNBT, context.world());
		context.world().spawnEntityInWorld(e);
	}

	public void readFromWorld(IBuilderContext context, Entity entity) {
		entity.writeToNBTOptional(entityNBT);
	}

	@Override
	public void translateToBlueprint(Translation transform) {
		NBTTagList nbttaglist = entityNBT.getTagList("Pos", 6);
		Position pos = new Position(nbttaglist.func_150309_d(0),
				nbttaglist.func_150309_d(1), nbttaglist.func_150309_d(2));
		pos = transform.translate(pos);

		entityNBT.setTag("Pos",
				this.newDoubleNBTList(pos.x, pos.y, pos.z));
	}

	@Override
	public void translateToWorld(Translation transform) {
		NBTTagList nbttaglist = entityNBT.getTagList("Pos", 6);
		Position pos = new Position(nbttaglist.func_150309_d(0),
				nbttaglist.func_150309_d(1), nbttaglist.func_150309_d(2));
		pos = transform.translate(pos);

		entityNBT.setTag("Pos",
				this.newDoubleNBTList(pos.x, pos.y, pos.z));
	}

	@Override
	public void idsToBlueprint(MappingRegistry registry) {
		registry.scanAndTranslateStacksToRegistry(entityNBT);
	}

	@Override
	public void idsToWorld(MappingRegistry registry) {
		try {
			registry.scanAndTranslateStacksToWorld(entityNBT);
		} catch (MappingNotFoundException e) {
			entityNBT = new NBTTagCompound();
		}
	}

	@Override
	public void rotateLeft(IBuilderContext context) {
		NBTTagList nbttaglist = entityNBT.getTagList("Pos", 6);
		Position pos = new Position(nbttaglist.func_150309_d(0),
				nbttaglist.func_150309_d(1), nbttaglist.func_150309_d(2));
		pos = context.rotatePositionLeft(pos);
		entityNBT.setTag("Pos",
				this.newDoubleNBTList(pos.x, pos.y, pos.z));

		nbttaglist = entityNBT.getTagList("Rotation", 5);
		float yaw = nbttaglist.func_150308_e(0);
		yaw += 90;
		entityNBT.setTag(
				"Rotation",
				this.newFloatNBTList(yaw,
						nbttaglist.func_150308_e(1)));
	}

	@Override
	public void writeSchematicToNBT(NBTTagCompound nbt, MappingRegistry registry) {
		super.writeSchematicToNBT(nbt, registry);

		nbt.setInteger("entityId", registry.getIdForEntity(entity));
		nbt.setTag("entity", entityNBT);

		NBTTagList rq = new NBTTagList();

		for (ItemStack stack : storedRequirements) {
			NBTTagCompound sub = new NBTTagCompound();
			stack.writeToNBT(stack.writeToNBT(sub));
			sub.setInteger("id", registry.getIdForItem(stack.getItem()));
			rq.appendTag(sub);
		}

		nbt.setTag("rq", rq);
	}

	@Override
	public void readSchematicFromNBT(NBTTagCompound nbt, MappingRegistry registry) {
		super.readSchematicFromNBT(nbt, registry);

		entityNBT = nbt.getCompoundTag("entity");

		NBTTagList rq = nbt.getTagList("rq",
				Constants.NBT.TAG_COMPOUND);

		ArrayList<ItemStack> rqs = new ArrayList<ItemStack>();

		for (int i = 0; i < rq.tagCount(); ++i) {
			try {
				NBTTagCompound sub = rq.getCompoundTagAt(i);

				if (sub.getInteger("id") >= 0) {
					// Maps the id in the blueprint to the id in the world
					sub.setInteger("id", Item.itemRegistry
							.getIDForObject(registry.getItemForId(sub
									.getInteger("id"))));

					rqs.add(ItemStack.loadItemStackFromNBT(sub));
				} else {
					defaultPermission = BuildingPermission.CREATIVE_ONLY;
				}
			} catch (Throwable t) {
				t.printStackTrace();
				defaultPermission = BuildingPermission.CREATIVE_ONLY;
			}
		}

		storedRequirements = rqs.toArray(new ItemStack[rqs.size()]);
	}

	protected NBTTagList newDoubleNBTList(double... par1ArrayOfDouble) {
		NBTTagList nbttaglist = new NBTTagList();
		double[] adouble = par1ArrayOfDouble;
		int i = par1ArrayOfDouble.length;

		for (int j = 0; j < i; ++j) {
			double d1 = adouble[j];
			nbttaglist.appendTag(new NBTTagDouble(d1));
		}

		return nbttaglist;
	}

	protected NBTTagList newFloatNBTList(float... par1ArrayOfFloat) {
		NBTTagList nbttaglist = new NBTTagList();
		float[] afloat = par1ArrayOfFloat;
		int i = par1ArrayOfFloat.length;

		for (int j = 0; j < i; ++j) {
			float f1 = afloat[j];
			nbttaglist.appendTag(new NBTTagFloat(f1));
		}

		return nbttaglist;
	}

	public boolean isAlreadyBuilt(IBuilderContext context) {
		NBTTagList nbttaglist = entityNBT.getTagList("Pos", 6);
		Position newPosition = new Position(nbttaglist.func_150309_d(0),
				nbttaglist.func_150309_d(1), nbttaglist.func_150309_d(2));

		for (Object o : context.world().loadedEntityList) {
			Entity e = (Entity) o;

			Position existingPositon = new Position(e.posX, e.posY, e.posZ);

			if (existingPositon.isClose(newPosition, 0.1F)) {
				return true;
			}
		}

		return false;
	}

	@Override
	public int buildTime() {
		return 5;
	}

	@Override
	public BuildingPermission getBuildingPermission() {
		return defaultPermission;
	}
}
