Player/Inventory/Container/InventoryContainer.cs
namespace Opium;

public partial class InventoryContainer : Component
{
	/// <summary>
	/// Which item type does this inventory container accept?
	/// </summary>
	[Property] public ItemType ItemType { get; set; }

	[Property] public int MaxItems { get; set; } = 0;
	[Property] public List<InventoryItem> Items { get; set; }
	[Property] public List<InventoryItemResource> DefaultItems { get; set; }

	protected override void OnEnabled()
	{
		Items ??= new();
		Items?.Clear();

		for ( int i = 0; i < MaxItems; i++ )
		{
			// Add empty items
			Items.Add( null );
		}

		SetupDefaultItems();
	}

	/// <summary>
	/// Sets up default items if we have any set. (Mainly for development)
	/// </summary>
	void SetupDefaultItems()
	{
		if ( DefaultItems is not null && DefaultItems.Count > 0 )
		{
			int index = 0;
			foreach ( var resource in DefaultItems )
			{
				SetItem( index, resource );
				index++;
			}
		}
	}

	/// <summary>
	/// Sets an item from a resource
	/// </summary>
	/// <param name="index"></param>
	/// <param name="resource"></param>
	public void SetItem( int index, InventoryItemResource resource )
	{
		// TF?
		if ( Items.Count < index ) return;

		Items[index] = new InventoryItem( resource );
	}

	/// <summary>
	/// Clear an item from the index
	/// </summary>
	/// <param name="index"></param>
	public void RemoveItem( int index )
	{
		if ( MaxItems < 1 )
		{
			Items.RemoveAt( index );
		}
		else
		{
			Items[index] = null;
		}
	}

	// Try to find an existing inventory item that can be stacked.
	private InventoryItem FindExistingItem( InventoryItemResource resource, int quantity )
	{
		foreach ( var item in Items.Where( x => x is not null ) )
		{
			// If the item is the same, and it's stackable, let's use it!
			if ( item.ItemResource == resource && item.CanStack( quantity ) )
			{
				return item;
			}
		}

		return null;
	}

	/// <summary>
	/// Tries to add an item to the player's inventory
	/// </summary>
	/// <param name="resource"></param>
	/// <param name="quantity"></param>
	public void AddItem( InventoryItemResource resource, int quantity = 1 )
	{
		// Look for an existing item instance that has enough stack quantity for our item.
		var existingItem = FindExistingItem( resource, quantity );
		if ( existingItem is not null )
		{
			// Up the item's quantity, and bail.
			existingItem.Quantity += quantity;
			return;
		}

		// If we don't have an item limit
		if ( MaxItems < 1 )
		{
			Items.Add( new InventoryItem( resource ) );
		}
		else
		{
			// Find the index of the first available slot
			var indexOf = Items.IndexOf( Items.FirstOrDefault( x => x is null ) );
			if ( indexOf != -1 )
			{
				SetItem( indexOf, resource );
			}
		}
	}

	/// <summary>
	/// Does the player have an item in this container?
	/// </summary>
	/// <param name="resource"></param>
	/// <returns></returns>
	public bool HasItem( InventoryItemResource resource )
	{
		var itemInstance = Items.FirstOrDefault( x => x.ItemResource == resource );
		return itemInstance is not null;
	}

	/// <summary>
	/// Gets an item from a container.
	/// </summary>
	/// <param name="resource"></param>
	/// <returns></returns>
	public (InventoryItem Item, int Index) GetItem( InventoryItemResource resource )
	{
		var itemInstance = Items.FirstOrDefault( x => x.ItemResource == resource );
		var itemId = Items.IndexOf( itemInstance );

		return (itemInstance, itemId);
	}

	public override int GetHashCode()
	{
		int hash = base.GetHashCode();
		if ( Items is null ) return hash;
		
		foreach ( var item in Items )
		{
			hash += item?.ItemResource?.GetHashCode() ?? 0;
		}

		return hash;
	}
}