#include "Item.h"

#include "ExMath.h"
#include "Game.h"
#include "Log.h"
#include "Player.h"
#include "Render.h"

struct ItemType *ItemTypes;
static int numItems = 0;

static struct _ItemNameAssoc
{
	char name[256];
	int entityId;
} *itemNames;

void Items_Init(const RPKFile *rpk)
{
	SDL_RWops *in;
	size_t dataSize;
	int i;
	void *data;
	
	dataSize = RPK_GetEntryData(rpk, "items", &data);
	in = SDL_RWFromMem(data, dataSize);

	in->read(in, &numItems, 1, 2);
	ItemTypes = SDL_calloc(numItems, sizeof(struct ItemType));
	
	for (i = 0; i < numItems; i++)
	{
		byte stringLength;
		in->read(in, &stringLength, 1, 1);
		ItemTypes[i].title = SDL_malloc(stringLength + 1);
		in->read(in, ItemTypes[i].title, 1, stringLength);
		ItemTypes[i].title[stringLength] = '\0';

		in->read(in, &stringLength, 1, 1);
		ItemTypes[i].desc = SDL_malloc(stringLength + 1);
		in->read(in, ItemTypes[i].desc, 1, stringLength);
		ItemTypes[i].desc[stringLength] = '\0';

		in->read(in, &ItemTypes[i].w, 1, 1);
		in->read(in, &ItemTypes[i].h, 1, 1);
		in->read(in, &ItemTypes[i].initialData, 1, 2);
		in->read(in, &ItemTypes[i].autoFire, 1, 1);
		in->read(in, &ItemTypes[i].minFloor, 1, 1);
		in->read(in, &ItemTypes[i].maxFloor, 1, 1);
		in->read(in, &ItemTypes[i].special, 1, 1);
		in->read(in, &ItemTypes[i].maxUses, 1, 2);
		in->read(in, &ItemTypes[i].useAction, 1, 2);

		ItemTypes[i].baseStats[0].type = ISTAT_DAMAGE;
		in->read(in, &ItemTypes[i].baseStats[0].value, 1, 4);
		ItemTypes[i].baseStats[1].type = ISTAT_SPEED;
		in->read(in, &ItemTypes[i].baseStats[1].value, 1, 4);
		ItemTypes[i].baseStats[2].type = ISTAT_RANGE;
		in->read(in, &ItemTypes[i].baseStats[2].value, 1, 4);
	}

	in->close(in);
	SDL_free(data);

	// Loading item names

	dataSize = RPK_GetEntryData(rpk, "ITEMNAMES", &data);
	if (dataSize == 0)
	{
		Msg_Fatal("Missing item name definition entry");
	}
	in = SDL_RWFromConstMem(data, dataSize);

	itemNames = SDL_calloc(numItems, sizeof(struct _ItemNameAssoc));

	for (i = 0; i < numItems; i++)
	{
		char gfxEntryName[RPK_NAME_LENGTH + 1];
		int typeId, nameEndOffs;

		int nameLen = SDL_ReadU8(in);
		in->read(in, itemNames[i].name, 1, nameLen);
		itemNames[i].name[nameLen] = '\0';
		typeId = SDL_ReadLE16(in);
		itemNames[i].entityId = typeId;

		nameEndOffs = (int)SDL_strlcpy(gfxEntryName, itemNames[i].name, RPK_NAME_LENGTH + 1);
		ItemTypes[typeId].invSprite = Bmp_CreateFromPAKEntry(rpk, gfxEntryName);
		
		SDL_strlcat(gfxEntryName, "_world", RPK_NAME_LENGTH + 1);
		ItemTypes[typeId].worldSprite = Gfx_LoadSprite(rpk, gfxEntryName);
		
		gfxEntryName[nameEndOffs] = '\0';
		SDL_strlcat(gfxEntryName, "_hud1", RPK_NAME_LENGTH + 1);
		ItemTypes[typeId].hudGfx0 = Bmp_CreateFromPAKEntry(rpk, gfxEntryName);
		gfxEntryName[nameEndOffs] = '\0';
		SDL_strlcat(gfxEntryName, "_hud2", RPK_NAME_LENGTH + 1);
		ItemTypes[typeId].hudGfx1 = Bmp_CreateFromPAKEntry(rpk, gfxEntryName);
	}

	in->close(in);
	SDL_free(data);
}

void Items_Cleanup(void)
{
	int i;
	for (i = 0; i < numItems; i++)
	{
		SDL_free(ItemTypes[i].title);
		SDL_free(ItemTypes[i].desc);
		Bmp_Destroy(&ItemTypes[i].invSprite);
		Bmp_Destroy(&ItemTypes[i].hudGfx0);
		Bmp_Destroy(&ItemTypes[i].hudGfx1);
	}
	SDL_free(ItemTypes);
	SDL_free(itemNames);
	numItems = 0;
}

float Item_GetStat(const Item_t *item, enum ItemStatType stat)
{
	int i;
	for (i = 0; i < 3; i++)
	{
		if (item->stats[i].type == stat)
			return item->stats[i].value;
	}
	return 0.0f;
}

Item_t Item_GetById(int id)
{
	Item_t result;
	int i;

	result.type = INVALID_ITEM_TYPE;
	if (id > numItems)
		return result;

	result.type = id;
	for (i = 0; i < 3; i++)
	{
		result.stats[i] = ItemTypes[id].baseStats[i];
		result.stats[i].value += Math_RandomFloat() * (result.stats[i].value / 5);
	}
	result.data = ItemTypes[id].initialData;
	result.remainingUses = ItemTypes[id].maxUses;

	return result;
}

Item_t Item_GetByName(const char *name)
{
	Item_t result;
	result.type = INVALID_ITEM_TYPE;
	for (int i = 0; i < numItems; i++)
	{
		if (SDL_strcmp(itemNames[i].name, name) == 0)
			return Item_GetById(i);
	}
	return result;
}

Item_t Loot_PickRandom(int depth)
{
	int typeNum;
	do
	{
		typeNum = RNG_RANGE(0, numItems);
	} while (depth < ItemTypes[typeNum].minFloor || depth > ItemTypes[typeNum].maxFloor);
	return Item_GetById(typeNum);
}

int Item_SpecialUseOn(Item_t *item, Item_t *target)
{
	static int scrollSoundId = SOUND_NONE;
	int type = ItemTypes[item->type].special;
	int v;

	if (!target)
		return 0;

	switch (type)
	{
	case ISPEC_SCROLL_REPAIR:
		if (scrollSoundId == SOUND_NONE)
			scrollSoundId = Audio_GetIDForName("SfxScrollUse");
		v = ItemTypes[target->type].maxUses;
		/* TODO: Reduce max durability to prevent infinite items? */
		target->remainingUses += 50;
		if (target->remainingUses > v)
			target->remainingUses = v;
		Audio_PlaySound(scrollSoundId);
		return 1;
	case ISPEC_SCROLL_UPGRADE:
		if (scrollSoundId == SOUND_NONE)
			scrollSoundId = Audio_GetIDForName("SfxScrollUse");
		v = RNG_RANGE(0, 3);
		if (target->stats[v].type == ISTAT_SPEED)
		{
			target->stats[v].value -= Math_RandomFloat() * 3.0f + 1.0f;
			if (target->stats[v].value < 1.0f)
				target->stats[v].value = 1.0f;
		}
		else
			target->stats[v].value += Math_RandomFloat() * 3.0f + 1.0f;
		Audio_PlaySound(scrollSoundId);
		return 1;
	default:
		return 0;
	}
}
