Editor/ShaderGraphPlus/ShaderTemplate/ShaderTemplateResource.cs
using Editor;

namespace ShaderGraphPlus;

[AssetType( Name = ShaderGraphPlusGlobals.ShaderTemplateAssetTypeName, Extension = ShaderGraphPlusGlobals.ShaderTemplateAssetTypeExtension )]
public sealed partial class ShaderTemplateResource
{

	/// <summary>
	/// The name of the Template in the ShaderType dropdown menu of the ShaderGraphPlus graph settings. Fallsback to asset name if left empty.
	/// </summary>
	[TabPage( "General" )]
	public string Title { get; set; } = "";

	/// <summary>
	/// The icon that will be used in the ShaderType dropdown menu of the ShaderGraphPlus graph settings.
	/// </summary>
	[TabPage( "General" )]
	[IconName]
	public string Icon { get; set; } = "view_in_ar";

	/// <summary>
	/// What shader type this shader template represents
	/// </summary>
	[TabPage( "General" )]
	public ShaderDomain ShaderDomain { get; set; } = ShaderDomain.Surface;

	/// <summary>
	/// What shading model this shader template supports
	/// </summary>
	[TabPage( "General" )]
	[ShowIf( nameof( ShaderDomain ), ShaderDomain.Surface )]
	public ShadingModel ShadingModel { get; set; } = ShadingModel.Lit;

	/// <summary>
	/// Should this template enforce RenderFace to the specified option
	/// </summary>
	[TabPage( "General" )]
	[Title( "Enforce Render Face" )]
	[ToggleGroup( "EnforceRenderFace" )]
	[ShowIf( nameof( ShaderDomain ), ShaderDomain.Surface )]
	public bool EnforceRenderFace { get; set; } = true;

	[TabPage( "General" )]
	[ToggleGroup( "EnforceRenderFace" )]
	[ShowIf( nameof( ShaderDomain ), ShaderDomain.Surface )]
	public RenderFace RenderFace { get; set; } = RenderFace.Front;

	[TabPage( "General" ), Group( "Supported Optional Material Inputs" )]
	[ShowIf( nameof( ShaderDomain ), ShaderDomain.Surface )]
	public bool Opacity { get; set; } = true;

	[TabPage( "General" ), Group( "Supported Optional Material Inputs" )]
	[ShowIf( nameof( ShaderDomain ), ShaderDomain.Surface )]
	public bool PositionOffset { get; set; } = true;

	[TabPage( "General" ), Group( "Supported Blend Modes" )]
	[InfoBox( "When no blend modes are toggled, the opaque blending mode will be used", "info", EditorTint.Blue )]
	[ShowIf( nameof( ShaderDomain ), ShaderDomain.Surface )]
	public bool OpaqueBlend { get; set; } = true;

	[TabPage( "General" ), Group( "Supported Blend Modes" )]
	[ShowIf( nameof( ShaderDomain ), ShaderDomain.Surface )]
	public bool MaskedBlend { get; set; } = true;

	[TabPage( "General" ), Group( "Supported Blend Modes" )]
	[ShowIf( nameof( ShaderDomain ), ShaderDomain.Surface )]
	public bool TranslucentBlend { get; set; } = true;

	[TabPage( "Code" ), TextArea]
	public string Code { get; set; } = ShaderTemplateSurface.Code;

	/// <summary>
	/// Check the template for any errors.
	/// </summary>
	/// <param name="path">Path to the ShaderTemplate resource</param>
	/// <param name="errors">Issues found with this template</param>
	public bool Validate( string path, out IEnumerable<string> errors )
	{
		if ( string.IsNullOrWhiteSpace( Code ) )
		{
			errors = ["Shader Template has no code!!!"];
			return false;
		}

		var templateErrors = new List<string>();

		foreach ( var tag in ShaderTemplate.TemplateTagMap )
		{
			if ( !Code.Contains( tag.Key ) )
			{
				templateErrors.Add( $"Shader Template \"{path}\" missing tag '{tag.Key}'" );

				continue;
			}

			// Detect duplicate tags
			var firstIndex = Code.IndexOf( tag.Key );

			if ( firstIndex != Code.LastIndexOf( tag.Key ) && firstIndex != -1 )
			{
				templateErrors.Add( $"Shader Template \"{path}\" contains more than 1 '{tag.Key}' tag" );
			}
		}

		errors = templateErrors;

		if ( errors.Any() )
		{
			return false;
		}

		return true;
	}
}