using System.CodeDom.Compiler;
using System.IO;

namespace Spectre.Console.SourceGenerator.Spinners;

/// <summary>
/// Emits C# source code for spinners.
/// </summary>
internal static class SpinnerEmitter
{
    private const string AutoGeneratedHeader = """
        //------------------------------------------------------------------------------
        // <auto-generated>
        //     This code was generated by a tool.
        //
        //     Partly generated from
        //     https://github.com/sindresorhus/cli-spinners/blob/master/spinners.json
        //
        //     Changes to this file may cause incorrect behavior and will be lost if
        //     the code is regenerated.
        // </auto-generated>
        //------------------------------------------------------------------------------

        """;

    /// <summary>
    /// Emits the Spinner.Generated.g.cs file content.
    /// </summary>
    public static string Emit(EquatableArray<SpinnerModel> spinners)
    {
        using var stringWriter = new StringWriter();
        using var writer = new IndentedTextWriter(stringWriter, "    ");

        writer.WriteLine(AutoGeneratedHeader);
        writer.WriteLine("using System;");
        writer.WriteLine("using System.Collections.Generic;");
        writer.WriteLine();
        writer.WriteLine("namespace Spectre.Console");
        writer.WriteLine("{");
        writer.Indent++;

        writer.WriteLine("public abstract partial class Spinner");
        writer.WriteLine("{");
        writer.Indent++;

        // Emit private spinner classes
        foreach (var spinner in spinners)
        {
            EmitSpinnerClass(writer, spinner);
        }

        // Emit Known class
        writer.WriteLine("/// <summary>");
        writer.WriteLine("/// Contains well-known spinners.");
        writer.WriteLine("/// </summary>");
        writer.WriteLine("public static class Known");
        writer.WriteLine("{");
        writer.Indent++;

        foreach (var spinner in spinners)
        {
            writer.WriteLine("/// <summary>");
            writer.WriteLine($"/// Gets the \"{spinner.Name}\" spinner.");
            writer.WriteLine("/// </summary>");
            writer.WriteLine($"public static Spinner {spinner.NormalizedName} {{ get; }} = new {spinner.NormalizedName}Spinner();");
        }

        writer.Indent--;
        writer.WriteLine("}");

        writer.Indent--;
        writer.WriteLine("}");
        writer.Indent--;
        writer.WriteLine("}");

        return stringWriter.ToString();
    }

    private static void EmitSpinnerClass(IndentedTextWriter writer, SpinnerModel spinner)
    {
        writer.WriteLine($"private sealed class {spinner.NormalizedName}Spinner : Spinner");
        writer.WriteLine("{");
        writer.Indent++;

        writer.WriteLine($"public override TimeSpan Interval => TimeSpan.FromMilliseconds({spinner.Interval});");
        writer.WriteLine($"public override bool IsUnicode => {(spinner.IsUnicode ? "true" : "false")};");
        writer.WriteLine("public override IReadOnlyList<string> Frames => new List<string>");
        writer.WriteLine("{");
        writer.Indent++;

        foreach (var frame in spinner.Frames)
        {
            writer.WriteLine($"\"{frame}\",");
        }

        writer.Indent--;
        writer.WriteLine("};");

        writer.Indent--;
        writer.WriteLine("}");
    }
}
