Items/Plant.cs
using System;
using Clover.Carriable;
using Clover.Data;
using Clover.Interactable;
using Clover.Persistence;
using Clover.Player;

namespace Clover.Items;

[Category( "Clover/Items" )]
public class Plant : Component, IInteract, IWaterable, IDiggable, IPersistent
{
	[RequireComponent] public WorldItem WorldItem { get; set; }
	[RequireComponent] public WorldLayerObject WorldLayerObject { get; set; }

	[Property] public GameObject Model { get; set; }
	[Property] public GameObject SeedHole { get; set; } // TODO: better name

	[Property] public GameObject WateredParticles { get; set; }

	// [Property] public PlantData PlantData { get; set; }
	public PlantData PlantData => WorldItem.ItemData as PlantData;

	public DateTime LastWatered { get; set; }

	public DateTime LastProcess { get; set; }
	public float Growth { get; set; } = 0f;
	public float Wilt { get; set; } = 0f;
	public float Water { get; set; } = 0f;
	public bool WantsToSpread { get; set; } = false;

	// flower grows fully in 3 days
	public const float GrowthPerHour = 1f / 72f;

	// wilting takes a day
	public const float WiltPerHour = 1f / 24f;

	// watered once per day
	public const float WaterUsedPerHour = 1f / 24f;

	public enum GrowthStage
	{
		Seed = 0,
		Sprout = 1,
		Stem = 2,
		Budding = 3,
		Flowering = 4,
	}

	public GrowthStage Stage
	{
		get
		{
			if ( Growth < 0.2f )
			{
				return GrowthStage.Seed;
			}
			else if ( Growth < 0.4f )
			{
				return GrowthStage.Sprout;
			}
			else if ( Growth < 0.6f )
			{
				return GrowthStage.Stem;
			}
			else if ( Growth < 0.8f )
			{
				return GrowthStage.Budding;
			}
			else
			{
				return GrowthStage.Flowering;
			}
		}
	}

	public void StartInteract( PlayerCharacter player )
	{
		throw new System.NotImplementedException();
	}

	public void FinishInteract( PlayerCharacter player )
	{
		throw new System.NotImplementedException();
	}

	public string GetInteractName()
	{
		return "?";
	}

	public void OnWater( WateringCan wateringCan )
	{
		Log.Info( "Watering plant" );
		LastWatered = DateTime.Now;
		Water = 1f;
		UpdateVisuals();
	}

	public bool CanDig()
	{
		return Growth >= 1f;
	}

	public bool OnDig( PlayerCharacter player, WorldItem item )
	{
		// TODO: give item
		return false;
	}

	public bool GiveItemWhenDug()
	{
		throw new System.NotImplementedException();
	}

	public void OnSave( PersistentItem item )
	{
		item.SetSaveData( "LastWatered", LastWatered );
		item.SetSaveData( "LastProcess", LastProcess );
		item.SetSaveData( "Growth", Growth );
		item.SetSaveData( "Wilt", Wilt );
		item.SetSaveData( "Water", Water );
	}

	public void OnLoad( PersistentItem item )
	{
		LastWatered = item.GetSaveData<DateTime>( "LastWatered" );
		LastProcess = item.GetSaveData<DateTime>( "LastProcess" );
		Growth = item.GetSaveData<float>( "Growth" );
		Wilt = item.GetSaveData<float>( "Wilt" );
		Water = item.GetSaveData<float>( "Water" );

		UpdateVisuals();
	}

	protected override void OnFixedUpdate()
	{
		if ( DateTime.Now - LastProcess > TimeSpan.FromHours( 1 ) )
		{
			SimulateHour( DateTime.Now );
			LastProcess = DateTime.Now;
			UpdateVisuals();
		}
	}

	private void UpdateVisuals()
	{
		if ( Model.IsValid() )
		{
			var growth = Math.Clamp( Growth, 0.2f, 1f );
			Model.LocalScale = new Vector3( growth, growth, growth );
		}

		if ( SeedHole.IsValid() )
		{
			SeedHole.Enabled = Growth < 0.2f;
		}

		if ( WateredParticles.IsValid() )
		{
			WateredParticles.Enabled = Water > 0.9f;
		}
	}

	public void SimulateHour( DateTime time )
	{
		/*var lastRain = NodeManager.WeatherManager.GetLastPrecipitation( time );
		var hoursSinceLastRain = (time - lastRain.Time).TotalHours;
		if ( hoursSinceLastRain <= 1 )
		{
			Water = 1f;
			LastWatered = lastRain.Time;
		}*/

		// growth
		if ( Growth < 1f && Wilt == 0 )
		{
			// Growth += GrowthPerHour;
			Growth = Math.Clamp( Growth + GrowthPerHour, 0f, 1f );
		}

		// wilt
		if ( Water > 0 )
		{
			Water = Math.Clamp( Water - WaterUsedPerHour, 0f, 1f );
			Wilt = Math.Clamp( Wilt - WiltPerHour, 0f, 1f );
		}
		else
		{
			Wilt = Math.Clamp( Wilt + WiltPerHour, 0f, 1f );
		}

		if ( Stage == GrowthStage.Flowering && Wilt == 0 )
		{
			if ( Random.Shared.Float() > 0.9 )
			{
				WantsToSpread = true;
			}
		}

		if ( WantsToSpread )
		{
			Spread();
			WantsToSpread = false;
		}

		Log.Info( $"Simulated hour for plant: Growth: {Growth}, Wilt: {Wilt}, Water: {Water}" );
	}

	public void Spread()
	{
		/*var nodeLink = WorldLayerObject.World.GetItem( GameObject );
		if ( nodeLink == null )
		{
			Log.Warning( "No node link found for plant" );
			return;
		}

		var neighbors = WorldLayerObject.World.GetNeighbors( nodeLink.GridPosition );
		var emptyNeighbors =
			neighbors.Where( n => WorldLayerObject.World.GetItem( n, World.ItemPlacement.Floor ) == null );
		var validNeighbors = emptyNeighbors.Where( n => !WorldLayerObject.World.IsBlockedGridPosition( n ) ).ToList();

		if ( !validNeighbors.Any() )
		{
			Log.Warning( "No valid neighbors found for plant" );
			return;
		}

		// var randomNeighbor = validNeighbors.ToList().PickRandom();
		var randomNeighbor = Random.Shared.FromList( validNeighbors.ToList() );

		// clone self
		// World.SpawnNode( ItemData, (ItemData as PlantData).PlantedScene, randomNeighbor, World.ItemRotation.North, World.ItemPlacement.Floor );
		WorldLayerObject.World.SpawnCustomNode(
			PlantData,
			PlantData.PlantedScene,
			randomNeighbor,
			World.ItemRotation.North,
			World.ItemPlacement.Floor
		);*/
	}
}