#include "Inventory.h"

#include <SDL.h>

#include "ExMath.h"
#include "Game.h"
#include "Player.h"
#include "World.h"

#define INBOUNDS(slot) ((slot) >= 0 && (slot) < INV_SIZE)

Item_t *Inv_GetItem(inventory_t *inv, int slot, int *itemSlot)
{
	int i;

	if (!INBOUNDS(slot) || inv->slots[slot] == SLOT_EMPTY)
		return NULL;
	
	if (inv->slots[slot] & SLOT_CONTAINSITEM)
	{
		if (itemSlot != NULL) *itemSlot = slot;
		return &inv->items[slot];
	}

	// There is an item using this slot but not stored here, we need to
	// find it.

	// Dumb and simple way to do this; iterate over all items and find the first
	// one that occupies the slot we are looking at.
	for (i = 0; i < INV_SIZE; i++)
	{
		const Item_t *item;
		int x, y;
		int ix, iy;

		if (!(inv->slots[i] & SLOT_CONTAINSITEM))
			continue;

		item = &inv->items[i];
			
		ix = (i % INV_GRID_WIDTH);
		iy = (i / INV_GRID_WIDTH);
		for (y = iy; y < iy + ItemTypes[item->type].h; y++)
		{
			if (y > INV_GRID_HEIGHT) continue;
			for (x = ix; x < ix + ItemTypes[item->type].w; x++)
			{
				if (x > INV_GRID_WIDTH) continue;
				if ((x + y * INV_GRID_WIDTH) == slot)
				{
					if (itemSlot != NULL) *itemSlot = i;
					return &inv->items[i];
				}
			}
		}
	}

	return NULL;
}

bool Inv_PutItem(inventory_t *inv, Item_t item, int slot)
{
	int x, y;

	if (!Inv_CanPut(inv, &item, slot))
		return false;

	/* Mark surrounding slots as occupied if this is a grid slot */
	if (slot < INV_HAND_SLOT)
	{
		int ix = (slot % INV_GRID_WIDTH);
		int iy = (slot / INV_GRID_WIDTH);

		for (y = iy; y < iy + ItemTypes[item.type].h; y++)
		{
			for (x = ix; x < ix + ItemTypes[item.type].w; x++)
			{
				inv->slots[x + y * INV_GRID_WIDTH] = SLOT_ISOCCUPIED;
			}
		}
	}

	inv->items[slot] = item;
	inv->slots[slot] = SLOT_CONTAINSITEM | SLOT_ISOCCUPIED;

	return true;
}

void Inv_ClearItem(inventory_t *inv, int slot)
{
	const Item_t *slotItem;
	int itemSlot, ix, iy;
	int x, y;

	if (!INBOUNDS(slot))
		return;
	
	if (slot >= INV_HAND_SLOT)
	{
		inv->slots[slot] = SLOT_EMPTY;
		return;
	}

	// This function should still clear an item even if the given slot isn't
	// the one where the item data is stored, so we use this function to get
	// the item's actual slot index and iterate from there.
	slotItem = Inv_GetItem(inv, slot, &itemSlot);
	if (slotItem == NULL) return;

	ix = (itemSlot % INV_GRID_WIDTH);
	iy = (itemSlot / INV_GRID_WIDTH);
	for (y = iy; y < iy + ItemTypes[slotItem->type].h; y++)
	{
		for (x = ix; x < ix + ItemTypes[slotItem->type].w; x++)
		{
			inv->slots[x + y * INV_GRID_WIDTH] = SLOT_EMPTY;
		}
	}
}

void Inv_Clear(inventory_t *inv)
{
	int i;
	for (i = 0; i < INV_SIZE; i++)
	{
		inv->slots[i] = SLOT_EMPTY;
	}
}

bool Inv_AutoPlace(inventory_t *inv, Item_t item)
{
	int i;

	// Try to place in the hand/belt slots first
	for (i = INV_HAND_SLOT; i < INV_SIZE; i++)
	{
		if (Inv_CanPut(inv, &item, i))
		{
			Inv_PutItem(inv, item, i);
			return true;
		}
	}

	for (i = 0; i < INV_GRID_SIZE; i++)
	{
		if (Inv_CanPut(inv, &item, i))
		{
			Inv_PutItem(inv, item, i);
			return true;
		}
	}
	return false;
}

bool Inv_CanPut(inventory_t *inv, const Item_t *item, int slot)
{
	int x, y;

	if (!INBOUNDS(slot))
		return false;

	/* Hand slots accept items of any size for now. */
	if (slot == INV_HAND_SLOT && inv->slots[INV_HAND_SLOT] == SLOT_EMPTY)
		return true;
	if (slot == INV_HAND_SLOT2 && inv->slots[INV_HAND_SLOT2] == SLOT_EMPTY)
		return true;
	/* Belt slots accept any 1x1 item */
	if (slot >= INV_BELT_SLOT)
		return inv->slots[slot] == SLOT_EMPTY && ItemTypes[item->type].w == 1 && ItemTypes[item->type].h == 1;

	int ix = (slot % INV_GRID_WIDTH);
	int iy = (slot / INV_GRID_WIDTH);

	for (y = iy; y < iy + ItemTypes[item->type].h; y++)
	{
		if (y >= INV_GRID_HEIGHT) return false;
		for (x = ix; x < ix + ItemTypes[item->type].w; x++)
		{
			if (x >= INV_GRID_WIDTH) return false;
			if (inv->slots[x + y * INV_GRID_WIDTH] & SLOT_ISOCCUPIED)
				return false;
		}
	}

	return true;
}

