#ifndef EDITSCENE_INVENTORY_HPP
#define EDITSCENE_INVENTORY_HPP
#pragma once

#include <Ogre.h>
#include <flecs.h>
#include <vector>
#include <string>
#include <cstdint>

/**
 * A single slot in an inventory.
 * Stores a reference to an item entity (if the item is a world entity)
 * or stores item data directly for items that exist only in inventory.
 */
struct InventorySlot {
	// Flecs entity ID of the item (0 if slot is empty)
	flecs::entity_t itemEntity = 0;

	// Item data for items that exist only in inventory (no world entity)
	Ogre::String itemId;
	Ogre::String itemName;
	Ogre::String itemType;
	int stackSize = 0;
	int maxStackSize = 99;
	float weight = 0.1f;
	int value = 1;
	Ogre::String useActionName;

	bool isEmpty() const
	{
		return itemEntity == 0 && stackSize <= 0;
	}

	void clear()
	{
		itemEntity = 0;
		itemId.clear();
		itemName.clear();
		itemType.clear();
		stackSize = 0;
		maxStackSize = 99;
		weight = 0.1f;
		value = 1;
		useActionName.clear();
	}
};

/**
 * Inventory component.
 *
 * Attached to a character entity to hold items.
 * Can also be attached to container entities (chests, barrels, etc.)
 * to define their contents.
 *
 * The inventory stores items as InventorySlot entries, each of which
 * may reference a world ItemComponent entity or hold item data directly.
 */
struct InventoryComponent {
	// Maximum number of slots
	int maxSlots = 20;

	// Current slots
	std::vector<InventorySlot> slots;

	// Total weight of all items (computed)
	float totalWeight = 0.0f;

	// Maximum weight capacity (0 = unlimited)
	float maxWeight = 50.0f;

	// Whether this inventory is a container (chest, barrel, etc.)
	// Containers can be opened by characters to transfer items.
	bool isContainer = false;

	// Whether this inventory is currently open (for containers being browsed)
	bool isOpen = false;

	InventoryComponent() = default;

	explicit InventoryComponent(int maxSlots_)
		: maxSlots(maxSlots_)
	{
		slots.reserve(maxSlots_);
	}

	/** Find the first empty slot index, or -1 if full. */
	int findEmptySlot() const
	{
		for (int i = 0; i < maxSlots; i++) {
			if (i >= (int)slots.size())
				return i;
			if (slots[i].isEmpty())
				return i;
		}
		return -1;
	}

	/** Find a slot containing an item with the given itemId. */
	int findItem(const Ogre::String &itemId) const
	{
		for (int i = 0; i < (int)slots.size(); i++) {
			if (!slots[i].isEmpty() && slots[i].itemId == itemId)
				return i;
		}
		return -1;
	}

	/** Find a slot containing an item with the given itemName. */
	int findItemByName(const Ogre::String &itemName) const
	{
		for (int i = 0; i < (int)slots.size(); i++) {
			if (!slots[i].isEmpty() &&
			    slots[i].itemName == itemName)
				return i;
		}
		return -1;
	}

	/** Count total number of items (sum of stack sizes). */
	int countItems() const
	{
		int count = 0;
		for (const auto &slot : slots) {
			if (!slot.isEmpty())
				count += slot.stackSize;
		}
		return count;
	}

	/** Count how many of a specific itemId are in the inventory. */
	int countItem(const Ogre::String &itemId) const
	{
		int count = 0;
		for (const auto &slot : slots) {
			if (!slot.isEmpty() && slot.itemId == itemId)
				count += slot.stackSize;
		}
		return count;
	}

	/** Check if inventory has at least one of a specific itemId. */
	bool hasItem(const Ogre::String &itemId) const
	{
		return findItem(itemId) >= 0;
	}

	/** Check if inventory has at least one of a specific itemName. */
	bool hasItemByName(const Ogre::String &itemName) const
	{
		return findItemByName(itemName) >= 0;
	}

	/** Recalculate total weight. */
	void recalculateWeight()
	{
		totalWeight = 0.0f;
		for (const auto &slot : slots) {
			if (!slot.isEmpty())
				totalWeight += slot.weight * slot.stackSize;
		}
	}
};

#endif // EDITSCENE_INVENTORY_HPP
