Editor/ShaderGraphPlus/Nodes Core/NamedReroute.cs

using NodeEditorPlus;
using static ShaderGraphPlus.ShaderGraphPlusGlobals;

namespace ShaderGraphPlus.Nodes;

/// <summary>
/// Declare a named reroute.
/// </summary>
[Title( "Named Reroute Declaration" ), Category( "Utility" ), Icon( "route" )]
[InternalNode]
public sealed class NamedRerouteDeclarationNode : ShaderNodePlus, IErroringNode
{
	[JsonIgnore, Hide, Browsable( false )]
	public override Color NodeTitleColor => _namedRerouteTitleColor;

	[Hide]
	public override string Title => string.IsNullOrEmpty( _name ) ?
	$"{DisplayInfo.For( this ).Name}" :
	$"{_name}"; // $"{DisplayInfo.For( this ).Name} ({Name})";

	[JsonIgnore, Hide, Browsable( false )]
	private bool _hasNameConflict;

	[JsonIgnore, Hide, Browsable( false )]
	private string _name;
	public string Name
	{
		get => _name;
		set
		{
			var lastName = _name;

			if ( !_hasNameConflict )
			{
				UpdateNameReferences( lastName, value );
			}

			_name = value;
		}
	}

	[JsonIgnore, Hide, Browsable( false )]
	private Color _namedRerouteTitleColor = Color.Parse( "#9d00ff" )!.Value;
	[Title( "Title Color" )]
	public Color NamedRerouteTitleColor
	{
		get => _namedRerouteTitleColor;
		set
		{
			_namedRerouteTitleColor = value;

			UpdateTitleColors( _namedRerouteTitleColor );
		}
	}

	private void UpdateNameReferences( string lastName, string newName )
	{
		var graph = Graph as ShaderGraphPlus;

		if ( !string.IsNullOrWhiteSpace( lastName ) )
		{
			foreach ( var namedReroute in graph.Nodes.OfType<NamedRerouteNode>() )
			{
				if ( namedReroute.Name == lastName )
				{
					//SGPLog.Info( $"Changing named reroute name reference from \"{namedReroute.Name}\" to \"{newName}\"" );
					namedReroute.Name = newName;
				}
			}
		}
	}

	private void UpdateTitleColors( Color newColor )
	{
		if ( !string.IsNullOrWhiteSpace( Name ) && Graph is ShaderGraphPlus graph )
		{
			foreach ( var namedReroute in graph.Nodes.OfType<NamedRerouteNode>() )
			{
				if ( namedReroute.Name == Name )
				{
					namedReroute.NamedReouteTitleColor = newColor;
				}
			}
		}
	}

	[Title( "Input" )]
	[Input]
	[Hide]
	public NodeInput Input { get; set; }

	[Output, Hide, Title( "" )]
	public NodeResult.Func Result => ( GraphCompiler compiler ) =>
	{
		var result = compiler.ResultOrDefault( Input, 0.0f );
		result.Constant = true;
		return result;
	};

	public List<string> GetErrors()
	{
		var errors = new List<string>();

		if ( !string.IsNullOrWhiteSpace( _name ) )
		{
			var graph = Graph as ShaderGraphPlus;

			if ( _name == "None" )
				errors.Add( "\"None\" is a reserved name" );

			foreach ( var node in graph.Nodes.OfType<NamedRerouteDeclarationNode>() )
			{
				if ( node == this )
					continue;

				if ( node._name == _name )
				{
					errors.Add( $"Duplicate name \"{_name}\" on {this.DisplayInfo.Name}" );
					_hasNameConflict = true;
					break;
				}
				else
				{
					_hasNameConflict = false;
				}
			}
		}

		return errors;
	}
}

/// <summary>
/// Reference a declared named reroute.
/// </summary>
[Title( "Named Reroute" ), Category( "Utility" ), Icon( "route" )]
[InternalNode]
public sealed class NamedRerouteNode : ShaderNodePlus
{
	[JsonIgnore, Hide, Browsable( false )]
	public override Color NodeTitleColor => NamedReouteTitleColor;

	[Hide]
	public override string Title => string.IsNullOrEmpty( Name ) ?
		$"{DisplayInfo.For( this ).Name}" :
		$"{Name}"; // $"{DisplayInfo.For( this ).Name} ({Name})";

	[JsonIgnore, Hide, Browsable( false )]
	private string _name;
	[global::Editor( ControlWidgetCustomEditors.NamedRerouteReferenceEditor )]
	public string Name
	{
		get => _name;
		set
		{
			_name = value;
			UpdateTitleColor();
		}
	}

	[Hide, Browsable( false )]
	public Color NamedReouteTitleColor { get; set; } = Color.Parse( "#9d00ff" )!.Value;

	private void UpdateTitleColor()
	{
		if ( !string.IsNullOrWhiteSpace( Name ) && Graph is ShaderGraphPlus graph )
		{
			foreach ( var namedRerouteDeclaration in graph.Nodes.OfType<NamedRerouteDeclarationNode>() )
			{
				if ( namedRerouteDeclaration.Name == Name )
				{
					NamedReouteTitleColor = namedRerouteDeclaration.NamedRerouteTitleColor;
					break;
				}
			}
		}
	}

	[Output, Hide, Title( "" )]
	public NodeResult.Func Result => ( GraphCompiler compiler ) =>
	{
		var result = compiler.ResultNamedReroute( Name );

		if ( !result.IsValid )
		{
			return new NodeResult( ResultType.Float, "0.0f", constant: true );
		}

		return new NodeResult( result.ResultType, result.Code, constant: true );
	};
}