<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>
// ------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//     Runtime Version: 17.0.0.0
//  
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
// ------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Numerics;
using System.Text;

using SharpGLTF.Memory;

using ENCODING = SharpGLTF.Schema2.EncodingType;

<#

void WriteVertexMaterialPermutation(int colorCount, int textureCount)
{
    string vertexTypeName = "Vertex";
    if (colorCount > 0) vertexTypeName += $"Color{colorCount}";
    if (textureCount > 0) vertexTypeName += $"Texture{textureCount}";    

    var colorsSet = new List<string>();    
    if (colorCount == 1) colorsSet.Add("Color");    
    else if (colorCount > 1) { for(int i=0; i < colorCount; ++i) colorsSet.Add($"Color{i}"); }    

    var texcrdSet = new List<string>();
    if (textureCount == 1) texcrdSet.Add("TexCoord");
    else if (textureCount > 1) { for(int i=0; i < textureCount; ++i) texcrdSet.Add($"TexCoord{i}"); }       

    var currentSet = new List<(string Type, string Arg, string Param)>();
    foreach(var cset in colorsSet) currentSet.Add(("Vector4", cset.ToLower(), $"{cset}"));
    foreach(var tset in texcrdSet) currentSet.Add(("Vector2", tset.ToLower(), $"{tset}"));

    var attributeArgs = string.Join(", ", currentSet.Select(item => $"{item.Type} {item.Arg}") );    
    var attributeParams = string.Join(", ", currentSet.Select(item => item.Param));

    
    
    WriteLine("/// <summary>");
    WriteLine("/// Defines a Vertex attribute with a material Color.");
    WriteLine("/// </summary>");
    // WriteLine("#if NET6_0_OR_GREATER");
    // WriteLine("[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicFields)]");
    // WriteLine("#endif");
    WriteLine("[System.CodeDom.Compiler.GeneratedCodeAttribute(\"Microsoft.VisualStudio.TextTemplating\", \"17.0.0.0\")]");
    WriteLine("[System.Diagnostics.DebuggerDisplay(\"{_GetDebuggerDisplay(),nq}\")]");
    WriteLine("[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]");

    WriteLine($"public partial struct {vertexTypeName} : IVertexMaterial, IEquatable<{vertexTypeName}>");   
    WriteLine("{"); PushIndent("    ");

    // diagnostics

    WriteLine("#region diagnostics"); WriteLine("");

    WriteLine("private readonly string _GetDebuggerDisplay() => VertexUtils._GetDebuggerDisplay(this);");

    WriteLine("#endregion"); WriteLine("");

    // constructors

    WriteLine("#region constructors"); WriteLine("");

    WriteLine($"public {vertexTypeName}({attributeArgs})");      
    WriteLine("{"); PushIndent("    ");
    foreach (var item in currentSet) { WriteLine($"{item.Param} = {item.Arg};"); }
    PopIndent(); WriteLine("}");    

    WriteLine($"public {vertexTypeName}(IVertexMaterial src)");   
    WriteLine("{"); PushIndent("    ");
    WriteLine("Guard.NotNull(src, nameof(src));");
    for (int i=0; i < colorCount; ++i)   { WriteLine($"this.{colorsSet[i]} = {i} < src.MaxColors  ? src.GetColor({i}) : Vector4.One;"); }
    for (int i=0; i < textureCount; ++i) { WriteLine($"this.{texcrdSet[i]} = {i} < src.MaxTextCoords ? src.GetTexCoord({i}) : Vector2.Zero;"); }
    PopIndent(); WriteLine("}");

    WriteLine("#endregion"); WriteLine("");
    
    // data       
    
    WriteLine("#region data"); WriteLine("");

    for(int i=0; i < colorCount; ++i)
    {
        // WriteLine($"[VertexAttribute(\"COLOR_{i}\", ENCODING.UNSIGNED_BYTE, true)]");
        WriteLine($"public Vector4 {colorsSet[i]};");
        // WriteLine("");
    }

    for(int i=0; i < textureCount; ++i)
    {
        // WriteLine($"[VertexAttribute(\"TEXCOORD_{i}\")]");
        WriteLine($"public Vector2 {texcrdSet[i]};");
        // WriteLine("");
    }

    WriteLine("");

    WriteLine("IEnumerable<KeyValuePair<string, AttributeFormat>> IVertexReflection.GetEncodingAttributes()");
    WriteLine("{"); PushIndent("    ");
    for (int i=0; i < colorCount; ++i)   { var name = "COLOR_" + i; WriteLine($"yield return new KeyValuePair<string, AttributeFormat>(\"{name}\", new AttributeFormat(Schema2.DimensionType.VEC4, ENCODING.UNSIGNED_BYTE, true));"); }
    for (int i=0; i < textureCount; ++i) { var name = "TEXCOORD_" + i; WriteLine($"yield return new KeyValuePair<string, AttributeFormat>(\"{name}\", new AttributeFormat(Schema2.DimensionType.VEC2));"); }
    PopIndent(); WriteLine("}");

    WriteLine($"public readonly int MaxColors => {colorCount};");
    WriteLine("");

    WriteLine($"public readonly int MaxTextCoords => {textureCount};");
    WriteLine("");

    WriteLine($"public readonly override int GetHashCode()");
    WriteLine("{"); PushIndent("    ");
    WriteLine("#if !NETSTANDARD2_0");
    WriteLine($"return HashCode.Combine({attributeParams});");
    WriteLine("#else");
    var hashParams = string.Join("+", currentSet.Select(item => $"{item.Param}.GetHashCode()"));
    WriteLine($"return {hashParams};");
    WriteLine("#endif");
    PopIndent(); WriteLine("}");
    WriteLine("");

    WriteLine($"public readonly override bool Equals(Object obj) {{ return obj is {vertexTypeName} other ? Equals(other) : false; }}");
    WriteLine("");

    WriteLine($"public readonly bool Equals({vertexTypeName} other) {{ return AreEqual(this, other); }}");
    WriteLine("");

    WriteLine($"public static bool operator ==(in {vertexTypeName} a, in {vertexTypeName} b) {{ return AreEqual(a, b); }}");
    WriteLine("");

    WriteLine($"public static bool operator !=(in {vertexTypeName} a, in {vertexTypeName} b) {{ return !AreEqual(a, b); }}");
    WriteLine("");

    WriteLine($"public static bool AreEqual(in {vertexTypeName} a, in {vertexTypeName} b)");
    WriteLine("{"); PushIndent("    ");
    foreach(var attr in currentSet) { WriteLine($"if (a.{attr.Param} != b.{attr.Param}) return false;"); }    
    WriteLine("return true;");
    PopIndent(); WriteLine("}");
    WriteLine("");

    WriteLine("#endregion"); WriteLine("");

    // API    

    WriteLine("#region API"); WriteLine("");

    WriteLine("public readonly VertexMaterialDelta Subtract(IVertexMaterial baseValue)");
    WriteLine("{"); PushIndent("    ");
    WriteLine($"return new VertexMaterialDelta(({vertexTypeName})baseValue, this);");
    PopIndent(); WriteLine("}");

    WriteLine("public void Add(in VertexMaterialDelta delta)");
    WriteLine("{"); PushIndent("    ");    

    foreach(var attr in currentSet)    
    {
        var src = $"{attr.Param}Delta";
        if (src == "ColorDelta") src= "Color0Delta";
        if (src == "TexCoordDelta") src= "TexCoord0Delta";
        WriteLine($"this.{attr.Param} += delta.{src};");
    }    
    PopIndent(); WriteLine("}");


    WriteLine("void IVertexMaterial.SetColor(int index, Vector4 color)");
    WriteLine("{"); PushIndent("    ");
    for(int i=0; i < colorCount; ++i) { WriteLine($"if (index == {i}) this.{colorsSet[i]} = color;"); }        
    PopIndent(); WriteLine("}");

    WriteLine("void IVertexMaterial.SetTexCoord(int index, Vector2 coord)");
    WriteLine("{"); PushIndent("    ");
    for(int i=0; i < textureCount; ++i) { WriteLine($"if (index == {i}) this.{texcrdSet[i]} = coord;"); }    
    PopIndent(); WriteLine("}");

    WriteLine("public readonly Vector4 GetColor(int index)");
    WriteLine("{"); PushIndent("    ");
    WriteLine("switch (index)");
    WriteLine("{"); PushIndent("    ");
    for(int i=0; i < colorCount; ++i) { WriteLine($"case {i}: return this.{colorsSet[i]};"); }        
    WriteLine("default: throw new ArgumentOutOfRangeException(nameof(index));");
    PopIndent(); WriteLine("}");
    PopIndent(); WriteLine("}");

    WriteLine("public readonly Vector2 GetTexCoord(int index)");
    WriteLine("{"); PushIndent("    ");
    WriteLine("switch (index)");
    WriteLine("{"); PushIndent("    ");
    for(int i=0; i < textureCount; ++i) { WriteLine($"case {i}: return this.{texcrdSet[i]};"); }    
    WriteLine("default: throw new ArgumentOutOfRangeException(nameof(index));");
    PopIndent(); WriteLine("}");
    PopIndent(); WriteLine("}");

    WriteLine("#endregion"); WriteLine("");

    // closing

    PopIndent(); WriteLine("}");

    WriteLine("");
    WriteLine("");    
}

void WriteVertexSubtractPermutation(int colorCount, int textureCount)
{
    string vertexTypeName = "Vertex";
    if (colorCount > 0) vertexTypeName += $"Color{colorCount}";
    if (textureCount > 0) vertexTypeName += $"Texture{textureCount}";  
    
    WriteLine($"internal VertexMaterialDelta(in {vertexTypeName} rootVal, in {vertexTypeName} morphVal)");
    WriteLine("{"); PushIndent("    ");

    
    colorCount = Math.Min(2,colorCount);
    textureCount = Math.Min(4,textureCount);

    WriteLine($"MaxColors = {colorCount};");
    WriteLine($"MaxTextCoords = {textureCount};");

    for(int i=0; i< 2; ++i)
    {
        var name = colorCount == 1 ? "Color" : $"Color{i}";

        if (colorCount > i) WriteLine($"Color{i}Delta = morphVal.{name} - rootVal.{name};");
        else WriteLine($"Color{i}Delta = Vector4.Zero;");    
    }

    for(int i=0; i < 4; ++i)
    {
        var name = textureCount == 1 ? "TexCoord" : $"TexCoord{i}";

        if (textureCount > i) WriteLine($"TexCoord{i}Delta = morphVal.{name} - rootVal.{name};");
        else WriteLine($"TexCoord{i}Delta = Vector2.Zero;");    
    }

    PopIndent(); WriteLine("}");
}

#>


namespace SharpGLTF.Geometry.VertexTypes
{
    <# PushIndent("     "); #>
    
    <# WriteVertexMaterialPermutation(0, 1); #>
    <# WriteVertexMaterialPermutation(0, 2); #>
    <# WriteVertexMaterialPermutation(0, 3); #>
    <# WriteVertexMaterialPermutation(0, 4); #>

    <# WriteVertexMaterialPermutation(1, 0); #>
    <# WriteVertexMaterialPermutation(1, 1); #>
    <# WriteVertexMaterialPermutation(1, 2); #>
    <# WriteVertexMaterialPermutation(1, 3); #>
    <# WriteVertexMaterialPermutation(1, 4); #>

    <# WriteVertexMaterialPermutation(2, 0); #>
    <# WriteVertexMaterialPermutation(2, 1); #>
    <# WriteVertexMaterialPermutation(2, 2); #>
    <# WriteVertexMaterialPermutation(2, 3); #>
    <# WriteVertexMaterialPermutation(2, 4); #>

    <# PopIndent(); #>

    partial struct VertexMaterialDelta
    {

<# PushIndent("         "); #>

<# WriteVertexSubtractPermutation(0, 1); #>
<# WriteVertexSubtractPermutation(0, 2); #>
<# WriteVertexSubtractPermutation(0, 3); #>
<# WriteVertexSubtractPermutation(0, 4); #>

<# WriteVertexSubtractPermutation(1, 0); #>
<# WriteVertexSubtractPermutation(1, 1); #>
<# WriteVertexSubtractPermutation(1, 2); #>
<# WriteVertexSubtractPermutation(1, 3); #>
<# WriteVertexSubtractPermutation(1, 4); #>

<# WriteVertexSubtractPermutation(2, 0); #>
<# WriteVertexSubtractPermutation(2, 1); #>
<# WriteVertexSubtractPermutation(2, 2); #>
<# WriteVertexSubtractPermutation(2, 3); #>
<# WriteVertexSubtractPermutation(2, 4); #>

<# ClearIndent(); #>

    }

    
}