Editor/ShaderGraphPlus/ClassNodeType.cs
using Editor;
using ShaderGraphPlus.Nodes;

namespace ShaderGraphPlus;

public class ClassNodeType : INodeType
{
	public virtual string Identifier => Type.FullName;

	public TypeDescription Type { get; }
	public DisplayInfo DisplayInfo { get; protected set; }

	public Menu.PathElement[] Path => Menu.GetSplitPath( DisplayInfo );

	public ClassNodeType( TypeDescription type )
	{
		Type = type;
		if ( Type is not null )
			DisplayInfo = DisplayInfo.ForType( Type.TargetType );
		else
			DisplayInfo = new DisplayInfo();
	}

	public bool TryGetInput( Type valueType, out string name )
	{
		var property = Type.Properties
			.Select( x => (Property: x, Attrib: x.GetCustomAttribute<BaseNodePlus.InputAttribute>()) )
			.Where( x => x.Attrib != null )
			.FirstOrDefault( x => x.Attrib.Type?.IsAssignableFrom( valueType ) ?? true )
			.Property;

		name = property?.Name;
		return name is not null;
	}

	public bool TryGetOutput( Type valueType, out string name )
	{
		var property = Type.Properties
			.Select( x => (Property: x, Attrib: x.GetCustomAttribute<BaseNodePlus.OutputAttribute>()) )
			.Where( x => x.Attrib != null )
			.FirstOrDefault( x => x.Attrib.Type?.IsAssignableTo( valueType ) ?? true )
			.Property;

		name = property?.Name;
		return name is not null;
	}


	public virtual IGraphNode CreateNode( INodeGraph graph )
	{
		var node = Type.Create<BaseNodePlus>();

		node.Graph = graph;

		return node;
	}
}

public sealed class SubgraphNodeType : ClassNodeType
{
	public override string Identifier => AssetPath;
	string AssetPath { get; }

	public SubgraphNodeType( string assetPath, TypeDescription type ) : base( type )
	{
		AssetPath = assetPath;
	}

	public void SetDisplayInfo( ShaderGraphPlus subgraph )
	{
		var info = DisplayInfo;
		if ( !string.IsNullOrEmpty( subgraph.Title ) )
			info.Name = subgraph.Title;
		else
			info.Name = System.IO.Path.GetFileNameWithoutExtension( AssetPath );
		if ( !string.IsNullOrEmpty( subgraph.Description ) )
			info.Description = subgraph.Description;
		if ( !string.IsNullOrEmpty( subgraph.Icon ) )
			info.Icon = subgraph.Icon;
		if ( !string.IsNullOrEmpty( subgraph.Category ) )
			info.Group = subgraph.Category;
		DisplayInfo = info;
	}

	public override IGraphNode CreateNode( INodeGraph graph )
	{
		var node = base.CreateNode( graph );

		if ( node is SubgraphNode subgraphNode )
		{
			subgraphNode.SubgraphPath = AssetPath;
			subgraphNode.OnNodeCreated();
		}

		return node;
	}
}

public sealed class SubgraphOutputNodeType : ClassNodeType
{
	private string Name;
	private SubgraphPortType OutputType;

	public SubgraphOutputNodeType( TypeDescription type, SubgraphPortType outputType, string name = "" ) : base( type )
	{
		Name = name;
		OutputType = outputType;
	}

	public override IGraphNode CreateNode( INodeGraph graph )
	{
		var node = base.CreateNode( graph );
		return node;
	}
}

public sealed class ParameterNodeType : ClassNodeType
{
	BlackboardParameter Parameter;
	Action OnNodeCreated;

	string ImagePath;

	public ParameterNodeType( BlackboardParameter parameter ) : base( null )
	{
		Parameter = parameter;
	}

	public ParameterNodeType( TypeDescription type, string imagePath, Action onNodeCreated ) : base( type )
	{
		ImagePath = imagePath;
		OnNodeCreated = onNodeCreated;
	}

	public override IGraphNode CreateNode( INodeGraph graph )
	{
		var sg = graph as ShaderGraphPlus;

		if ( Parameter == null && !string.IsNullOrWhiteSpace( ImagePath ) )
		{
			var baseName = $"{(sg.IsSubgraph ? "SubgraphInput" : "MaterialParameter")}";
			var id = 0;
			while ( sg.HasParameterWithName( $"{baseName}{id}" ) )
			{
				id++;
			}

			var name = $"{baseName}{id}";

			if ( sg.IsSubgraph && Type.TargetType == typeof( SubgraphInput ) )
			{
				Parameter = new Texture2DSubgraphInputParameter()
				{
					Name = name,
					Value = new TextureInput() { DefaultTexture = ImagePath }
				};
			}
			else if ( !sg.IsSubgraph && Type.TargetType == typeof( Texture2DParameterNode ) )
			{
				Parameter = new Texture2DParameter()
				{
					Name = name,
					Value = new TextureInput() { DefaultTexture = ImagePath }
				};
			}

			sg.AddParameter( Parameter );

			var node = Parameter.ToNode() as BaseNodePlus;
			node.Graph = sg;

			OnNodeCreated?.Invoke();

			return node;
		}
		else
		{
			var node = Parameter.ToNode() as BaseNodePlus;
			node.Graph = sg;
			return node;
		}
	}
}

public sealed class NamedRerouteDeclarationNodeType : ClassNodeType
{
	private string Name;

	public NamedRerouteDeclarationNodeType( TypeDescription type, string name = "" ) : base( type )
	{
		Name = name;
	}

	public override IGraphNode CreateNode( INodeGraph graph )
	{
		var node = base.CreateNode( graph );
		if ( node is NamedRerouteDeclarationNode namedRerouteDeclarationNode )
		{
			namedRerouteDeclarationNode.Name = Name;

			return namedRerouteDeclarationNode;
		}
		return node;
	}
}

public sealed class NamedRerouteNodeType : ClassNodeType
{
	private string Name;

	public NamedRerouteNodeType( TypeDescription type, string name = "" ) : base( type )
	{
		Name = name;
	}

	public override IGraphNode CreateNode( INodeGraph graph )
	{
		var node = base.CreateNode( graph );
		if ( node is NamedRerouteNode namedRerouteNode )
		{
			namedRerouteNode.Name = Name;

			return namedRerouteNode;
		}
		return node;
	}
}