Player/PlayerTool.cs
namespace PlanetMeat;

[Group( "Planet Meat - Player" ), Title( "Player Tool" ), Icon( "touch_app" )]
public abstract class PlayerTool : Component, IAttackBlame
{
	public static PlayerTool Selected { get; private set; } = null;
	public static List<PlayerTool> Toolbox { get; } = [];

	private IWorldRing GhostRing
	{
		get;
		set
		{
			if ( field?.GameObject is GameObject ringGo && ringGo.IsValid )
				ringGo.Destroy();
			field = value;
		}
	}

	protected virtual IWorldRing NextWorldRing => null;

	protected IWorldRing CreateWorldRing( Vector3 position, bool ghost = false )
	{
		var ring = NextWorldRing;
		if ( ring is null )
			return null;

		ring.WorldPosition = position;
		if ( ghost )
			ring.MultiplyAlpha( 0.4f );
		return ring;
	}
	protected IWorldRing CreateWorldRing( bool ghost = false ) => CreateWorldRing( WorldPosition, ghost );

	private void TryCreateGhostRing()
	{
		GhostRing = IsSelected && IsUsable ? CreateWorldRing( true ) : null;
	}

	public static void BakeTools( IEnumerable<PlayerTool> allTools )
	{
		Toolbox.Clear();
		Toolbox.AddRange( allTools.Where( t => t.Available ) );
		Toolbox.Sort( SortTools );
	}

	private static int SortTools( PlayerTool a, PlayerTool b )
	{
		var aTech = a.Tech;
		var bTech = b.Tech;

		var aFree = aTech is null;
		var bFree = bTech is null;
		if ( aFree != bFree )
			return aFree ? -1 : 1;

		if ( !aFree && !bFree )
		{
			var costComparison = aTech.Duration.CompareTo( bTech.Duration );
			if ( costComparison != 0 )
				return costComparison;
		}

		return a.Ident.CompareTo( b.Ident );
	}

	public static int IndexToSlotNumber( int i ) => i < 9 ? 1 + i : (i == 9 ? 0 : -1);
	public static string IndexToSlotName( int i ) => i < 10 ? "Slot" + IndexToSlotNumber( i ) : "";
	public static bool IndexSlotPressed( int i ) => i < 10 && Input.Pressed( IndexToSlotName( i ) );

	[Property] public string Ident { get; set; } = "";
	[Property] public string Icon { get; set; } = "";
	[Property] public string RequiredTech { get; set; } = "";
	private BaseTech Tech => Research.Current.GetTech( RequiredTech );
	[Property] public string RechargeImprovementIdent { get; set; } = "";

	private bool _needsTechCheck = true;
	private bool HasTech
	{
		get
		{
			if ( _needsTechCheck )
			{
				_needsTechCheck = false;
				field = Tech.Completed;
			}
			return field;
		}
	}
	public virtual bool Available => HasTech;

	public virtual float BaseCooldown => 1.0f;
	public ValueAggregator<float> AggregateRecharge => field ??= ValueAggregator<float>.FromImprovement( RechargeImprovementIdent, 1.0f );
	public float Cooldown => BaseCooldown / AggregateRecharge.Total;

	public float CooldownRemaining { get; private set; }
	public bool IsOnCooldown => CooldownRemaining > 0.0f;
	public virtual bool IsUsable => !IsOnCooldown;
	public virtual bool IsSelectable => true;

	public bool IsSelected => this == Selected;

	public void BeginUse()
	{
		if ( IsSelectable && Agenda.Current.Phase == Agenda.Phases.Wave )
		{
			Selected?.EndUse();
			Selected = this;
			OnUseBegins();
		}
	}
	protected virtual void OnUseBegins()
	{
		TryCreateGhostRing();
	}

	public void EndUse()
	{
		if ( IsSelected )
			Selected = null;
		OnUseEnds();
	}
	protected virtual void OnUseEnds()
	{
		GhostRing = null;
	}

	public void ToggleUse()
	{
		if ( IsSelected )
			EndUse();
		else
			BeginUse();
	}

	protected override void OnUpdate()
	{
		base.OnUpdate();
		if ( IsOnCooldown )
		{
			CooldownRemaining -= Time.Delta;
			if ( !IsOnCooldown )
				EndCooldown();
		}
	}

	public void StartCooldown()
	{
		CooldownRemaining = Cooldown;
		OnCooldownStarted();
	}
	protected virtual void OnCooldownStarted()
	{
		GhostRing = null;
	}

	public void EndCooldown()
	{
		CooldownRemaining = 0.0f;
		OnCooldownEnded();
	}
	protected virtual void OnCooldownEnded()
	{
		TryCreateGhostRing();
	}

	public virtual void OnHover( Vector3 position )
	{
		GhostRing?.WorldPosition = position;
	}

	public virtual void OnPlaceStarted( Vector3 position ) { }
	public virtual void OnPlaceUpdated( Vector3 fromPosition, Vector3 toPosition ) { }
	public virtual void OnPlaceEnded( Vector3 position ) { }

	public virtual void OnCancel( Vector3 position )
	{
		EndUse();
	}
}