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);
}