using Xunit.Abstractions;
using static Terminal.Gui.Application;

namespace Terminal.Gui.DialogTests;

public class DialogTests
{
    private readonly ITestOutputHelper _output;
    public DialogTests (ITestOutputHelper output) { _output = output; }

    [Fact]
    [AutoInitShutdown]
    public void Add_Button_Works ()
    {
        RunState runstate = null;

        var d = (FakeDriver)Driver;

        var title = "1234";
        var btn1Text = "yes";
        var btn1 = $"{CM.Glyphs.LeftBracket} {btn1Text} {CM.Glyphs.RightBracket}";
        var btn2Text = "no";
        var btn2 = $"{CM.Glyphs.LeftBracket} {btn2Text} {CM.Glyphs.RightBracket}";

        // We test with one button first, but do this to get the width right for 2
        int width = $@"{CM.Glyphs.VLine} {btn1} {btn2} {CM.Glyphs.VLine}".Length;
        d.SetBufferSize (width, 1);

        // Default (center)
        var dlg = new Dialog
        {
            Title = title,
            Width = width,
            Height = 1,
            ButtonAlignment = Dialog.ButtonAlignments.Center,
            Buttons = [new Button { Text = btn1Text }]
        };

        // Create with no top or bottom border to simplify testing button layout (no need to account for title etc..)
        dlg.Border.Thickness = new Thickness (1, 0, 1, 0);
        runstate = Begin (dlg);
        var buttonRow = $"{CM.Glyphs.VLine}     {btn1}    {CM.Glyphs.VLine}";
        TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);

        // Now add a second button
        buttonRow = $"{CM.Glyphs.VLine} {btn1} {btn2} {CM.Glyphs.VLine}";
        dlg.AddButton (new Button { Text = btn2Text });
        var first = false;
        RunIteration (ref runstate, ref first);
        TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
        End (runstate);

        // Justify
        dlg = new Dialog
        {
            Title = title,
            Width = width,
            Height = 1,
            ButtonAlignment = Dialog.ButtonAlignments.Justify,
            Buttons = [new Button { Text = btn1Text }]
        };

        // Create with no top or bottom border to simplify testing button layout (no need to account for title etc..)
        dlg.Border.Thickness = new Thickness (1, 0, 1, 0);
        runstate = Begin (dlg);
        buttonRow = $"{CM.Glyphs.VLine}         {btn1}{CM.Glyphs.VLine}";
        TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);

        // Now add a second button
        buttonRow = $"{CM.Glyphs.VLine}{btn1}   {btn2}{CM.Glyphs.VLine}";
        dlg.AddButton (new Button { Text = btn2Text });
        first = false;
        RunIteration (ref runstate, ref first);
        TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
        End (runstate);

        // Right
        dlg = new Dialog
        {
            Title = title,
            Width = width,
            Height = 1,
            ButtonAlignment = Dialog.ButtonAlignments.Right,
            Buttons = [new Button { Text = btn1Text }]
        };

        // Create with no top or bottom border to simplify testing button layout (no need to account for title etc..)
        dlg.Border.Thickness = new Thickness (1, 0, 1, 0);
        runstate = Begin (dlg);
        buttonRow = $"{CM.Glyphs.VLine}{new string (' ', width - btn1.Length - 2)}{btn1}{CM.Glyphs.VLine}";
        TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);

        // Now add a second button
        buttonRow = $"{CM.Glyphs.VLine}  {btn1} {btn2}{CM.Glyphs.VLine}";
        dlg.AddButton (new Button { Text = btn2Text });
        first = false;
        RunIteration (ref runstate, ref first);
        TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
        End (runstate);

        // Left
        dlg = new Dialog
        {
            Title = title,
            Width = width,
            Height = 1,
            ButtonAlignment = Dialog.ButtonAlignments.Left,
            Buttons = [new Button { Text = btn1Text }]
        };

        // Create with no top or bottom border to simplify testing button layout (no need to account for title etc..)
        dlg.Border.Thickness = new Thickness (1, 0, 1, 0);
        runstate = Begin (dlg);
        buttonRow = $"{CM.Glyphs.VLine}{btn1}{new string (' ', width - btn1.Length - 2)}{CM.Glyphs.VLine}";
        TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);

        // Now add a second button
        buttonRow = $"{CM.Glyphs.VLine}{btn1} {btn2}  {CM.Glyphs.VLine}";
        dlg.AddButton (new Button { Text = btn2Text });
        first = false;
        RunIteration (ref runstate, ref first);
        TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
        End (runstate);
    }

    [Fact]
    [AutoInitShutdown]
    public void ButtonAlignment_Four ()
    {
        RunState runstate = null;

        var d = (FakeDriver)Driver;

        var title = "1234";

        // E.g "|[ yes ][ no ][ maybe ]|"
        var btn1Text = "yes";
        var btn1 = $"{CM.Glyphs.LeftBracket} {btn1Text} {CM.Glyphs.RightBracket}";
        var btn2Text = "no";
        var btn2 = $"{CM.Glyphs.LeftBracket} {btn2Text} {CM.Glyphs.RightBracket}";
        var btn3Text = "maybe";
        var btn3 = $"{CM.Glyphs.LeftBracket} {btn3Text} {CM.Glyphs.RightBracket}";
        var btn4Text = "never";
        var btn4 = $"{CM.Glyphs.LeftBracket} {btn4Text} {CM.Glyphs.RightBracket}";

        var buttonRow = $"{CM.Glyphs.VLine} {btn1} {btn2} {btn3} {btn4} {CM.Glyphs.VLine}";
        int width = buttonRow.Length;
        d.SetBufferSize (buttonRow.Length, 3);

        // Default - Center
        (runstate, Dialog _) = RunButtonTestDialog (
                                                    title,
                                                    width,
                                                    Dialog.ButtonAlignments.Center,
                                                    new Button { Text = btn1Text },
                                                    new Button { Text = btn2Text },
                                                    new Button { Text = btn3Text },
                                                    new Button { Text = btn4Text }
                                                   );
        TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
        End (runstate);

        // Justify
        buttonRow = $"{CM.Glyphs.VLine}{btn1} {btn2}  {btn3}  {btn4}{CM.Glyphs.VLine}";
        Assert.Equal (width, buttonRow.Length);

        (runstate, Dialog _) = RunButtonTestDialog (
                                                    title,
                                                    width,
                                                    Dialog.ButtonAlignments.Justify,
                                                    new Button { Text = btn1Text },
                                                    new Button { Text = btn2Text },
                                                    new Button { Text = btn3Text },
                                                    new Button { Text = btn4Text }
                                                   );
        TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
        End (runstate);

        // Right
        buttonRow = $"{CM.Glyphs.VLine}  {btn1} {btn2} {btn3} {btn4}{CM.Glyphs.VLine}";
        Assert.Equal (width, buttonRow.Length);

        (runstate, Dialog _) = RunButtonTestDialog (
                                                    title,
                                                    width,
                                                    Dialog.ButtonAlignments.Right,
                                                    new Button { Text = btn1Text },
                                                    new Button { Text = btn2Text },
                                                    new Button { Text = btn3Text },
                                                    new Button { Text = btn4Text }
                                                   );
        TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
        End (runstate);

        // Left
        buttonRow = $"{CM.Glyphs.VLine}{btn1} {btn2} {btn3} {btn4}  {CM.Glyphs.VLine}";
        Assert.Equal (width, buttonRow.Length);

        (runstate, Dialog _) = RunButtonTestDialog (
                                                    title,
                                                    width,
                                                    Dialog.ButtonAlignments.Left,
                                                    new Button { Text = btn1Text },
                                                    new Button { Text = btn2Text },
                                                    new Button { Text = btn3Text },
                                                    new Button { Text = btn4Text }
                                                   );
        TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
        End (runstate);
    }

    [Fact]
    [AutoInitShutdown]
    public void ButtonAlignment_Four_On_Too_Small_Width ()
    {
        RunState runstate = null;

        var d = (FakeDriver)Driver;

        var title = "1234";

        // E.g "|[ yes ][ no ][ maybe ][ never ]|"
        var btn1Text = "yes";
        var btn1 = $"{CM.Glyphs.LeftBracket} {btn1Text} {CM.Glyphs.RightBracket}";
        var btn2Text = "no";
        var btn2 = $"{CM.Glyphs.LeftBracket} {btn2Text} {CM.Glyphs.RightBracket}";
        var btn3Text = "maybe";
        var btn3 = $"{CM.Glyphs.LeftBracket} {btn3Text} {CM.Glyphs.RightBracket}";
        var btn4Text = "never";
        var btn4 = $"{CM.Glyphs.LeftBracket} {btn4Text} {CM.Glyphs.RightBracket}";
        var buttonRow = string.Empty;

        var width = 30;
        d.SetBufferSize (width, 1);

        // Default - Center
        buttonRow =
            $"{CM.Glyphs.VLine}es {CM.Glyphs.RightBracket} {btn2} {btn3} {CM.Glyphs.LeftBracket} neve{CM.Glyphs.VLine}";

        (runstate, Dialog dlg) = RunButtonTestDialog (
                                                      title,
                                                      width,
                                                      Dialog.ButtonAlignments.Center,
                                                      new Button { Text = btn1Text },
                                                      new Button { Text = btn2Text },
                                                      new Button { Text = btn3Text },
                                                      new Button { Text = btn4Text }
                                                     );
        Assert.Equal (new Size (width, 1), (Size)dlg.Frame.Size);
        TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
        End (runstate);

        // Justify
        buttonRow =
            $"{
                CM.Glyphs.VLine
            }{
                CM.Glyphs.LeftBracket
            } yes {
                CM.Glyphs.LeftBracket
            } no {
                CM.Glyphs.LeftBracket
            } maybe {
                CM.Glyphs.LeftBracket
            } never {
                CM.Glyphs.RightBracket
            }{
                CM.Glyphs.VLine
            }";

        (runstate, Dialog _) = RunButtonTestDialog (
                                                    title,
                                                    width,
                                                    Dialog.ButtonAlignments.Justify,
                                                    new Button { Text = btn1Text },
                                                    new Button { Text = btn2Text },
                                                    new Button { Text = btn3Text },
                                                    new Button { Text = btn4Text }
                                                   );
        TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
        End (runstate);

        // Right
        buttonRow = $"{CM.Glyphs.VLine}{CM.Glyphs.RightBracket} {btn2} {btn3} {btn4}{CM.Glyphs.VLine}";

        (runstate, Dialog _) = RunButtonTestDialog (
                                                    title,
                                                    width,
                                                    Dialog.ButtonAlignments.Right,
                                                    new Button { Text = btn1Text },
                                                    new Button { Text = btn2Text },
                                                    new Button { Text = btn3Text },
                                                    new Button { Text = btn4Text }
                                                   );
        TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
        End (runstate);

        // Left
        buttonRow = $"{CM.Glyphs.VLine}{btn1} {btn2} {btn3} {CM.Glyphs.LeftBracket} n{CM.Glyphs.VLine}";

        (runstate, Dialog _) = RunButtonTestDialog (
                                                    title,
                                                    width,
                                                    Dialog.ButtonAlignments.Left,
                                                    new Button { Text = btn1Text },
                                                    new Button { Text = btn2Text },
                                                    new Button { Text = btn3Text },
                                                    new Button { Text = btn4Text }
                                                   );
        TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
        End (runstate);
    }

    [Fact]
    [AutoInitShutdown]
    public void ButtonAlignment_Four_WideOdd ()
    {
        RunState runstate = null;

        var d = (FakeDriver)Driver;

        var title = "1234";

        // E.g "|[ yes ][ no ][ maybe ]|"
        var btn1Text = "really long button 1";
        var btn1 = $"{CM.Glyphs.LeftBracket} {btn1Text} {CM.Glyphs.RightBracket}";
        var btn2Text = "really long button 2";
        var btn2 = $"{CM.Glyphs.LeftBracket} {btn2Text} {CM.Glyphs.RightBracket}";
        var btn3Text = "really long button 3";
        var btn3 = $"{CM.Glyphs.LeftBracket} {btn3Text} {CM.Glyphs.RightBracket}";
        var btn4Text = "really long button 44"; // 44 is intentional to make length different than rest
        var btn4 = $"{CM.Glyphs.LeftBracket} {btn4Text} {CM.Glyphs.RightBracket}";

        // Note extra spaces to make dialog even wider
        //                         123456                          1234567
        var buttonRow = $"{CM.Glyphs.VLine}      {btn1} {btn2} {btn3} {btn4}      {CM.Glyphs.VLine}";
        int width = buttonRow.Length;
        d.SetBufferSize (buttonRow.Length, 1);

        // Default - Center
        (runstate, Dialog _) = RunButtonTestDialog (
                                                    title,
                                                    width,
                                                    Dialog.ButtonAlignments.Center,
                                                    new Button { Text = btn1Text },
                                                    new Button { Text = btn2Text },
                                                    new Button { Text = btn3Text },
                                                    new Button { Text = btn4Text }
                                                   );
        TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
        End (runstate);

        // Justify
        buttonRow = $"{CM.Glyphs.VLine}{btn1}     {btn2}     {btn3}     {btn4}{CM.Glyphs.VLine}";
        Assert.Equal (width, buttonRow.Length);

        (runstate, Dialog _) = RunButtonTestDialog (
                                                    title,
                                                    width,
                                                    Dialog.ButtonAlignments.Justify,
                                                    new Button { Text = btn1Text },
                                                    new Button { Text = btn2Text },
                                                    new Button { Text = btn3Text },
                                                    new Button { Text = btn4Text }
                                                   );
        TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
        End (runstate);

        // Right
        buttonRow = $"{CM.Glyphs.VLine}            {btn1} {btn2} {btn3} {btn4}{CM.Glyphs.VLine}";
        Assert.Equal (width, buttonRow.Length);

        (runstate, Dialog _) = RunButtonTestDialog (
                                                    title,
                                                    width,
                                                    Dialog.ButtonAlignments.Right,
                                                    new Button { Text = btn1Text },
                                                    new Button { Text = btn2Text },
                                                    new Button { Text = btn3Text },
                                                    new Button { Text = btn4Text }
                                                   );
        TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
        End (runstate);

        // Left
        buttonRow = $"{CM.Glyphs.VLine}{btn1} {btn2} {btn3} {btn4}            {CM.Glyphs.VLine}";
        Assert.Equal (width, buttonRow.Length);

        (runstate, Dialog _) = RunButtonTestDialog (
                                                    title,
                                                    width,
                                                    Dialog.ButtonAlignments.Left,
                                                    new Button { Text = btn1Text },
                                                    new Button { Text = btn2Text },
                                                    new Button { Text = btn3Text },
                                                    new Button { Text = btn4Text }
                                                   );
        TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
        End (runstate);
    }

    [Fact]
    [AutoInitShutdown]
    public void ButtonAlignment_Four_Wider ()
    {
        RunState runstate = null;

        var d = (FakeDriver)Driver;

        var title = "1234";

        // E.g "|[ yes ][ no ][ maybe ]|"
        var btn1Text = "yes";
        var btn1 = $"{CM.Glyphs.LeftBracket} {btn1Text} {CM.Glyphs.RightBracket}";
        var btn2Text = "no";
        var btn2 = $"{CM.Glyphs.LeftBracket} {btn2Text} {CM.Glyphs.RightBracket}";
        var btn3Text = "你你你你你"; // This is a wide char
        var btn3 = $"{CM.Glyphs.LeftBracket} {btn3Text} {CM.Glyphs.RightBracket}";

        // Requires a Nerd Font
        var btn4Text = "\uE36E\uE36F\uE370\uE371\uE372\uE373";
        var btn4 = $"{CM.Glyphs.LeftBracket} {btn4Text} {CM.Glyphs.RightBracket}";

        // Note extra spaces to make dialog even wider
        //                         123456                           123456
        var buttonRow = $"{CM.Glyphs.VLine}      {btn1} {btn2} {btn3} {btn4}      {CM.Glyphs.VLine}";
        int width = buttonRow.GetColumns ();
        d.SetBufferSize (width, 3);

        // Default - Center
        (runstate, Dialog _) = RunButtonTestDialog (
                                                    title,
                                                    width,
                                                    Dialog.ButtonAlignments.Center,
                                                    new Button { Text = btn1Text },
                                                    new Button { Text = btn2Text },
                                                    new Button { Text = btn3Text },
                                                    new Button { Text = btn4Text }
                                                   );
        TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
        End (runstate);

        // Justify
        buttonRow = $"{CM.Glyphs.VLine}{btn1}     {btn2}     {btn3}     {btn4}{CM.Glyphs.VLine}";
        Assert.Equal (width, buttonRow.GetColumns ());

        (runstate, Dialog _) = RunButtonTestDialog (
                                                    title,
                                                    width,
                                                    Dialog.ButtonAlignments.Justify,
                                                    new Button { Text = btn1Text },
                                                    new Button { Text = btn2Text },
                                                    new Button { Text = btn3Text },
                                                    new Button { Text = btn4Text }
                                                   );
        TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
        End (runstate);

        // Right
        buttonRow = $"{CM.Glyphs.VLine}            {btn1} {btn2} {btn3} {btn4}{CM.Glyphs.VLine}";
        Assert.Equal (width, buttonRow.GetColumns ());

        (runstate, Dialog _) = RunButtonTestDialog (
                                                    title,
                                                    width,
                                                    Dialog.ButtonAlignments.Right,
                                                    new Button { Text = btn1Text },
                                                    new Button { Text = btn2Text },
                                                    new Button { Text = btn3Text },
                                                    new Button { Text = btn4Text }
                                                   );
        TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
        End (runstate);

        // Left
        buttonRow = $"{CM.Glyphs.VLine}{btn1} {btn2} {btn3} {btn4}            {CM.Glyphs.VLine}";
        Assert.Equal (width, buttonRow.GetColumns ());

        (runstate, Dialog _) = RunButtonTestDialog (
                                                    title,
                                                    width,
                                                    Dialog.ButtonAlignments.Left,
                                                    new Button { Text = btn1Text },
                                                    new Button { Text = btn2Text },
                                                    new Button { Text = btn3Text },
                                                    new Button { Text = btn4Text }
                                                   );
        TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
        End (runstate);
    }

    [Fact]
    [AutoInitShutdown]
    public void ButtonAlignment_One ()
    {
        var d = (FakeDriver)Driver;
        RunState runstate = null;

        var title = "1234";

        // E.g "|[ ok ]|"
        var btnText = "ok";

        var buttonRow =
            $"{CM.Glyphs.VLine}  {CM.Glyphs.LeftBracket} {btnText} {CM.Glyphs.RightBracket}  {CM.Glyphs.VLine}";
        int width = buttonRow.Length;

        d.SetBufferSize (width, 1);

        (runstate, Dialog _) = RunButtonTestDialog (
                                                    title,
                                                    width,
                                                    Dialog.ButtonAlignments.Center,
                                                    new Button { Text = btnText }
                                                   );

        // Center
        TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
        End (runstate);

        // Justify 
        buttonRow =
            $"{CM.Glyphs.VLine}    {CM.Glyphs.LeftBracket} {btnText} {CM.Glyphs.RightBracket}{CM.Glyphs.VLine}";
        Assert.Equal (width, buttonRow.Length);

        (runstate, Dialog _) = RunButtonTestDialog (
                                                    title,
                                                    width,
                                                    Dialog.ButtonAlignments.Justify,
                                                    new Button { Text = btnText }
                                                   );
        TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
        End (runstate);

        // Right
        buttonRow =
            $"{CM.Glyphs.VLine}    {CM.Glyphs.LeftBracket} {btnText} {CM.Glyphs.RightBracket}{CM.Glyphs.VLine}";
        Assert.Equal (width, buttonRow.Length);

        (runstate, Dialog _) = RunButtonTestDialog (
                                                    title,
                                                    width,
                                                    Dialog.ButtonAlignments.Right,
                                                    new Button { Text = btnText }
                                                   );
        TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
        End (runstate);

        // Left
        buttonRow =
            $"{CM.Glyphs.VLine}{CM.Glyphs.LeftBracket} {btnText} {CM.Glyphs.RightBracket}    {CM.Glyphs.VLine}";
        Assert.Equal (width, buttonRow.Length);

        (runstate, Dialog _) = RunButtonTestDialog (
                                                    title,
                                                    width,
                                                    Dialog.ButtonAlignments.Left,
                                                    new Button { Text = btnText }
                                                   );
        TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
        End (runstate);

        // Wider
        buttonRow =
            $"{CM.Glyphs.VLine}   {CM.Glyphs.LeftBracket} {btnText} {CM.Glyphs.RightBracket}   {CM.Glyphs.VLine}";
        width = buttonRow.Length;

        d.SetBufferSize (width, 1);

        (runstate, Dialog _) = RunButtonTestDialog (
                                                    title,
                                                    width,
                                                    Dialog.ButtonAlignments.Center,
                                                    new Button { Text = btnText }
                                                   );
        TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
        End (runstate);

        // Justify
        buttonRow =
            $"{CM.Glyphs.VLine}      {CM.Glyphs.LeftBracket} {btnText} {CM.Glyphs.RightBracket}{CM.Glyphs.VLine}";
        Assert.Equal (width, buttonRow.Length);

        (runstate, Dialog _) = RunButtonTestDialog (
                                                    title,
                                                    width,
                                                    Dialog.ButtonAlignments.Justify,
                                                    new Button { Text = btnText }
                                                   );
        TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
        End (runstate);

        // Right
        buttonRow =
            $"{CM.Glyphs.VLine}      {CM.Glyphs.LeftBracket} {btnText} {CM.Glyphs.RightBracket}{CM.Glyphs.VLine}";
        Assert.Equal (width, buttonRow.Length);

        (runstate, Dialog _) = RunButtonTestDialog (
                                                    title,
                                                    width,
                                                    Dialog.ButtonAlignments.Right,
                                                    new Button { Text = btnText }
                                                   );
        TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
        End (runstate);

        // Left
        buttonRow =
            $"{CM.Glyphs.VLine}{CM.Glyphs.LeftBracket} {btnText} {CM.Glyphs.RightBracket}      {CM.Glyphs.VLine}";
        Assert.Equal (width, buttonRow.Length);

        (runstate, Dialog _) = RunButtonTestDialog (
                                                    title,
                                                    width,
                                                    Dialog.ButtonAlignments.Left,
                                                    new Button { Text = btnText }
                                                   );
        TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
        End (runstate);
    }

    [Fact]
    [AutoInitShutdown]
    public void ButtonAlignment_Three ()
    {
        RunState runstate = null;

        var d = (FakeDriver)Driver;

        var title = "1234";

        // E.g "|[ yes ][ no ][ maybe ]|"
        var btn1Text = "yes";
        var btn1 = $"{CM.Glyphs.LeftBracket} {btn1Text} {CM.Glyphs.RightBracket}";
        var btn2Text = "no";
        var btn2 = $"{CM.Glyphs.LeftBracket} {btn2Text} {CM.Glyphs.RightBracket}";
        var btn3Text = "maybe";
        var btn3 = $"{CM.Glyphs.LeftBracket} {btn3Text} {CM.Glyphs.RightBracket}";

        var buttonRow = $@"{CM.Glyphs.VLine} {btn1} {btn2} {btn3} {CM.Glyphs.VLine}";
        int width = buttonRow.Length;

        d.SetBufferSize (buttonRow.Length, 3);

        (runstate, Dialog _) = RunButtonTestDialog (
                                                    title,
                                                    width,
                                                    Dialog.ButtonAlignments.Center,
                                                    new Button { Text = btn1Text },
                                                    new Button { Text = btn2Text },
                                                    new Button { Text = btn3Text }
                                                   );
        TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
        End (runstate);

        // Justify
        buttonRow = $@"{CM.Glyphs.VLine}{btn1}  {btn2}  {btn3}{CM.Glyphs.VLine}";
        Assert.Equal (width, buttonRow.Length);

        (runstate, Dialog _) = RunButtonTestDialog (
                                                    title,
                                                    width,
                                                    Dialog.ButtonAlignments.Justify,
                                                    new Button { Text = btn1Text },
                                                    new Button { Text = btn2Text },
                                                    new Button { Text = btn3Text }
                                                   );
        TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
        End (runstate);

        // Right
        buttonRow = $@"{CM.Glyphs.VLine}  {btn1} {btn2} {btn3}{CM.Glyphs.VLine}";
        Assert.Equal (width, buttonRow.Length);

        (runstate, Dialog _) = RunButtonTestDialog (
                                                    title,
                                                    width,
                                                    Dialog.ButtonAlignments.Right,
                                                    new Button { Text = btn1Text },
                                                    new Button { Text = btn2Text },
                                                    new Button { Text = btn3Text }
                                                   );
        TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
        End (runstate);

        // Left
        buttonRow = $@"{CM.Glyphs.VLine}{btn1} {btn2} {btn3}  {CM.Glyphs.VLine}";
        Assert.Equal (width, buttonRow.Length);

        (runstate, Dialog _) = RunButtonTestDialog (
                                                    title,
                                                    width,
                                                    Dialog.ButtonAlignments.Left,
                                                    new Button { Text = btn1Text },
                                                    new Button { Text = btn2Text },
                                                    new Button { Text = btn3Text }
                                                   );
        TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
        End (runstate);
    }

    [Fact]
    [AutoInitShutdown]
    public void ButtonAlignment_Two ()
    {
        RunState runstate = null;

        var d = (FakeDriver)Driver;

        var title = "1234";

        // E.g "|[ yes ][ no ]|"
        var btn1Text = "yes";
        var btn1 = $"{CM.Glyphs.LeftBracket} {btn1Text} {CM.Glyphs.RightBracket}";
        var btn2Text = "no";
        var btn2 = $"{CM.Glyphs.LeftBracket} {btn2Text} {CM.Glyphs.RightBracket}";

        var buttonRow = $@"{CM.Glyphs.VLine} {btn1} {btn2} {CM.Glyphs.VLine}";
        int width = buttonRow.Length;

        d.SetBufferSize (buttonRow.Length, 3);

        (runstate, Dialog _) = RunButtonTestDialog (
                                                    title,
                                                    width,
                                                    Dialog.ButtonAlignments.Center,
                                                    new Button { Text = btn1Text },
                                                    new Button { Text = btn2Text }
                                                   );
        TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
        End (runstate);

        // Justify
        buttonRow = $@"{CM.Glyphs.VLine}{btn1}   {btn2}{CM.Glyphs.VLine}";
        Assert.Equal (width, buttonRow.Length);

        (runstate, Dialog _) = RunButtonTestDialog (
                                                    title,
                                                    width,
                                                    Dialog.ButtonAlignments.Justify,
                                                    new Button { Text = btn1Text },
                                                    new Button { Text = btn2Text }
                                                   );
        TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
        End (runstate);

        // Right
        buttonRow = $@"{CM.Glyphs.VLine}  {btn1} {btn2}{CM.Glyphs.VLine}";
        Assert.Equal (width, buttonRow.Length);

        (runstate, Dialog _) = RunButtonTestDialog (
                                                    title,
                                                    width,
                                                    Dialog.ButtonAlignments.Right,
                                                    new Button { Text = btn1Text },
                                                    new Button { Text = btn2Text }
                                                   );
        TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
        End (runstate);

        // Left
        buttonRow = $@"{CM.Glyphs.VLine}{btn1} {btn2}  {CM.Glyphs.VLine}";
        Assert.Equal (width, buttonRow.Length);

        (runstate, Dialog _) = RunButtonTestDialog (
                                                    title,
                                                    width,
                                                    Dialog.ButtonAlignments.Left,
                                                    new Button { Text = btn1Text },
                                                    new Button { Text = btn2Text }
                                                   );
        TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
        End (runstate);
    }

    [Fact]
    [AutoInitShutdown]
    public void ButtonAlignment_Two_Hidden ()
    {
        RunState runstate = null;
        var firstIteration = false;

        var d = (FakeDriver)Driver;

        var title = "1234";

        // E.g "|[ yes ][ no ]|"
        var btn1Text = "yes";
        var btn1 = $"{CM.Glyphs.LeftBracket} {btn1Text} {CM.Glyphs.RightBracket}";
        var btn2Text = "no";
        var btn2 = $"{CM.Glyphs.LeftBracket} {btn2Text} {CM.Glyphs.RightBracket}";

        var buttonRow = $@"{CM.Glyphs.VLine} {btn1} {btn2} {CM.Glyphs.VLine}";
        int width = buttonRow.Length;

        d.SetBufferSize (buttonRow.Length, 3);

        Dialog dlg = null;
        Button button1, button2;

        // Default (Center)
        button1 = new Button { Text = btn1Text };
        button2 = new Button { Text = btn2Text };
        (runstate, dlg) = RunButtonTestDialog (title, width, Dialog.ButtonAlignments.Center, button1, button2);
        button1.Visible = false;
        RunIteration (ref runstate, ref firstIteration);
        buttonRow = $@"{CM.Glyphs.VLine}         {btn2} {CM.Glyphs.VLine}";
        TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
        End (runstate);

        // Justify
        Assert.Equal (width, buttonRow.Length);
        button1 = new Button { Text = btn1Text };
        button2 = new Button { Text = btn2Text };
        (runstate, dlg) = RunButtonTestDialog (title, width, Dialog.ButtonAlignments.Justify, button1, button2);
        button1.Visible = false;
        RunIteration (ref runstate, ref firstIteration);
        buttonRow = $@"{CM.Glyphs.VLine}          {btn2}{CM.Glyphs.VLine}";
        TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
        End (runstate);

        // Right
        Assert.Equal (width, buttonRow.Length);
        button1 = new Button { Text = btn1Text };
        button2 = new Button { Text = btn2Text };
        (runstate, dlg) = RunButtonTestDialog (title, width, Dialog.ButtonAlignments.Right, button1, button2);
        button1.Visible = false;
        RunIteration (ref runstate, ref firstIteration);
        TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
        End (runstate);

        // Left
        Assert.Equal (width, buttonRow.Length);
        button1 = new Button { Text = btn1Text };
        button2 = new Button { Text = btn2Text };
        (runstate, dlg) = RunButtonTestDialog (title, width, Dialog.ButtonAlignments.Left, button1, button2);
        button1.Visible = false;
        RunIteration (ref runstate, ref firstIteration);
        buttonRow = $@"{CM.Glyphs.VLine}        {btn2}  {CM.Glyphs.VLine}";
        TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
        End (runstate);
    }

    [Fact]
    [AutoInitShutdown]
    public void Dialog_In_Window_With_Size_One_Button_Aligns ()
    {
        ((FakeDriver)Driver).SetBufferSize (20, 5);

        var win = new Window ();

        var iterations = 0;

        Iteration += (s, a) =>
                     {
                         if (++iterations > 2)
                         {
                             RequestStop ();
                         }
                     };
        var btn = $"{CM.Glyphs.LeftBracket} Ok {CM.Glyphs.RightBracket}";

        win.Loaded += (s, a) =>
                      {
                          var dlg = new Dialog { Width = 18, Height = 3, Buttons = [new Button { Text = "Ok" }] };

                          dlg.Loaded += (s, a) =>
                                        {
                                            Refresh ();

                                            var expected = @$"
┌──────────────────┐
│┌────────────────┐│
││     {
    btn
}     ││
│└────────────────┘│
└──────────────────┘";
                                            _ = TestHelpers.AssertDriverContentsWithFrameAre (expected, _output);
                                        };

                          Run (dlg);
                      };
        Run (win);
    }

    [Theory]
    [AutoInitShutdown]
    [InlineData (
                    5,
                    @"
┌┌───────────────┐─┐
││               │ │
││     ⟦ Ok ⟧    │ │
│└───────────────┘ │
└──────────────────┘"
                )]
    [InlineData (
                    6,
                    @"
┌┌───────────────┐─┐
││               │ │
││               │ │
││     ⟦ Ok ⟧    │ │
│└───────────────┘ │
└──────────────────┘"
                )]
    [InlineData (
                    7,
                    @"
┌──────────────────┐
│┌───────────────┐ │
││               │ │
││               │ │
││     ⟦ Ok ⟧    │ │
│└───────────────┘ │
└──────────────────┘"
                )]
    [InlineData (
                    8,
                    @"
┌──────────────────┐
│┌───────────────┐ │
││               │ │
││               │ │
││               │ │
││     ⟦ Ok ⟧    │ │
│└───────────────┘ │
└──────────────────┘"
                )]
    [InlineData (
                    9,
                    @"
┌──────────────────┐
│┌───────────────┐ │
││               │ │
││               │ │
││               │ │
││               │ │
││     ⟦ Ok ⟧    │ │
│└───────────────┘ │
└──────────────────┘"
                )]
    public void Dialog_In_Window_Without_Size_One_Button_Aligns (int height, string expected)
    {
        ((FakeDriver)Driver).SetBufferSize (20, height);
        var win = new Window ();

        int iterations = -1;

        Iteration += (s, a) =>
                     {
                         iterations++;

                         if (iterations == 0)
                         {
                             var dlg = new Dialog { Buttons = [new Button { Text = "Ok" }] };
                             Run (dlg);
                         }
                         else if (iterations == 1)
                         {
                             // BUGBUG: This seems wrong; is it a bug in Dim.Percent(85)?? No
                             _ = TestHelpers.AssertDriverContentsWithFrameAre (expected, _output);
                         }
                         else
                         {
                             RequestStop ();
                         }
                     };

        Run (win);
    }

    [Fact]
    [AutoInitShutdown]
    public void Dialog_Opened_From_Another_Dialog ()
    {
        ((FakeDriver)Driver).SetBufferSize (30, 10);

        var btn1 = new Button { Text = "press me 1" };
        Button btn2 = null;
        Button btn3 = null;
        string expected = null;

        btn1.Accept += (s, e) =>
                        {
                            btn2 = new Button { Text = "Show Sub" };
                            btn3 = new Button { Text = "Close" };
                            btn3.Accept += (s, e) => RequestStop ();

                            btn2.Accept += (s, e) =>
                                            {
                                                // Don't test MessageBox in Dialog unit tests!
                                                var subBtn = new Button { Text = "Ok", IsDefault = true };
                                                var subDlg = new Dialog { Text = "ya", Width = 20, Height = 5, Buttons = [subBtn] };
                                                subBtn.Accept += (s, e) => RequestStop (subDlg);
                                                Run (subDlg);
                                            };
                            var dlg = new Dialog { Buttons = [btn2, btn3] };

                            Run (dlg);
                        };

        var btn =
            $"{
                CM.Glyphs.LeftBracket
            }{
                CM.Glyphs.LeftDefaultIndicator
            } Ok {
                CM.Glyphs.RightDefaultIndicator
            }{
                CM.Glyphs.RightBracket
            }";

        int iterations = -1;

        Iteration += (s, a) =>
                     {
                         iterations++;

                         if (iterations == 0)
                         {
                             Assert.True (btn1.NewKeyDownEvent (Key.Space));
                         }
                         else if (iterations == 1)
                         {
                             expected = @$"
  ┌───────────────────────┐
  │                       │
  │                       │
  │                       │
  │                       │
  │                       │
  │{
      CM.Glyphs.LeftBracket
  } Show Sub {
      CM.Glyphs.RightBracket
  } {
      CM.Glyphs.LeftBracket
  } Close {
      CM.Glyphs.RightBracket
  } │
  └───────────────────────┘";
                             TestHelpers.AssertDriverContentsWithFrameAre (expected, _output);

                             Assert.True (btn2.NewKeyDownEvent (Key.Space));
                         }
                         else if (iterations == 2)
                         {
                             TestHelpers.AssertDriverContentsWithFrameAre (
                                                                           @$"
  ┌───────────────────────┐
  │  ┌──────────────────┐ │
  │  │ya                │ │
  │  │                  │ │
  │  │     {
      btn
  }     │ │
  │  └──────────────────┘ │
  │{
      CM.Glyphs.LeftBracket
  } Show Sub {
      CM.Glyphs.RightBracket
  } {
      CM.Glyphs.LeftBracket
  } Close {
      CM.Glyphs.RightBracket
  } │
  └───────────────────────┘",
                                                                           _output
                                                                          );

                             Assert.True (Current.NewKeyDownEvent (Key.Enter));
                         }
                         else if (iterations == 3)
                         {
                             TestHelpers.AssertDriverContentsWithFrameAre (expected, _output);

                             Assert.True (btn3.NewKeyDownEvent (Key.Space));
                         }
                         else if (iterations == 4)
                         {
                             TestHelpers.AssertDriverContentsWithFrameAre ("", _output);

                             RequestStop ();
                         }
                     };

        Run ();
        Shutdown ();

        Assert.Equal (4, iterations);
    }

    [Fact]
    [AutoInitShutdown]
    public void FileDialog_FileSystemWatcher ()
    {
        for (var i = 0; i < 8; i++)
        {
            var fd = new FileDialog ();
            fd.Ready += (s, e) => RequestStop ();
            Run (fd);
        }
    }

    [Fact]
    [AutoInitShutdown]
    public void Location_Default ()
    {
        var d = new Dialog ();
        Begin (d);
        ((FakeDriver)Driver).SetBufferSize (100, 100);

        // Default location is centered, so 100 / 2 - 85 / 2 = 7
        var expected = 7;
        Assert.Equal (new Point (expected, expected), (Point)d.Frame.Location);
    }

    [Fact]
    [AutoInitShutdown]
    public void Location_Not_Default ()
    {
        var d = new Dialog { X = 1, Y = 1 };
        Begin (d);
        ((FakeDriver)Driver).SetBufferSize (100, 100);

        // Default location is centered, so 100 / 2 - 85 / 2 = 7
        var expected = 1;
        Assert.Equal (new Point (expected, expected), (Point)d.Frame.Location);
    }

    [Fact]
    [AutoInitShutdown]
    public void Location_When_Application_Top_Not_Default ()
    {
        var expected = 5;
        var d = new Dialog { X = expected, Y = expected, Height = 5, Width = 5 };
        Begin (d);
        ((FakeDriver)Driver).SetBufferSize (20, 10);

        // Default location is centered, so 100 / 2 - 85 / 2 = 7
        Assert.Equal (new Point (expected, expected), (Point)d.Frame.Location);

        TestHelpers.AssertDriverContentsWithFrameAre (
                                                      @"
     ┌───┐
     │   │
     │   │
     │   │
     └───┘",
                                                      _output
                                                     );
    }

    [Fact]
    [AutoInitShutdown]
    public void Location_When_Not_Application_Top_Not_Default ()
    {
        Top.BorderStyle = LineStyle.Double;

        int iterations = -1;

        Iteration += (s, a) =>
                     {
                         iterations++;

                         if (iterations == 0)
                         {
                             var d = new Dialog { X = 5, Y = 5, Height = 3, Width = 5 };
                             Begin (d);

                             Assert.Equal (new Point (5, 5), (Point)d.Frame.Location);

                             TestHelpers.AssertDriverContentsWithFrameAre (
                                                                           @"
╔══════════════════╗
║                  ║
║                  ║
║                  ║
║                  ║
║    ┌───┐         ║
║    │   │         ║
║    └───┘         ║
║                  ║
╚══════════════════╝",
                                                                           _output
                                                                          );

                             d = new Dialog { X = 5, Y = 5 };
                             Begin (d);

                             // This is because of PostionTopLevels and EnsureVisibleBounds
                             Assert.Equal (new Point (3, 2), (Point)d.Frame.Location);

                             // #3127: Before					
                             //					Assert.Equal (new Size (17, 8), d.Frame.Size);
                             //					TestHelpers.AssertDriverContentsWithFrameAre (@"
                             //╔══════════════════╗
                             //║                  ║
                             //║  ┌───────────────┐
                             //║  │               │
                             //║  │               │
                             //║  │               │
                             //║  │               │
                             //║  │               │
                             //║  │               │
                             //╚══└───────────────┘", _output);

                             // #3127: After: Because Toplevel is now Width/Height = Dim.Filll
                             Assert.Equal (new Size (15, 6), (Size)d.Frame.Size);

                             TestHelpers.AssertDriverContentsWithFrameAre (
                                                                           @"
╔══════════════════╗
║                  ║
║  ┌─────────────┐ ║
║  │             │ ║
║  │             │ ║
║  │             │ ║
║  │             │ ║
║  └─────────────┘ ║
║                  ║
╚══════════════════╝",
                                                                           _output
                                                                          );
                         }
                         else if (iterations > 0)
                         {
                             RequestStop ();
                         }
                     };

        Begin (Top);
        ((FakeDriver)Driver).SetBufferSize (20, 10);
        Run ();
    }

    [Fact]
    [AutoInitShutdown]
    public void One_Button_Works ()
    {
        RunState runstate = null;

        var d = (FakeDriver)Driver;

        var title = "";
        var btnText = "ok";

        var buttonRow =
            $"{CM.Glyphs.VLine}   {CM.Glyphs.LeftBracket} {btnText} {CM.Glyphs.RightBracket}   {CM.Glyphs.VLine}";

        int width = buttonRow.Length;
        d.SetBufferSize (buttonRow.Length, 10);

        (runstate, Dialog _) = RunButtonTestDialog (
                                                    title,
                                                    width,
                                                    Dialog.ButtonAlignments.Center,
                                                    new Button { Text = btnText }
                                                   );
        TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);
        End (runstate);
    }

    [Fact]
    [AutoInitShutdown]
    public void Size_Default ()
    {
        var d = new Dialog ();
        Begin (d);
        ((FakeDriver)Driver).SetBufferSize (100, 100);

        // Default size is Percent(85) 
        Assert.Equal (new Size ((int)(100 * .85), (int)(100 * .85)), (Size)d.Frame.Size);
    }

    [Fact]
    [AutoInitShutdown]
    public void Size_Not_Default ()
    {
        var d = new Dialog { Width = 50, Height = 50 };

        Begin (d);
        ((FakeDriver)Driver).SetBufferSize (100, 100);

        // Default size is Percent(85) 
        Assert.Equal (new Size (50, 50), (Size)d.Frame.Size);
    }

    [Fact]
    [AutoInitShutdown]
    public void Zero_Buttons_Works ()
    {
        RunState runstate = null;

        var d = (FakeDriver)Driver;

        var title = "1234";

        var buttonRow = $"{CM.Glyphs.VLine}        {CM.Glyphs.VLine}";
        int width = buttonRow.Length;
        d.SetBufferSize (buttonRow.Length, 3);

        (runstate, Dialog _) = RunButtonTestDialog (title, width, Dialog.ButtonAlignments.Center, null);
        TestHelpers.AssertDriverContentsWithFrameAre ($"{buttonRow}", _output);

        End (runstate);
    }

    private (RunState, Dialog) RunButtonTestDialog (
        string title,
        int width,
        Dialog.ButtonAlignments align,
        params Button [] btns
    )
    {
        var dlg = new Dialog
        {
            Title = title,
            X = 0,
            Y = 0,
            Width = width,
            Height = 1,
            ButtonAlignment = align,
            Buttons = btns
        };

        // Create with no top or bottom border to simplify testing button layout (no need to account for title etc..)
        dlg.Border.Thickness = new Thickness (1, 0, 1, 0);

        return (Begin (dlg), dlg);
    }
}
