<#@ template hostspecific="true" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="EnvDTE" #>
<#@ assembly name="System.Windows.Forms" #>
<#@ import namespace="System.Collections" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.ComponentModel.Design" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Resources" #>
<#@ import namespace="System.Text.RegularExpressions" #>
<#@ import namespace="EnvDTE" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="Microsoft.VisualStudio.TextTemplating" #>

<#
    var model = LoadResources();
#>
// <auto-generated />

using System;
using System.Reflection;
using System.Resources;
using System.Globalization;
<#
    if (!model.NoDiagnostics)
    {
#>
using System.Threading;
<#
    }
#>

namespace <#= model.Namespace #>
{
<#
    if (model.Namespace.EndsWith("Internal")
        || model.AccessModifier == "internal")
    {
#>
    /// <summary>
    ///     This is an internal API that supports the FreeSql infrastructure and not subject to
    ///     the same compatibility standards as public APIs. It may be changed or removed without notice in
    ///     any release. You should only use it directly in your code with extreme caution and knowing that
    ///     doing so can result in application failures when updating to a new FreeSql release.
    /// </summary>
<#
    }
    else
    {
#>
    /// <summary>
    ///     <para>
    ///		    String resources used in FreeSql exceptions, etc.
    ///     </para>
    ///     <para>
    ///		    These strings are exposed publicly for use by database providers and extensions.
    ///         It is unusual for application code to need these strings.
    ///     </para>
    /// </summary>
<#
    }
#>
    <#= model.AccessModifier #> static class <#= model.Class #>
    {
        private static readonly ResourceManager _resourceManager = new ResourceManager("<#= model.ResourceName #>", typeof(<#= model.Class #>).Assembly);
		
        private static CultureInfo _resourceCulture;
		
        /// <summary>
        ///   重写当前线程的 CurrentUICulture 属性，对
        ///   使用此强类型资源类的所有资源查找执行重写。
        /// </summary>
        public static CultureInfo Culture
        {
            get
            {
                return _resourceCulture;
            }
            set
            {
                _resourceCulture = value;
            }
        }
<#
    foreach (var resource in model.Resources)
    {
#>

        /// <summary>
<#
        foreach (var line in Lines(resource.Value))
        {
#>
        /// <#= Xml(line) #>
<#
        }
#>
        /// </summary>
<#
            if (resource.Obsolete)
            {
#>
        [Obsolete]
<#
            }

            if (resource.Parameters.Any())
            {
#>
        public static string <#= resource.Name #>(<#= List("object ", resource.Parameters.Select(e => e.ParamString)) #>)
            => string.Format(
                GetString("<#= resource.Name #>", <#= List(resource.Parameters.Select(e => e.NameOfString)) #>),
                <#= List(resource.Parameters.Select(e => e.ParamString)) #>);
<#
            }
            else
            {
#>
        public static string <#= resource.Name #>
            => GetString("<#= resource.Name #>");
<#
            }
        }
#>

        private static string GetString(string name, params string[] formatterNames)
        {
            var value = _resourceManager.GetString(name,_resourceCulture);
            for (var i = 0; i < formatterNames.Length; i++)
            {
                value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}");
            }

            return value;
        }
    }
}

<#+
    ResourceFile LoadResources()
    {
        var result = new ResourceFile();

        if (Session.ContainsKey("AccessModifier"))
        {
            result.AccessModifier = (string)Session["AccessModifier"];
        };

        var services = (IServiceProvider)Host;
        var dte = (DTE)services.GetCOMService(typeof(DTE));
        if (!Session.TryGetValue("NoDiagnostics", out var noDiagnostics))
        {
            noDiagnostics = false;
        }

        result.NoDiagnostics = (bool)noDiagnostics;

        var resourceFile = (string)Session["ResourceFile"];
        if (!Path.IsPathRooted(resourceFile))
        {
            resourceFile = Host.ResolvePath(resourceFile);
        }

        var resourceProjectItem = dte.Solution.FindProjectItem(resourceFile);
        var templateProjectItem = dte.Solution.FindProjectItem(Host.TemplateFile);
        var project = templateProjectItem.ContainingProject;
        var rootNamespace = (string)project.Properties.Item("RootNamespace").Value;
        var resourceDir = Path.GetDirectoryName(resourceFile);
        var projectDir = (string)project.Properties.Item("FullPath").Value;
        var resourceNamespace = rootNamespace + "." + resourceDir.Substring(projectDir.Length)
            .Replace(Path.DirectorySeparatorChar, '.');

        result.Namespace = (string)resourceProjectItem.Properties.Item("CustomToolNamespace")?.Value;
        if (string.IsNullOrEmpty(result.Namespace))
        {
            result.Namespace = resourceNamespace;
        }

        result.Class = Path.GetFileNameWithoutExtension(resourceFile);

        result.ResourceName = resourceNamespace + "." + result.Class;
		if(File.Exists(resourceFile.Replace(".resx", ".zh-Hans.resx")))
        {
            resourceFile = resourceFile.Replace(".resx", ".zh-Hans.resx");
        }
        List<ResXDataNode> sortedResources;
        using (var reader = new ResXResourceReader(resourceFile))
        {
            reader.UseResXDataNodes = true;
            sortedResources = Enumerable.ToList(
                from DictionaryEntry r in reader
                orderby r.Key
                select (ResXDataNode)r.Value);

            result.Resources = sortedResources
                .Select(r => new Resource(r))
                .ToList();
        }

        using (var writer = new ResXResourceWriter(resourceFile))
        {
            foreach (var node in sortedResources)
                writer.AddResource(node);

            writer.Generate();
        }

        return result;
    }

    IEnumerable<string> Lines(string value)
        => value.Split(new[] { Environment.NewLine }, StringSplitOptions.None);

    string Xml(string value)
        => value.Replace("<", "&lt;").Replace(">", "&gt;");

    string List(IEnumerable<string> items)
        => List(null, items);

    string List(string prefix, IEnumerable<string> items, string suffix = null)
        => string.Join(", ", items.Select(i => prefix + i + suffix));

    class ResourceFile
    {
        public string Namespace { get; set; }
        public string AccessModifier { get; set; } = "public";
        public string Class { get; set; }
        public string ResourceName { get; set; }
        public IEnumerable<Resource> Resources { get; set; }
        public bool NoDiagnostics { get; set; }
    }

    class Resource
    {
        public Resource(ResXDataNode node)
        {
            Name = node.Name;
            Value = (string)node.GetValue((ITypeResolutionService)null);
            var parameters = Regex.Matches(Value, @"\{(\w+)\}")
                .Cast<Match>()
                .Select(m => m.Groups[1].Value)
                .Distinct()
                .Select(n => ("nameof(" + n + ")", n))
                .ToList();

            foreach (var parameter in parameters.ToList())
            {
                var rawString = parameter.Item2;
                var underscoreIndex = rawString.IndexOf('_');
                if (underscoreIndex > 0)
                {
                    var newIndex = int.Parse(rawString.Substring(0, underscoreIndex));
                    parameters[newIndex] = ("\"" + rawString + "\"", rawString.Substring(underscoreIndex + 1));
                }
            }

            Parameters = parameters;

            var eventInfo = node.Comment.Split(' ');
            var argumentsRead = 0;
            if (eventInfo.FirstOrDefault() == "Obsolete")
            {
                Obsolete = true;
                argumentsRead++;
            }

            Level = eventInfo.Skip(argumentsRead++).FirstOrDefault() ?? "BadLevel";
            EventId = eventInfo.Skip(argumentsRead++).FirstOrDefault() ?? "BadEventId";
            Types = eventInfo.Skip(argumentsRead++).ToList();
        }

        public string Name { get; }
        public string Value { get; }
        public string EventId { get; }
        public string Level { get; }
        public bool Obsolete { get; }
        public IEnumerable<(string NameOfString, string ParamString)> Parameters { get; }
        public IEnumerable<string> Types { get; }
    }
#>