Inventory/InventorySlot.Actions.cs
using System;
using System.Resources;
using Clover.Components;
using Clover.Data;
using Clover.Items;
using Clover.Persistence;
using Clover.Ui;

namespace Clover.Inventory;

public sealed partial class InventorySlot
{
	public void Drop()
	{
		if ( !Networking.IsHost )
		{
			Log.Error( "Only the host can drop items for now." );
			return;
		}

		Log.Info( $"Dropping item {PersistentItem.ItemData.ResourceName}" );
		var position = InventoryContainer.Player.GetAimingGridPosition();
		var playerRotation =
			World.GetItemRotationFromDirection(
				World.Get4Direction( InventoryContainer.Player.PlayerController.Yaw ) );

		/*try
		{*/
		InventoryContainer.Player.World.SpawnDroppedNode( PersistentItem, position, playerRotation );
		/*}
		catch ( Exception e )
		{
			//x NodeManager.UserInterface.ShowWarning( e.Message );
			Log.Error( e.Message );
			return;
		}*/

		Sound.Play( "sounds/interact/item_drop.sound", InventoryContainer.Owner.WorldPosition );

		TakeOneOrDelete();
	}

	public void Place()
	{
		if ( !Networking.IsHost )
		{
			Log.Error( "Only the host can place items for now." );
			return;
		}

		InventoryContainer.Player.ItemPlacer.StartPlacingInventorySlot( Index );
		InventoryUi.Instance.Close();
	}

	public void Equip()
	{
		if ( PersistentItem.ItemData is IEdibleData )
		{
			HoldEdible();
			return;
		}

		Components.Equips.EquipSlot slot;

		// get slot from item
		/*if ( _persistentItem is Persistence.BaseCarriable )
		{
			slot = Components.Equips.EquipSlot.Tool;
		}
		else if ( _persistentItem is ClothingItem clothingItem )
		{
			slot = clothingItem.EquipSlot;
			if ( slot == 0 )
			{
				// throw new Exception( $"Invalid equip slot for {clothingItem} ({clothingItem.GetName()}) {clothingItem.ItemData.GetType()}" );
				NodeManager.UserInterface.ShowWarning( "Invalid equip slot for this item." );
			}
		}
		else
		{
			// throw new Exception( $"Item {_persistentItem} is not equipable." );
			NodeManager.UserInterface.ShowWarning( "This item is not equipable." );
			return;
		}*/

		slot = Equips.EquipSlot.Tool; // TODO: get slot from item

		if ( PersistentItem.ItemData is not ToolData toolData )
		{
			Log.Error( "Item is not a tool" ); // TODO: handle other types of items
			return;
		}

		// create a copy of the currently equipped item
		PersistentItem currentEquip = null;
		if ( InventoryContainer.Player.Equips.HasEquippedItem( slot ) )
		{
			currentEquip =
				Persistence.PersistentItem.Create( InventoryContainer.Player.Equips.GetEquippedItem( slot ) );
		}

		/*if ( _persistentItem is Persistence.BaseCarriable carriable )
		{
			var carriableNode = carriable.Create();
			InventoryContainer.Player.Equips.SetEquippedItem( Components.Equips.EquipSlot.Tool, carriableNode );
		}
		else if ( _persistentItem is ClothingItem clothingItem )
		{
			var clothingNode = clothingItem.Create();
			InventoryContainer.Player.Equips.SetEquippedItem( slot, clothingNode );
		}*/

		// equip the item
		if ( toolData != null )
		{
			var tool = GetItem().SpawnCarriable();
			InventoryContainer.Player.Equips.SetEquippedItem( slot, tool.GameObject );
		}

		var currentIndex = Index;

		// remove this item from inventory, making place for the previously equipped item
		Delete();

		// if there was a previously equipped item, add it back to the inventory
		if ( currentEquip != null )
		{
			InventoryContainer.AddItemToIndex( currentEquip, currentIndex );
		}

		InventoryContainer.Player.Save();
	}

	/*
	public void Bury()
	{
		Logger.Info( "Burying item" );

		var pos = InventoryContainer.Player.Interact.GetAimingGridPosition();
		var floorItem = InventoryContainer.Player.World.GetItem( pos, World.ItemPlacement.Floor );
		if ( floorItem.Node is not Hole hole )
		{
			return;
		}

		// spawn item underground
		InventoryContainer.Player.World.SpawnPersistentNode( _persistentItem, pos, World.ItemRotation.North, World.ItemPlacement.Underground,
			true );

		// remove hole so it isn't obstructing the dirt that will be spawned next
		InventoryContainer.Player.World.RemoveItem( hole );

		// spawn dirt on top
		InventoryContainer.Player.World.SpawnNode( ResourceManager.LoadItemFromId<ItemData>( "buried_item" ), pos,
			World.ItemRotation.North, World.ItemPlacement.Floor, false );

		Logger.Info( "Item buried" );

		TakeOneOrDelete();
	}

	public void Plant()
	{
		var pos = InventoryContainer.Player.Interact.GetAimingGridPosition();
		var floorItem = InventoryContainer.Player.World.GetItem( pos, World.ItemPlacement.Floor );
		if ( floorItem.Node is not Hole hole )
		{
			return;
		}

		if ( _persistentItem.ItemData is SeedData seedData )
		{

			var plantItemData = seedData.SpawnedItemData;

			if ( plantItemData == null )
			{
				// throw new System.Exception( "Seed data does not have a spawned item data." );
				NodeManager.UserInterface.ShowWarning( "This seed does not have a plant data." );
				return;
			}

			// remove hole so it isn't obstructing the plant that will be spawned next
			InventoryContainer.Player.World.RemoveItem( hole );

			if ( plantItemData is PlantData plantData )
			{
				InventoryContainer.Player.World.SpawnNode( plantItemData, plantData.PlantedScene, pos, World.ItemRotation.North, World.ItemPlacement.Floor );
			}
			else
			{
				// throw new System.Exception( "Spawned item data is not a plant data. Unsupported for now." );
				NodeManager.UserInterface.ShowWarning( "Spawned item data is not a plant data. Unsupported for now." );
				return;
			}

			TakeOneOrDelete();

			return;

		}
		else if ( _persistentItem.ItemData is PlantData plantData )
		{

			var plantScene = plantData.PlantedScene;

			if ( plantScene == null )
			{
				// throw new System.Exception( "Plant data does not have a planted scene." );
				NodeManager.UserInterface.ShowWarning( "This plant does not have a planted scene." );
			}

			// remove hole so it isn't obstructing the plant that will be spawned next
			InventoryContainer.Player.World.RemoveItem( hole );

			InventoryContainer.Player.World.SpawnNode( plantData, plantScene, pos, World.ItemRotation.North, World.ItemPlacement.Floor );

			TakeOneOrDelete();

			return;

		}

		// throw new System.Exception( "Item data is not a seed or plant data." );

		NodeManager.UserInterface.ShowWarning( "This item is not a seed or plant." );

		// Inventory.World.RemoveItem( floorItem );
	}

	public void SetWallpaper()
	{

		if ( _persistentItem.ItemData is not WallpaperData wallpaperData )
		{
			// throw new System.Exception( "Item data is not a wallpaper data." );
			NodeManager.UserInterface.ShowWarning( "This item is not a wallpaper." );
			return;
		}

		var interiorManager = InventoryContainer.Player.World.GetNodesOfType<InteriorManager>().FirstOrDefault();

		if ( interiorManager == null || !GodotObject.IsInstanceValid( interiorManager ) )
		{
			// throw new System.Exception( "Interior manager not found." );
			NodeManager.UserInterface.ShowWarning( "Interior manager not found. Are you inside a house?" );
			return;
		}

		// TODO: get which room to set the wallpaper for
		interiorManager.SetWallpaper( "first", wallpaperData );

		TakeOneOrDelete();

	}

	public void SetFlooring()
	{

		if ( _persistentItem.ItemData is not FlooringData floorData )
		{
			// throw new System.Exception( "Item data is not a flooring data." );
			NodeManager.UserInterface.ShowWarning( "This item is not a flooring." );
			return;
		}

		var interiorManager = InventoryContainer.Player.World.GetNodesOfType<InteriorManager>().FirstOrDefault();

		if ( interiorManager == null || !GodotObject.IsInstanceValid( interiorManager ) )
		{
			// throw new System.Exception( "Interior manager not found." );
			NodeManager.UserInterface.ShowWarning( "Interior manager not found. Are you inside a house?" );
			return;
		}

		// TODO: get which room to set the wallpaper for
		interiorManager.SetFloor( "first", floorData );

		TakeOneOrDelete();

	}

	public void Eat()
	{

		// TODO: check with some kind of interface if the item is edible
		if ( _persistentItem.ItemData is not FruitData foodData )
		{
			// throw new System.Exception( "Item data is not a food data." );
			NodeManager.UserInterface.ShowWarning( "This item is not edible." );
			return;
		}

		InventoryContainer.Player.Inventory.GetNode<AudioStreamPlayer3D>( "ItemEat" ).Play();

		Logger.Info( "Eating food" );

		TakeOneOrDelete();

	}

	public Texture2D GetIconTexture()
	{
		return _persistentItem.GetIconTexture();
	}*/

	public void Split( int amount )
	{
		if ( Amount <= 1 )
		{
			//x NodeManager.UserInterface.ShowWarning( "Can't split a single item." );
			return;
		}

		if ( amount > Amount )
		{
			//x NodeManager.UserInterface.ShowWarning( "Can't split more than the amount of items in the slot." );
			return;
		}

		if ( InventoryContainer.FreeSlots <= 0 )
		{
			//x NodeManager.UserInterface.ShowWarning( "No free slots." );
			return;
		}

		SetAmount( Amount - amount );

		var newItem = PersistentItem.Clone();

		// var slot = new InventorySlot<PersistentItem>( InventoryContainer );
		// slot.SetItem( newItem );
		// slot.Amount = amount;

		var slot = InventoryContainer.AddItem( newItem );
		slot.SetAmount( amount );
	}

	/*public void Open()
	{
		if ( _persistentItem is not Persistence.Gift gift ) throw new Exception( "Item is not a gift" );

		if ( gift.Items.Count == 0 ) throw new Exception( "Gift is empty" );

		if ( InventoryContainer.FreeSlots < gift.Items.Count ) throw new Exception( "Not enough free slots" );

		foreach ( var item in gift.Items )
		{
			InventoryContainer.AddItem( item );
		}

		TakeOneOrDelete();

	}
	*/

	public void SpawnObject()
	{
		if ( !Networking.IsHost )
		{
			Log.Error( "Only the host can spawn objects for now." );
			return;
		}

		var objectData = PersistentItem.ItemData.ObjectData;

		if ( objectData == null )
		{
			Log.Error( "Item does not have an object data" );
			return;
		}

		var gameObject = objectData.Prefab.Clone();

		var worldObject = gameObject.GetComponent<WorldObject>();
		worldObject.WorldLayerObject.SetLayer( InventoryContainer.Player.WorldLayerObject.Layer, true );

		var size = gameObject.GetBounds().Size.Length;

		worldObject.WorldPosition = InventoryContainer.Player.WorldPosition +
		                            InventoryContainer.Player.Model.WorldRotation.Forward * size;

		TakeOneOrDelete();
	}

	public void Plant()
	{
		if ( !Networking.IsHost )
		{
			Log.Error( "Only the host can plant objects for now." );
			return;
		}

		if ( PersistentItem.ItemData is not SeedData seedData )
		{
			Log.Error( "Item is not a seed" );
			return;
		}

		var aimingGridPosition = InventoryContainer.Player.GetAimingGridPosition();
		var floorItem = InventoryContainer.Player.World.GetWorldItem<Hole>( aimingGridPosition );
		if ( floorItem == null )
		{
			Log.Warning( "No hole found" );
			return;
		}

		var spawnedItemData = seedData.SpawnedItemData;

		if ( spawnedItemData == null )
		{
			Log.Error( "Seed does not have a spawned item data" );
			return;
		}

		floorItem.RemoveFromWorld();

		if ( spawnedItemData is PlantData plantData )
		{
			InventoryContainer.Player.World.SpawnCustomItem( plantData, plantData.PlantedScene, aimingGridPosition,
				World.ItemRotation.North );
		}
		else
		{
			Log.Error( "Spawned item data is not a plant data" );
			return;
		}

		TakeOneOrDelete();
	}

	public void Destroy()
	{
		TakeOneOrDelete();
	}

	public void HoldEdible()
	{
		var carriedPersistentItem = Persistence.PersistentItem.Create( "carried_edible:4023053997083351548" );
		carriedPersistentItem.SetSaveData( "EdibleData", PersistentItem.ItemData.GetIdentifier() );

		var carriedEdible = carriedPersistentItem.SpawnCarriable();

		InventoryContainer.Player.Equips.SetEquippedItem( Equips.EquipSlot.Tool, carriedEdible.GameObject );

		TakeOneOrDelete();
	}
}