namespace InnovEnergy.Lib.Utils;

// public class TextBlock
// {
//     public static TextBlock Empty { get; } = new TextBlock(Array.Empty<String>());
//
//     public IReadOnlyList<String> Lines { get; }
//
//     public Int32 Width => Lines.Count == 0 
//                         ? 0
//                         : Lines.Max(l=>l.Length);
//
//     public Int32 Height => Lines.Count;
//
//     public TextBlock(IReadOnlyList<String> lines) => Lines = lines;
//
//     public TextBlock AlignLeft   (Int32 width = -1) => AlignHorizontal((l, w) => l.PadRight(w), width);
//     public TextBlock AlignRight  (Int32 width = -1) => AlignHorizontal((l, w) => l.PadLeft(w), width);
//     public TextBlock AlignHCenter(Int32 width = -1) => AlignHorizontal((l, w) => l.PadLeft((w + l.Length) / 2).PadRight(w), width);
//
//     public TextBlock AlignTop    (Int32 height) => Lines.Concat(EmptyLines(height)).Take(height).ToArray(height);
//     public TextBlock AlignBottom (Int32 height) => EmptyLines(Lines.Count - height).Concat(Lines).Take(height).ToArray(height);
//     //public TextBlock AlignVCenter(Int32 height) => EmptyLines(height/2).Concat(Lines).Take(height).ToArray(height);
//     //public TextBlock AlignVCenter(Int32 height) => AlignHorizontal((l, w) => l.PadLeft((w + l.Length) / 2).PadRight(w), width);
//     
//     private static IEnumerable<String> EmptyLines(Int32 height)
//     {
//         return height > 0
//              ? Enumerable.Repeat("", height)
//              : Enumerable.Empty<String>();
//     }
//
//
//     public static TextBlock HSpace(Int32 width ) => new TextBlock(new []{"".PadRight(width)});
//     public static TextBlock VSpace(Int32 height) => new TextBlock(Enumerable.Repeat("", height).ToArray(height));
//
//     public static TextBlock Space(Int32 width, Int32 height)
//     {
//         var lines = Enumerable
//                    .Repeat("".PadRight(width), height)
//                    .ToArray(height);
//         
//         return new TextBlock(lines);
//     }
//
//     private TextBlock AlignHorizontal(Func<String, Int32, String> alignLine, Int32 width = -1)
//     {
//         if (!Lines.Any())
//             return Empty;
//
//         var strings = Lines
//                      .SelectMany(GetLines)
//                      .ToList();
//
//         width = width < 0 ? strings.Max(l => l.Length) : width;
//         
//         var aligned = strings
//                      .Select(l => l.Length > width ? l.Substring(0, width) : l)
//                      .Select(l => alignLine(l, width))
//                      .ToArray(strings.Count);
//         
//         return new TextBlock(aligned);
//     }
//     
//
//     private static IReadOnlyList<String> GetLines(Object l)
//     {
//         return l is TextBlock tb 
//                ? tb.Lines
//                : l.ToString()?.SplitLines() ?? new[] { "<null>" };
//     }
//
//     public override String ToString() => String.Join(Environment.NewLine, Lines);
//
//     public static implicit operator TextBlock(String[] lines) => new TextBlock(lines);
// }


public class TextBlock
{
    private readonly IReadOnlyList<String> _Lines;

    private TextBlock(params String[] lines) => _Lines = lines;

    public override String ToString() => _Lines.JoinLines();


    public static TextBlock AlignLeft(params Object[] things)
    {
        var lines = things
                   .SelectMany(GetLines)
                   .ToList();

        var width = lines.Max(l => l.Length);

        var alignedLines = lines.Select(l => l.PadRight(width)).ToArray(lines.Count);
        
        return new TextBlock(alignedLines);
    }

    public static TextBlock AlignRight(params Object[] things)
    {
        var lines = things
                   .SelectMany(GetLines)
                   .ToList();

        var width = lines.Max(l => l.Length);

        var alignedLines = lines.Select(l => l.PadLeft(width)).ToArray(lines.Count);
        
        return new TextBlock(alignedLines);
    }

    public static TextBlock CenterHorizontal(params Object[] things)
    {
        var lines = things
                   .SelectMany(GetLines)
                   .ToList();
        
        var width = lines.Max(l => l.Length);

        var alignedLines = lines
                          .Select(l => l.PadLeft((width + l.Length) / 2).PadRight(width))
                          .ToArray(lines.Count);
        
        return new TextBlock(alignedLines);
    }


    public static TextBlock AlignTop(params Object[] things)
    {
        var columns = things
                     .Select(GetLines)
                     .ToList();
        
        var height = columns.Max(l => l.Count);

        var alignedLines = Enumerable
                          .Range(0, height)
                          .Select(l => columns.Select(c => c.ElementAtOr(l, Space(c[0].Length))).Join())
                          .ToArray(height);
        
        return new TextBlock(alignedLines);
    }
    
    
    public static TextBlock AlignBottom(params Object[] things)
    {
        var columns = things
                     .Select(GetLines)
                     .ToList();
        
        var height = columns.Max(l => l.Count);

        var alignedLines = Enumerable
                          .Range(0, height)
                          .Select(l => columns.Select(c => c.ElementAtOr(c.Count - height + l, Space(c[0].Length))).Join())
                          .ToArray(height);
        
        return new TextBlock(alignedLines);
    }
    
  
    public static TextBlock CenterVertical(params Object[] things)
    {
        var columns = things
                     .Select(GetLines)
                     .ToList();
        
        var height = columns.Max(l => l.Count);

        var alignedLines = Enumerable
                          .Range(0, height)
                          .Select(l => columns.Select(c => c.ElementAtOr((c.Count - height) / 2 + l, Space(c[0].Length))).Join())
                          .ToArray(height);
        
        return new TextBlock(alignedLines);
    }
    
    
    
    public TextBlock Box()
    {
        var width = _Lines.Max(l => l.Length);

        var hLine  = "".PadRight(width + 2, '─');
        var top    = "┌" + hLine + "┐";
        var bottom = "└" + hLine + "┘";

        var lines = _Lines
                    .Select(l => l.SurroundWith(" "))
                    .Select(l => l.SurroundWith("│"))
                    .Prepend(top)
                    .Append(bottom)
                    .ToArray(_Lines.Count + 2);
        
        return new TextBlock(lines);
    }
    
    public TextBlock TitleBox(String title)
    {
        var linesWidth = _Lines.Max(l => l.Length);
        var titleWidth = title.Length;

        var width = Math.Max(linesWidth, titleWidth);
        
        var hLine   = "".PadRight(width + 2, '─');
        var top     = "┌" + hLine + "┐";
        var between = "├" + hLine + "┤";
        var bottom  = "└" + hLine + "┘";

        var centeredTitle = "│ " + title.PadLeft((width + title.Length) / 2).PadRight(width) + " │";

        var lines = _Lines
                    .Select(l => l.PadRight(width))
                    .Select(l => l.SurroundWith(" "))
                    .Select(l => l.SurroundWith("│"))
                    .Prepend(between)
                    .Prepend(centeredTitle)
                    .Prepend(top)
                    .Append(bottom)
                    .ToArray();
        
        return new TextBlock(lines);
    }


    
    
    public static TextBlock Spacer(Int32 n)
    {
        return new TextBlock(Enumerable.Repeat("".PadRight(n), n).ToArray(n));
    }
    
    public static TextBlock HorizontalSpace(Int32 n)
    {
        return new TextBlock("".PadRight(n));
    }
    
    public static TextBlock VerticalSpace(Int32 n)
    {
        return new TextBlock(Enumerable.Repeat("", n).ToArray(n));
    }


    private static IReadOnlyList<String> GetLines(Object t)
    {
        return t is TextBlock tb
             ? tb._Lines
             : t.ToString()!.SplitLines();
    }

    private static String Space(Int32 totalWidth)
    {
        return "".PadRight(totalWidth);
    }
}