package common.tileentity;

import common.color.TextColor;
import common.entity.npc.EntityNPC;
import common.inventory.Container;
import common.inventory.ContainerMachine;
import common.inventory.InventoryPlayer;
import common.item.ItemStack;
import common.nbt.NBTTagCompound;
import common.nbt.NBTTagList;
import common.network.Packet;
import common.packet.S35PacketUpdateTileEntity;
import common.rng.Random;
import common.util.ExtMath;

public abstract class TileEntityMachine extends TileEntityLockable implements IHopper, ITickable {
	public static enum Status {
		OFF(TextColor.DGRAY, "Inaktiv"), 
		COOLING(TextColor.YELLOW, "Abkühlen ..."), 
		RUNNING(TextColor.GREEN, "Aktiv"), 
		OVERHEAT(TextColor.RED, "Überhitzt!"), 
		BROKEN(TextColor.BLACK, "Defekt!");
		
		public final TextColor color;
		public final String name;
		
		private Status(TextColor color, String name) {
			this.color = color;
			this.name = name;
		}
	}
	
	protected final ItemStack[] inventory;
	protected final MachineResource[] resources;
	protected final Random rand = new Random();
//	protected boolean isCreative;
	protected int temperature;
	protected Status status = Status.OFF;

	protected TileEntityMachine(int slots, MachineResource ... resources) {
		this.inventory = new ItemStack[slots];
		this.resources = resources;
	}

	protected abstract int getTempIncrement();
	protected abstract int getTempDecrement();
	protected abstract int getMaxTemp();
	protected abstract boolean executeFunction();
	
	public MachineResource getResource(int slot) {
		return this.resources[slot];
	}

//	public void setCreative(boolean creative) {
//		this.isCreative = creative;
//	}
//
//	public boolean isCreative() {
//		return this.isCreative;
//	}
	
	public int getNumResources() {
		return this.resources.length;
	}

	public int getTemperature() {
		return this.temperature;
	}

	public void setTemperature(int temp) {
		this.temperature = temp;
	}

	public Status getStatus() {
		return this.status;
	}

	public void setStatus(Status status) {
		this.status = status;
	}

	public void readFromNBT(NBTTagCompound compound) {
		super.readFromNBT(compound);
		
		NBTTagList nbttaglist = compound.getTagList("Items", 10);
		this.clear();
		for(int i = 0; i < nbttaglist.tagCount(); ++i) {
			NBTTagCompound nbttagcompound = nbttaglist.getCompoundTagAt(i);
			int j = nbttagcompound.getByte("Slot");

			if(j >= 0 && j < this.inventory.length) {
				this.inventory[j] = ItemStack.loadItemStackFromNBT(nbttagcompound);
			}
		}
		
		nbttaglist = compound.getTagList("Resources", 10);
		for(MachineResource res : this.resources) {
			res.reset();
		}
		for(int i = 0; i < nbttaglist.tagCount() && i < this.inventory.length; ++i) {
			this.resources[i].readFromNbt(nbttaglist.getCompoundTagAt(i));
		}

//		this.isCreative = compound.getBoolean("Creative");
		this.temperature = compound.getInteger("Temperature");
		this.status = Status.values()[(int)compound.getByte("Status") % Status.values().length];
	}

	public void writeToNBT(NBTTagCompound compound) {
		super.writeToNBT(compound);
		
		NBTTagList nbttaglist = new NBTTagList();
		for(int i = 0; i < this.inventory.length; ++i) {
			if(this.inventory[i] != null) {
				NBTTagCompound nbttagcompound = new NBTTagCompound();
				nbttagcompound.setByte("Slot", (byte)i);
				this.inventory[i].writeToNBT(nbttagcompound);
				nbttaglist.appendTag(nbttagcompound);
			}
		}
		compound.setTag("Items", nbttaglist);
		
		nbttaglist = new NBTTagList();
		for(int z = 0; z < this.resources.length; z++) {
			NBTTagCompound res = new NBTTagCompound();
			this.resources[z].writeToNbt(res);
			nbttaglist.appendTag(res);
		}
		compound.setTag("Resources", nbttaglist);

//		compound.setBoolean("Creative", this.isCreative);
		compound.setInteger("Temperature", this.temperature);
		compound.setByte("Status", (byte)this.status.ordinal());
	}

//    public void markDirty()
//    {
//        super.markDirty();
//    }

	public int getSizeInventory() {
		return this.inventory.length;
	}

	public ItemStack getStackInSlot(int index) {
		return this.inventory[index];
	}

	public ItemStack decrStackSize(int index, int count) {
		if(this.inventory[index] != null) {
			if(this.inventory[index].stackSize <= count) {
				ItemStack itemstack1 = this.inventory[index];
				this.inventory[index] = null;
				return itemstack1;
			}
			else {
				ItemStack itemstack = this.inventory[index].splitStack(count);

				if(this.inventory[index].stackSize == 0) {
					this.inventory[index] = null;
				}

				return itemstack;
			}
		}
		else {
			return null;
		}
	}

	public ItemStack removeStackFromSlot(int index) {
		if(this.inventory[index] != null) {
			ItemStack itemstack = this.inventory[index];
			this.inventory[index] = null;
			return itemstack;
		}
		else {
			return null;
		}
	}

	public void setInventorySlotContents(int index, ItemStack stack) {
		this.inventory[index] = stack;

		if(stack != null && stack.stackSize > this.getInventoryStackLimit()) {
			stack.stackSize = this.getInventoryStackLimit();
		}
	}

	public boolean hasCustomName() {
		return false;
	}

	public int getInventoryStackLimit() {
		return ItemStack.MAX_SIZE;
	}

	public boolean isUseableByPlayer(EntityNPC player) {
		return this.worldObj.getTileEntity(this.pos) != this ? false
				: player.getDistanceSq((double)this.pos.getX() + 0.5D, (double)this.pos.getY() + 0.5D, (double)this.pos.getZ() + 0.5D) <= 64.0D;
	}

	public void openInventory(EntityNPC player) {
	}

	public void closeInventory(EntityNPC player) {
	}

	public boolean isItemValidForSlot(int index, ItemStack stack) {
		return true;
	}

	public void detonate() {
		this.worldObj.newExplosion(null, this.getXPos(), this.getYPos(), this.getZPos(), 5.0f, true, true, false);
	}

	public void update() {
		if(this.worldObj != null && !this.worldObj.client && this.status != Status.BROKEN) {
			int envTemp = (int)this.worldObj.getTemperatureC(this.getPos());
			if(this.executeFunction()) {
				this.temperature += this.getTempIncrement();
				this.status = this.temperature >= (this.getMaxTemp() * 9) / 10 ? Status.OVERHEAT : Status.RUNNING;
				if(this.temperature > this.getMaxTemp()) {
					this.status = Status.BROKEN;
					this.markDirty();
					this.detonate();
					return;
				}
				else {
					this.markDirty();
				}
			}
			else {
				int dec = this.getTempDecrement();
				if(dec != 0) {
					int prev = this.temperature;
					this.temperature -= dec;
					this.temperature = ExtMath.clampi(this.temperature, envTemp, Integer.MAX_VALUE);
					if(prev != this.temperature)
						this.markDirty();
					else
						dec = 0;
				}
				Status prev = this.status;
				this.status = this.temperature >= (this.getMaxTemp() * 9) / 10 ? Status.OVERHEAT : 
					(this.temperature == envTemp ? Status.OFF : Status.COOLING);
				if(dec == 0 && prev != this.status) {
					this.markDirty();
				}
			}
			if(this.temperature < envTemp) {
				this.temperature = envTemp;
				this.markDirty();
			}
		}
	}

	public boolean isEmpty(int slot) {
		if(slot == -1) {
			for(ItemStack itemstack : this.inventory) {
				if(itemstack != null)
					return false;
			}
			return true;
		}
		else {
			return this.inventory[slot] == null || this.inventory[slot].stackSize <= 0;
		}
	}

	public boolean hasAmount(int slot, int amount) {
		if(slot == -1) {
			int n = 0;
			for(ItemStack itemstack : this.inventory) {
				if(itemstack != null && itemstack.stackSize >= 1)
					n += itemstack.stackSize;
			}
			return n >= amount;
		}
		else {
			return this.inventory[slot] != null && this.inventory[slot].stackSize >= amount;
		}
	}

	public boolean isFull() {
		for(ItemStack itemstack : this.inventory) {
			if(itemstack == null || itemstack.stackSize != itemstack.getMaxStackSize()) {
				return false;
			}
		}
		return true;
	}

	public double getXPos() {
		return (double)this.pos.getX() + 0.5D;
	}

	public double getYPos() {
		return (double)this.pos.getY() + 0.5D;
	}

	public double getZPos() {
		return (double)this.pos.getZ() + 0.5D;
	}

	public Container createContainer(InventoryPlayer playerInventory, EntityNPC playerIn) {
		return new ContainerMachine(playerInventory, this, this, playerIn);
	}

	public int getField(int id) {
		return 0;
	}

	public void setField(int id, int value) {
	}

	public int getFieldCount() {
		return 0;
	}

	public void clear() {
		for(int i = 0; i < this.inventory.length; ++i) {
			this.inventory[i] = null;
		}
	}
	
	public Packet getDescriptionPacket()
    {
        return new S35PacketUpdateTileEntity(this);
    }
    
    public int getColor() {
    	return 0x8080ff;
    }
	
	public abstract String formatDisplay();
}
