/**
 * @file inv_gui.c
 * 
 * Handles interactions with the inventory through the GUI, and draws it to the screen.
 */

#include "inv_gui.h"

#include "Player.h"
#include "World.h"

static float controllerMouseX, controllerMouseY;
static float cmvx = 0.0f, cmvy = 0.0f;

static Bmp_t texBackground;
static Bmp_t texPlusButton;
static Bmp_t texCompareIcons;
static Bmp_t texCursor;

void Inv_InitGfx(const RPKFile *rpak)
{
	texBackground = Bmp_CreateFromPAKEntry(rpak, "Inventory");
	texPlusButton = Bmp_CreateFromPAKEntry(rpak, "PlusIcon");
	texCompareIcons = Bmp_CreateFromPAKEntry(rpak, "CompareIcons");
	texCursor = Bmp_CreateFromPAKEntry(rpak, "Cursor");

	controllerMouseX = 0;
	controllerMouseY = 0;
}

void Inv_CleanupGfx(void)
{
	Bmp_Destroy(&texBackground);
	Bmp_Destroy(&texPlusButton);
	Bmp_Destroy(&texCompareIcons);
}

#define INVGFX_SLOT_SIZE 16

int invOffsX, invOffsY;

#define INVGFX_HANDOFFS_X 5
#define INVGFX_HANDOFFS_Y 41
#define INVGFX_INVOFFS_X 4
#define INVGFX_INVOFFS_Y 87
/* Offset of stats panel */
#define INVGFX_STATSOFFS_X 138
#define INVGFX_STATSOFFS_Y 33
/* Offset of stats upgrade buttons */
#define INVGFX_STATBUTTON_X (INVGFX_STATSOFFS_X + 28)
#define INVGFX_STATBUTTON_Y (INVGFX_STATSOFFS_Y + 21)

int Inv_Click(inventory_t *inv, int mouseX, int mouseY)
{
	int xx, yy;
	int i;
	bool mouseHovered;

	player_t *player = &World_Players[0];

	/* Check level up buttons */
	if (player->statPoints > 0)
	{
		for (i = 0; i < 5; i++)
		{
			xx = invOffsX + INVGFX_STATBUTTON_X;
			yy = invOffsY + INVGFX_STATBUTTON_Y + i * 7;
			mouseHovered = (mouseX >= xx && mouseX < xx + 7) && (mouseY >= yy && mouseY < yy + 7);
			if (mouseHovered)
				return INV_INTERACT_UPGRADESTAT_STR + i;
		}
	}

	/* Drop item if clicked outside inventory */
	bool outsideInventory =
		(mouseX < invOffsX || mouseX >(texBackground.w + invOffsX)) ||
		(mouseY < invOffsY || mouseY >(texBackground.h + invOffsY));
	if (outsideInventory)
		return INV_INTERACT_DROP;

	/* Check for click inside hand or belt slots */
	for (i = 0; i < 5; i++)
	{
		int w, h;
		int slot = INV_HAND_SLOT + i;
		if (i >= 2)
		{
			w = 16;
			h = 16;
			xx = INVGFX_HANDOFFS_X + invOffsX + 68 + (i - 2) * w;
			yy = INVGFX_HANDOFFS_Y + invOffsY + 17;
		}
		else
		{
			w = 32;
			h = 32;
			xx = INVGFX_HANDOFFS_X + invOffsX + i * 34;
			yy = INVGFX_HANDOFFS_Y + invOffsY;
		}
		mouseHovered = (mouseX >= xx && mouseX <= xx + w) && (mouseY >= yy && mouseY <= yy + h);
		if (mouseHovered)
		{
			return INV_INTERACT_SLOT + slot;
		}
	}

	// Check for click in inventory
	if (inv->isDraggingItem)
	{
		// If an item is being dragged, the inventory slot that is clicked is offset by the dragged item's size so that the chosen slot is at the top left corner of the item.
		int hoveredX = (mouseX - (ItemTypes[inv->dragItem.type].w * INVGFX_SLOT_SIZE) / 2) - (invOffsX + INVGFX_INVOFFS_X);
		int hoveredY = (mouseY - (ItemTypes[inv->dragItem.type].h * INVGFX_SLOT_SIZE) / 2) - (invOffsY + INVGFX_INVOFFS_Y);
		
		// Quick check if click is outside inventory bounds
		if ((mouseX < (invOffsX + INVGFX_INVOFFS_X)) || (mouseY < (invOffsY + INVGFX_INVOFFS_Y)))
			return INV_INTERACT_NONE;
		
		int xs = ((hoveredX + INVGFX_SLOT_SIZE / 2) / INVGFX_SLOT_SIZE);
		int ys = ((hoveredY + INVGFX_SLOT_SIZE / 2) / INVGFX_SLOT_SIZE);
		if (xs < 0 || xs >= INV_GRID_WIDTH || ys < 0 || ys >= INV_GRID_HEIGHT)
			return INV_INTERACT_NONE;

		return INV_INTERACT_SLOT + (xs + ys * INV_GRID_WIDTH);
	}
	else
	{
		// If no item is being dragged, simply find the grid 
		int hoveredX = mouseX - invOffsX - INVGFX_INVOFFS_X;
		int hoveredY = mouseY - invOffsY - INVGFX_INVOFFS_Y;
		int xs = hoveredX / INVGFX_SLOT_SIZE;
		int ys = hoveredY / INVGFX_SLOT_SIZE;
		if (xs < 0 || xs >= INV_GRID_WIDTH || ys < 0 || ys >= INV_GRID_HEIGHT)
			return INV_INTERACT_NONE;
		return INV_INTERACT_SLOT + (xs + ys * INV_GRID_WIDTH);
	}

	return INV_INTERACT_NONE;
}

void Inv_Interact(inventory_t *inv, player_t *player, int interact)
{
	switch (interact)
	{
	case INV_INTERACT_NONE:
		return;
	case INV_INTERACT_DROP:
		if (inv->isDraggingItem)
		{
			World_DropItem(inv->dragItem, player->entity->x, player->entity->y, 0.6f, player->entity->rot);
			inv->isDraggingItem = false;
		}
		break;
	case INV_INTERACT_UPGRADESTAT_STR:
	case INV_INTERACT_UPGRADESTAT_SPD:
	case INV_INTERACT_UPGRADESTAT_DEF:
	case INV_INTERACT_UPGRADESTAT_VIT:
	case INV_INTERACT_UPGRADESTAT_INT:
		if (player->statPoints == 0)
			break;
		Player_UpgradeStat(player, interact - INV_INTERACT_UPGRADESTAT_STR);
		break;
	default:
		break;
	}

	if (interact < INV_INTERACT_SLOT || interact >= INV_INTERACT_UPGRADESTAT_STR)
		return;

	// Handle interaction with an item slot

	int slot = interact;
	if (inv->isDraggingItem)
	{
		// Try to swap with whichever item is at that slot
		Item_t *handItem = Inv_GetItem(inv, slot, NULL);
		if (handItem != NULL)
		{
			if (Item_SpecialUseOn(&inv->dragItem, handItem))
			{
				inv->dragItem.remainingUses--;
				if (inv->dragItem.remainingUses == 0)
					inv->isDraggingItem = false; /* Delete item */
			}
			else
			{
				Item_t tmp = inv->dragItem;
				inv->dragItem = *handItem;
				Inv_ClearItem(inv, slot);
				Inv_PutItem(inv, tmp, slot);
			}
		}
		else
		{
			if (Inv_PutItem(inv, inv->dragItem, slot))
				inv->isDraggingItem = false;
		}
	}
	else
	{
		// Pick up the item at the slot if one exists there
		Item_t *slotItem = Inv_GetItem(inv, slot, NULL);
		if (slotItem != NULL)
		{
			inv->dragItem = *slotItem;
			Inv_ClearItem(inv, slot);
			inv->isDraggingItem = true;
		}
	}
}

int Inv_ControllerClick(inventory_t *inv)
{
	return Inv_Click(inv, (int)controllerMouseX, (int)controllerMouseY);
}

void Inv_HandleControllerAxis(int axis, float value)
{
	if (axis == 0)
		cmvx = value * 2.0f;
	else
		cmvy = value * 2.0f;
}

void Inv_DrawItem(Bmp_t *screen, const Item_t *item, int x, int y)
{
	struct ItemType *type = &ItemTypes[item->type];
	Bmp_Blit(screen, &type->invSprite, x, y);

	if (type->maxUses > 1)
	{
		int durh = (int)(INVGFX_SLOT_SIZE * ((float)item->remainingUses / (float)type->maxUses));
		Bmp_FillRegion(screen, 0xffff0000, x, y + INVGFX_SLOT_SIZE * (type->h - 1), 2, INVGFX_SLOT_SIZE);
		Bmp_FillRegion(screen, 0xff7fff7f, x, y + INVGFX_SLOT_SIZE * (type->h - 1), 2, durh);
	}
}

#define INVGFX_TOOLTIP_WIDTH 128

static const char *itemstatnames[] = { NULL, "SPEED", "RANGE", "DMAGE", "BLOCK" };

static void DrawItemTooltip(Bmp_t *screen, inventory_t *inv, const Item_t *item, int x, int y)
{
	static char statsmsg[24];
	struct ItemType *type = &ItemTypes[item->type];

	int descW, descH;
	int statsH;
	int totalHeight = 0;
	int yoffs = y;
	int i;

	Bmp_SetActiveFont(BMPFONT_MINISERIF);
	Bmp_TextSize(type->desc, &descW, &descH, INVGFX_TOOLTIP_WIDTH);
	statsH = 26;

	totalHeight = 12 + descH + 8 + statsH;

	Bmp_FillRegion(screen, 0xdf000000, x - 4, y - 4, INVGFX_TOOLTIP_WIDTH + 4, totalHeight + 4);

	Bmp_SetActiveFont(BMPFONT_MINISERIF);
	Bmp_DrawText(screen, type->title, 0xff402000, x + 1, yoffs + 1, INVGFX_TOOLTIP_WIDTH);
	Bmp_DrawText(screen, type->title, 0xffff7f00, x, yoffs, INVGFX_TOOLTIP_WIDTH);
	yoffs += 12;

	Bmp_DrawText(screen, type->desc, 0xff7f7f7f, x, yoffs, INVGFX_TOOLTIP_WIDTH);
	yoffs += descH + 8;

	Bmp_SetActiveFont(BMPFONT_TINY);
	for (i = 0; i < 3; i++)
	{
		const struct ItemStat *stat = &item->stats[i];
		if (stat->type == ISTAT_NONE)
			continue;
		SDL_snprintf(statsmsg, 24, "%s: %.1f", itemstatnames[stat->type], stat->value);
		if (inv->slots[World_Players[0].handSlot] != SLOT_EMPTY && item != &inv->items[World_Players[0].handSlot])
		{
			float a = stat->value;
			float b = inv->items[World_Players[0].handSlot].stats[i].value;
			/* Lower speed value is better */
			if (stat->type == ISTAT_SPEED)
			{
				a = inv->items[World_Players[0].handSlot].stats[i].value;
				b = stat->value;
			}
			if (a > b)
				Bmp_BlitRegion(screen, &texCompareIcons, x + 46, yoffs - 1, 0, 0, 8, 8);
			else if (a < b)
				Bmp_BlitRegion(screen, &texCompareIcons, x + 46, yoffs - 1, 16, 0, 8, 8);
			else
				Bmp_BlitRegion(screen, &texCompareIcons, x + 46, yoffs - 1, 8, 0, 8, 8);
		}
		Bmp_DrawText(screen, statsmsg, 0xffb0b0b0, x, yoffs, INVGFX_TOOLTIP_WIDTH);
		yoffs += 8;
	}

}

static const char *statnames[] = { "Might", "Fortitude", "Agility", "Vitality", "Intellect" };
static const int statcolours[] = { 0xffff8000, 0xffff000b, 0xffcb4bf2, 0xff8eff00, 0xff2585f9 }; // coloUr eh!
static const char *statdesc[] = {
	"Increases the damage directly done towards enemies.",
	"Allows you to partially absorb damage dealt towards you.",
	"Increases the speed with which you can move or attack.",
	"Augments your health bar with additional life force.",
	"Increases your ability to comprehend the dark arts."
};

static void DrawStatTooltip(Bmp_t *screen, int statIndex, int x, int y)
{
	int descW, descH;
	int statsH;
	int totalHeight = 0;
	int yoffs = y;

	Bmp_TextSize(" ", &descW, &descH, INVGFX_TOOLTIP_WIDTH); // This sucks
	statsH = 26;
	totalHeight = 12 + descH + 8 + statsH;

	Bmp_FillRegion(screen, 0xdf000000, x + 5, y - 49, INVGFX_TOOLTIP_WIDTH + 4, totalHeight + 4);
	Bmp_SetActiveFont(BMPFONT_MINISERIF);
	Bmp_DrawText(screen, statnames[statIndex], (statcolours[statIndex] & 0xfefefe) >> 1, x + 9, yoffs - 45, INVGFX_TOOLTIP_WIDTH);
	Bmp_DrawText(screen, statnames[statIndex], statcolours[statIndex], x + 8, yoffs - 46, INVGFX_TOOLTIP_WIDTH);
	yoffs -= 34;
	Bmp_SetActiveFont(BMPFONT_TINY);
	Bmp_DrawText(screen, statdesc[statIndex], 0xff7f7f7f, x + 8, yoffs, INVGFX_TOOLTIP_WIDTH);
}

static char playerStatsMsg[64];

static void DrawHandOrBeltSlot(Bmp_t *screen, inventory_t *inv, int slot, int x, int y, int w, int h, bool hovered, Item_t **hoveredItem)
{
	if (hovered)
		Bmp_FillRegion(screen, 0x7fffffff, x, y, w, h);

	Item_t *handItem = Inv_GetItem(inv, slot, NULL);
	if (handItem != NULL)
	{
		int xc = x + (w - ItemTypes[handItem->type].w * 16) / 2;
		int yc = y + (h - ItemTypes[handItem->type].h * 16) / 2;
		Inv_DrawItem(screen, handItem, xc, yc);

		if (hovered && hoveredItem)
			*hoveredItem = handItem;
	}
}

void Inv_Render(Bmp_t *screen, inventory_t *inv, int mouseX, int mouseY)
{
	Item_t *hoveredItem = NULL;
	int xx, yy;
	int i, x, y;

	if (Game.input.mode == INPUTMODE_CONTROLLER)
	{
		mouseX = (int)controllerMouseX;
		mouseY = (int)controllerMouseY;
	}

	invOffsX = (screen->w - texBackground.w) / 2;
	invOffsY = (screen->h - texBackground.h) / 2;

	Bmp_Blit(screen, &texBackground, invOffsX, invOffsY);

	for (y = 0; y < INV_GRID_HEIGHT; y++)
	{
		for (x = 0; x < INV_GRID_WIDTH; x++)
		{
			int itemSlot;
			int slot = x + y * INV_GRID_WIDTH;

			xx = x * INVGFX_SLOT_SIZE + invOffsX + INVGFX_INVOFFS_X;
			yy = y * INVGFX_SLOT_SIZE + invOffsY + INVGFX_INVOFFS_Y;

			const Item_t *item = Inv_GetItem(inv, slot, &itemSlot);
			if (item != NULL && slot == itemSlot)
			{
				Inv_DrawItem(screen, item, xx, yy);
			}

			//int feerg = 0x7f000000;
			//if (invflags[slot] & SLOT_CONTAINSITEM)
			//{
			//	feerg |= 0x0000ff00;
			//}
			//if (invflags[slot] & SLOT_ISOCCUPIED)
			//{
			//	feerg |= 0x000000ff;
			//}
			//Bmp_FillRegion(screen, feerg, xx, yy, INVGFX_SLOT_SIZE, INVGFX_SLOT_SIZE);

			bool isHovered = (mouseX >= xx && mouseX < xx + INVGFX_SLOT_SIZE) && (mouseY >= yy && mouseY < yy + INVGFX_SLOT_SIZE);
			if (isHovered)
			{
				if (inv->isDraggingItem)
				{
					int hoveredX = (mouseX - (ItemTypes[inv->dragItem.type].w * INVGFX_SLOT_SIZE) / 2) - (invOffsX + INVGFX_INVOFFS_X);
					int hoveredY = (mouseY - (ItemTypes[inv->dragItem.type].h * INVGFX_SLOT_SIZE) / 2) - (invOffsY + INVGFX_INVOFFS_Y);
					int xs = ((hoveredX + INVGFX_SLOT_SIZE / 2) / INVGFX_SLOT_SIZE);
					int ys = ((hoveredY + INVGFX_SLOT_SIZE / 2) / INVGFX_SLOT_SIZE);
					int xc = xs * INVGFX_SLOT_SIZE + (invOffsX + INVGFX_INVOFFS_X);
					int yc = ys * INVGFX_SLOT_SIZE + (invOffsY + INVGFX_INVOFFS_Y);
					bmcol_t fillcol = Inv_CanPut(inv, &inv->dragItem, xs + ys * INV_GRID_WIDTH) ? 0x7fffffff : 0x7fff0000;
					Bmp_FillRegion(screen, fillcol, xc, yc, ItemTypes[inv->dragItem.type].w * INVGFX_SLOT_SIZE, ItemTypes[inv->dragItem.type].h * INVGFX_SLOT_SIZE);
				}
				else if (item != NULL)
				{
					int px = (itemSlot % INV_GRID_WIDTH) * INVGFX_SLOT_SIZE + invOffsX + INVGFX_INVOFFS_X;
					int py = (itemSlot / INV_GRID_WIDTH) * INVGFX_SLOT_SIZE + invOffsY + INVGFX_INVOFFS_Y;
					Bmp_FillRegion(screen, 0x7fffffff, px, py, ItemTypes[item->type].w * INVGFX_SLOT_SIZE, ItemTypes[item->type].h * INVGFX_SLOT_SIZE);
					hoveredItem = item;
				}
				else
				{
					Bmp_FillRegion(screen, 0x7fffffff, xx, yy, INVGFX_SLOT_SIZE, INVGFX_SLOT_SIZE);
				}
			}
		}
	}

	xx = INVGFX_HANDOFFS_X + invOffsX;
	yy = INVGFX_HANDOFFS_Y + invOffsY;
	/* Draw hand and belt slots */
	for (i = 0; i < 2; i++)
	{
		bool isSlotHovered = (mouseX >= xx && mouseX < xx + 32) && (mouseY >= yy && mouseY < yy + 32);
		DrawHandOrBeltSlot(screen, inv, INV_HAND_SLOT + i, xx, yy, 32, 32, isSlotHovered, &hoveredItem);
		xx += 34;
	}
	xx -= 1;
	yy += 17;
	for (i = 0; i < 3; i++)
	{
		bool isSlotHovered = (mouseX >= xx && mouseX < xx + 16) && (mouseY >= yy && mouseY < yy + 16);
		DrawHandOrBeltSlot(screen, inv, INV_BELT_SLOT + i, xx, yy, 16, 16, isSlotHovered, &hoveredItem);
		xx += 16;
	}

	if (inv->isDraggingItem)
	{
		int xc = mouseX - (ItemTypes[inv->dragItem.type].w * 16) / 2;
		int yc = mouseY - (ItemTypes[inv->dragItem.type].h * 16) / 2;
		Inv_DrawItem(screen, &inv->dragItem, xc, yc);
	}

	int heightoffs = INVGFX_STATSOFFS_Y;
	SDL_snprintf(playerStatsMsg, 64, "Level %d", World_Players[0].level);
	Bmp_SetActiveFont(BMPFONT_MINISERIF);
	Bmp_DrawText(screen, playerStatsMsg, 0xffafafaf, invOffsX + INVGFX_STATSOFFS_X, invOffsY + heightoffs, screen->w);
	heightoffs += 10;
	Bmp_SetActiveFont(BMPFONT_TINY);
	SDL_snprintf(playerStatsMsg, 64, "%dXP to next", LEVELXPS[World_Players[0].level + 1] - World_Players[0].xp);
	Bmp_DrawText(screen, playerStatsMsg, 0xffafafaf, invOffsX + INVGFX_STATSOFFS_X, invOffsY + heightoffs, screen->w);
	heightoffs += 12;

	SDL_snprintf(playerStatsMsg, 64, "STR: %d\nDEF: %d\nSPD: %d\nVIT: %d\nINT: %d", World_Players[0].str, World_Players[0].def, World_Players[0].spd, World_Players[0].vit, World_Players[0].itl);
	Bmp_DrawText(screen, playerStatsMsg, 0xffafafaf, invOffsX + INVGFX_STATSOFFS_X + 1, invOffsY + heightoffs, screen->w);

	if (World_Players[0].statPoints > 0)
	{
		for (i = 0; i < 5; i++)
		{
			if (!Player_CanUpgradeStat(&World_Players[0], i))
				continue;
			Bmp_Blit(screen, &texPlusButton, invOffsX + INVGFX_STATBUTTON_X, invOffsY + INVGFX_STATBUTTON_Y + i * 7);

			// if is hovering then:
			xx = invOffsX + INVGFX_STATBUTTON_X;
			yy = invOffsY + INVGFX_STATBUTTON_Y + i * 7;
			bool mouseHovered = (mouseX >= xx && mouseX < xx + 7) && (mouseY >= yy && mouseY < yy + 7);
			if (mouseHovered)
				DrawStatTooltip(screen, i, xx + 4, yy + 24);
		}
	}
	heightoffs += 5 * 7 + 4;

	SDL_snprintf(playerStatsMsg, 64, "Points: %d", World_Players[0].statPoints);
	Bmp_DrawText(screen, playerStatsMsg, 0xffafafaf, invOffsX + INVGFX_STATSOFFS_X, invOffsY + heightoffs, screen->w);

	SDL_snprintf(playerStatsMsg, 64, "%s: %d", Game.globalReg[1] < 0 ? "Heresy" : "Faith", Math_IAbs(Game.globalReg[1]));
	Bmp_DrawText(screen, playerStatsMsg, 0xffafafaf, invOffsX + INVGFX_STATSOFFS_X, invOffsY + texBackground.h - 16, screen->w);

	if (hoveredItem != NULL)
	{
		int tx = mouseX + ItemTypes[hoveredItem->type].w + 6;
		int ty = mouseY + ItemTypes[hoveredItem->type].h + 6;
		DrawItemTooltip(screen, inv, hoveredItem, tx, ty);
	}

	if (Game.input.mode == INPUTMODE_CONTROLLER)
	{
		controllerMouseX += cmvx;
		controllerMouseY += cmvy;
		if (controllerMouseX < 0.0f) controllerMouseX = 0.0f;
		if (controllerMouseX > (float)screen->w) controllerMouseX = (float)screen->w;
		if (controllerMouseY < 0.0f) controllerMouseY = 0.0f;
		if (controllerMouseY > (float)screen->h) controllerMouseY = (float)screen->h;
		Bmp_Blit(screen, &texCursor, (int)controllerMouseX, (int)controllerMouseY);
	}
}

void Inv_OnClose(inventory_t *inv)
{
	if (inv->isDraggingItem)
	{
		if (!Inv_AutoPlace(inv, inv->dragItem))
		{
			World_DropItem(inv->dragItem, World_Players[0].entity->x, World_Players[0].entity->y, 0.6f, World_Players[0].entity->rot);
		}
		inv->isDraggingItem = false;
	}
}
