<#/*THIS IS A T4 FILE - see t4_text_templating.md for what it is and how to run codegen*/#>
<#@ template debug="True" #>
<#@ output extension=".gen.cs" encoding="utf-8" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Collections" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Reflection" #>

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//
//     TextTransform Samples/Packages/com.unity.collections/Unity.Collections/FixedStringFormat.tt
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

using System;
using System.Runtime.InteropServices;
using Unity.Collections.LowLevel.Unsafe;
using UnityEngine.Internal;

<#
List<List<T>> AllCombinationsOf<T>(List<List<T>> sets)
{
    // need array bounds checking etc for production
    var combinations = new List<List<T>>();

    // prime the data
    foreach (var value in sets[0])
        combinations.Add(new List<T> { value });

    foreach (var set in sets.Skip(1))
        combinations = AddExtraSet(combinations, set);

    return combinations;
}

List<List<T>> AddExtraSet<T>(List<List<T>> combinations, List<T> set)
{
    var newCombinations = from value in set
                          from combination in combinations
                          select new List<T>(combination) { value };

    return newCombinations.ToList();
}

string WithCommas(IEnumerable<string> input)
{
    return string.Join(", ", input);
}

var ARGTYPES = new[] { "int", "float", "string", "FixedStringN", null };
var ARGCOUNT = 4;

var ARGSETS = new List<List<string>>();
for (int i = 0; i < ARGCOUNT; ++i)
    ARGSETS.Add(ARGTYPES.ToList());

var ARGCOMBOS = AllCombinationsOf(ARGSETS);
#>

namespace Unity.Collections
{
    /// <summary>
    /// Provides formatting methods for FixedString*N*.
    /// </summary>
    [BurstCompatible]
    public static class FixedString
    {
<#
    foreach (var COMBO in ARGCOMBOS)
    {
        while (COMBO.Count != 0 && COMBO.Last() == null)
            COMBO.RemoveAt(COMBO.Count - 1);
        if (COMBO.Count == 0 || COMBO.IndexOf(null) != -1)
            continue;

        var numFixedStringN = COMBO.Count((s) => s == "FixedStringN");

        // turn FixedStringN into T1..Tn
        var GENERICSPARAM = "";
        var BCOMPAT = "";

        if (COMBO.Contains("string"))
        {
            BCOMPAT = "[NotBurstCompatible]";
        }

        var GENERICSCONSTRAINT = new StringBuilder();
        var TxDOCS = new StringBuilder();
        if (numFixedStringN > 0)
        {
            GENERICSPARAM = $"<{string.Join(",", Enumerable.Range(1, numFixedStringN).Select((n) => $"T{n}"))}>";
            if (!COMBO.Contains("string"))
            {
                BCOMPAT = $"[BurstCompatible(GenericTypeArguments = new[] {{ {string.Join(", ", Enumerable.Range(1, numFixedStringN).Select((n) => $"typeof(FixedString32Bytes /*$T{n}*/)"))} }} )]";
            }

            for (int i = 0; i < numFixedStringN; ++i)
            {
                var index = COMBO.IndexOf("FixedStringN");
                COMBO[index] = $"T{i + 1}";
                GENERICSCONSTRAINT.Append($"            where T{i + 1} : struct, INativeList<byte>, IUTF8Bytes\n");
                TxDOCS.Append($"        /// <typeparam name=\"T{i + 1}\"><undoc /></typeparam>\r\n");
            }
        }

        var ARGS = Enumerable.Range(0, COMBO.Count).Select((n) => $"{COMBO[n]} arg{n}");
        var ARGNAMES = Enumerable.Range(0, COMBO.Count).Select((n) => $"arg{n}").ToList();
        var CONVERSIONS = new StringBuilder();

        for (int i = 0; i < COMBO.Count; ++i)
        {
            if (COMBO[i].StartsWith("T"))
                continue;
            CONVERSIONS.Append($"            FixedString32Bytes carg{i} = default; carg{i}.Append(arg{i});\n");
            ARGNAMES[i] = $"carg{i}";
        }

        var RETURNTYPE = "FixedString128Bytes";
        if (COMBO.Count > 3)
            RETURNTYPE = "FixedString512Bytes";

        var ARGxDOCS = String.Join("\r\n        /// ", Enumerable.Range(0, COMBO.Count).Select(n => $"<param name=\"arg{n}\">Value to interpolate into the format string.</param>"));
#>

        /// <summary>
        /// Returns a new string produced by interpolating a format string.
        /// </summary>
        /// <remarks>
        /// Similar to StringBuilder.AppendFormat but with significant limitations:
        /// - Only supports numeric format placeholders of the form `{0}` .. `{N}`.
        /// - No format modifiers (*e.g.* `{0:x}`) are supported.
        ///
        /// The various overloads of this method take up to four values for interpolation. The allowed argument types are:
        /// - FixedString*N*Bytes
        /// - string
        /// - int
        /// - float
        /// - structs implementing INativeList&lt;byte&gt; and IUTF8Bytes
        ///
        /// <seealso cref="FixedStringMethods.AppendFormat"/>
        /// </remarks>
        /// <param name="formatString">The format string.</param>
<#=TxDOCS#>
        /// <#=ARGxDOCS#>
        /// <returns>A new string produced by interpolating the format string.</returns>
        <#=BCOMPAT#>
        public static <#=RETURNTYPE#> Format<#=GENERICSPARAM#>(<#=RETURNTYPE#> formatString, <#=WithCommas(ARGS)#>)
<#=GENERICSCONSTRAINT#>
        {
            <#=RETURNTYPE#> result = default;
<#=CONVERSIONS#>

            result.AppendFormat(formatString, <#=WithCommas(ARGNAMES)#>);
            return result;
        }
<#
    }
#>

    }
}