package de.rico.adventure.inventory;

import java.util.LinkedList;

import javax.media.opengl.GL;

import de.rico.adventure.entity.item.Item;
import de.rico.engine.effect.ortho.ImageRenderer;
import de.rico.engine.effect.ortho.RectangleRenderer;
import de.rico.engine.effect.ortho.TextRenderer;
import de.rico.engine.input.InputManager;
import de.rico.engine.material.Color4f;

/**
 * Abstract base class for different kinds of inventory systems.
 * @author Frank Bruns
 *
 */
public abstract class AbstractInventory
{
	private LinkedList<Item> items = null;
	private LinkedList<Item> itemsToAdd = null;
	private LinkedList<Item> itemsToRemove = null;
	private Item selectedItem = null;
	private boolean isVisible = true;

	private int x = 0;
	private int y = 0;
	private int width = 0;
	private int height = 0;

	private Color4f bgColor = new Color4f(0f,0f,0f,1f);
	private Color4f textColor = new Color4f(1f,1f,1f,1f);

	protected RectangleRenderer rectRenderer = null;
	protected TextRenderer textRenderer = null;
	protected ImageRenderer imgRenderer = null;
	protected InputManager inputManager = null;

	/**
	 * Creates a new inventory system.
	 * @param input input manager object
	 */
	public AbstractInventory(InputManager input)
	{
		this.items = new LinkedList<Item>();
		this.itemsToAdd = new LinkedList<Item>();
		this.itemsToRemove = new LinkedList<Item>();

		this.inputManager = input;
		this.rectRenderer = new RectangleRenderer();
		this.textRenderer = new TextRenderer();
		this.imgRenderer = new ImageRenderer();
	}

	/**
	 * Returns the height of the inventory area.
	 * @return height of inventory area
	 */
	public int getHeight()
	{
		return this.height;
	}


	/**
	 * Sets the height of the inventory area
	 * @param height the height of the invemtory area
	 */
	public void setHeight(int height)
	{
		this.height = height;
	}


	/**
	 * Returns the width of the inventory area.
	 * @return width of the inventory area
	 */
	public int getWidth()
	{
		return this.width;
	}


	/**
	 * Sets the width of the inventory area
	 * @param width the width of the inventory area
	 */
	public void setWidth(int width)
	{
		this.width = width;
	}


	/**
	 * Returns the x coordinate of the inventory area.
	 * @return x-coordinate
	 */
	public int getX()
	{
		return this.x;
	}


	/**
	 * Sets the x-coordinate of the inventory area.
	 * @param x x-coordinate of the inventory area.
	 */
	public void setX(int x)
	{
		this.x = x;
	}


	/**
	 * Returns the y coordinate of the inventory area.
	 * @return y-coordinate of the inventory area
	 */
	public int getY()
	{
		return this.y;
	}


	/**
	 * Sets the y-coordinate of the inventory area.
	 * @param y y-coordinate of the inventory area
	 */
	public void setY(int y)
	{
		this.y = y;
	}

	/**
	 * Sets the visibility status of the inventory.
	 * @param visible set visible? --> true=yes, false=no
	 */
	public void setVisible(boolean visible)
	{
		this.isVisible = visible;
	}

	/**
	 * Determines whether or not the inventory is
	 * currently visible.
	 * @return is visible? --> true=yes, false=no
	 */
	public boolean isVisible()
	{
		return this.isVisible;
	}

	/**
	 * Helper method to figure out, if the mouse is located within
	 * the bounds of the inventory area.
	 * @return is mouse in inventory area bounds? --> true=yes, false=no
	 */
	public boolean isMouseInInventory()
	{
		int mouseX = this.inputManager.getMouseX();
		int mouseY = this.inputManager.getMouseY();

		if(mouseX >= this.x && mouseX <= this.x+this.width
				&& mouseY >= this.y && mouseY <= this.y+this.height)
		{
			return true;
		}
		else return false;
	}

	/**
	 * Determines whether or not the mouse is located over
	 * the specified inventory item.
	 * @param item item in the inventory to check
	 * @return is mouse over item in inventory? --> true=yes, false=no
	 */
	public abstract boolean isMouseOverItem(Item item);

	/**
	 * Returns the item that lies beneath the mouse cursor.
	 * @return item under mouse cursor
	 */
	public Item getItemUnderCursor()
	{
		Item item = null;

		for(Item i : this.getItems())
		{
			if(this.isMouseOverItem(i)) return i;
		}

		return item;
	}

	/**
	 * Sets the background color for the inventory area.
	 * @param color background color for the inventory
	 */
	public void setBGColor(Color4f color)
	{
		this.bgColor.setColor(color);
	}

	/**
	 * Returns the background color of the inventory area.
	 * @return background color of the inventory
	 */
	public Color4f getBGColor()
	{
		return this.bgColor;
	}

	/**
	 * Sets the text color for the inventory area.
	 * @param color text color for the inventory
	 */
	public void setTextColor(Color4f color)
	{
		this.textColor.setColor(color);
	}

	/**
	 * Returns the text color of the inventory area.
	 * @return text color of the inventory
	 */
	public Color4f getTextColor()
	{
		return this.textColor;
	}

	/**
	 * Returns a list of the items in the inventory
	 * @return list of inventory items
	 */
	public LinkedList<Item> getItems()
	{
		return this.items;
	}

	/**
	 * Adds an item to the inventory.
	 * @param item Item that shall be added to the inventory
	 */
	public void add(Item item)
	{
		// Set item to be added in the next update period.
		// This way concurrent modification exceptions can be avoided.
		this.itemsToAdd.add(item);
	}

	/**
	 * Removes an item from the inventory.
	 * @param item item to remove from the inventory
	 */
	public void remove(Item item)
	{
		// Set item to be removed in the next update period.
		// This way concurrent modification exceptions can be avoided.
		this.itemsToRemove.add(item);
	}

	/**
	 * Returns the currently selected inventory item.
	 * @return the currently selected inventory item, <code>null</code>
	 * if none is selected
	 */
	public Item getSelectedInventoryItem()
	{
		return this.selectedItem;
	}

	/**
	 * Determines whether the inventory contains the specified item
	 * @param item item to check for existence in the inventory
	 * @return inventory contains the item? --> true=yes, false=no
	 */
	public boolean contains(Item item)
	{
		if(this.items.contains(item)) return true;
		else return false;
	}

	/**
	 * Sets the currently selected inventory item to the specified item.
	 * Set it to null to express that no imventory item is selected
	 * @param item item to set to the selected inventory item
	 */
	public void setSelectedInventoryItem(Item item)
	{
		this.selectedItem = item;
	}

	/**
	 * This method updates the inventory.
	 * @param elapsedTime time between two frames
	 */
	public void update(long elapsedTime)
	{
		// remove items that have been set to be removed
		if(this.itemsToRemove.size() > 0)
		{
			this.items.removeAll(this.itemsToRemove);
			this.itemsToRemove.clear();
		}

		// add items that have been set to be added
		if(this.itemsToAdd.size() > 0)
		{
			this.items.addAll(this.itemsToAdd);
			this.itemsToAdd.clear();
		}
	}

	/**
	 * The method draws the inventory, when visible
	 * @param gl
	 */
	public abstract void draw(GL gl);
}
