// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
  testWidgets('ButtonBar default control smoketest', (WidgetTester tester) async {
    await tester.pumpWidget(
      const Directionality(
        textDirection: TextDirection.ltr,
        child: ButtonBar(),
      ),
    );
  });

  group('alignment', () {

    testWidgets('default alignment is MainAxisAlignment.end', (WidgetTester tester) async {
      await tester.pumpWidget(
        const MaterialApp(
          home: ButtonBar(
            children: <Widget>[
              SizedBox(width: 10.0, height: 10.0),
            ],
          ),
        ),
      );

      final Finder child = find.byType(SizedBox);
      // Should be positioned to the right of the bar,
      expect(tester.getRect(child).left, 782.0);  // bar width - default padding - 10
      expect(tester.getRect(child).right, 792.0); // bar width - default padding
    });

    testWidgets('ButtonBarTheme.alignment overrides default', (WidgetTester tester) async {
      await tester.pumpWidget(
        const MaterialApp(
          home: ButtonBarTheme(
            data: ButtonBarThemeData(
              alignment: MainAxisAlignment.center,
            ),
            child: ButtonBar(
              children: <Widget>[
                SizedBox(width: 10.0, height: 10.0),
              ],
            ),
          ),
        ),
      );

      final Finder child = find.byType(SizedBox);
      // Should be positioned in the center
      expect(tester.getRect(child).left, 395.0);  // (bar width - padding) / 2 - 10 / 2
      expect(tester.getRect(child).right, 405.0); // (bar width - padding) / 2 - 10 / 2 + 10
    });

    testWidgets('ButtonBar.alignment overrides ButtonBarTheme.alignment and default', (WidgetTester tester) async {
      await tester.pumpWidget(
        const MaterialApp(
          home: ButtonBarTheme(
            data: ButtonBarThemeData(
              alignment: MainAxisAlignment.center,
            ),
            child: ButtonBar(
              alignment: MainAxisAlignment.start,
              children: <Widget>[
                SizedBox(width: 10.0, height: 10.0),
              ],
            ),
          ),
        ),
      );

      final Finder child = find.byType(SizedBox);
      // Should be positioned on the left
      expect(tester.getRect(child).left, 8.0);   // padding
      expect(tester.getRect(child).right, 18.0); // padding + 10
    });

  });

  group('mainAxisSize', () {

    testWidgets('Default mainAxisSize is MainAxisSize.max', (WidgetTester tester) async {
      const Key buttonBarKey = Key('row');
      const Key child0Key = Key('child0');
      const Key child1Key = Key('child1');
      const Key child2Key = Key('child2');

      await tester.pumpWidget(
        MaterialApp(
          home: Center(
            child: ButtonBar(
              key: buttonBarKey,
              // buttonPadding set to zero to simplify test calculations.
              buttonPadding: EdgeInsets.zero,
              children: <Widget>[
                Container(key: child0Key, width: 100.0, height: 100.0),
                Container(key: child1Key, width: 100.0, height: 100.0),
                Container(key: child2Key, width: 100.0, height: 100.0),
              ],
            ),
          ),
        ),
      );

      // ButtonBar should take up all the space it is provided by its parent.
      final Rect buttonBarRect = tester.getRect(find.byKey(buttonBarKey));
      expect(buttonBarRect.size.width, equals(800.0));
      expect(buttonBarRect.size.height, equals(100.0));

      // The children of [ButtonBar] are aligned by [MainAxisAlignment.end] by
      // default.
      Rect childRect;
      childRect = tester.getRect(find.byKey(child0Key));
      expect(childRect.size.width, equals(100.0));
      expect(childRect.size.height, equals(100.0));
      expect(childRect.right, 800.0 - 200.0);

      childRect = tester.getRect(find.byKey(child1Key));
      expect(childRect.size.width, equals(100.0));
      expect(childRect.size.height, equals(100.0));
      expect(childRect.right, 800.0 - 100.0);

      childRect = tester.getRect(find.byKey(child2Key));
      expect(childRect.size.width, equals(100.0));
      expect(childRect.size.height, equals(100.0));
      expect(childRect.right, 800.0);
    });

    testWidgets('ButtonBarTheme.mainAxisSize overrides default', (WidgetTester tester) async {
      const Key buttonBarKey = Key('row');
      const Key child0Key = Key('child0');
      const Key child1Key = Key('child1');
      const Key child2Key = Key('child2');
      await tester.pumpWidget(
        MaterialApp(
          home: ButtonBarTheme(
            data: const ButtonBarThemeData(
              mainAxisSize: MainAxisSize.min,
            ),
            child: Center(
              child: ButtonBar(
                key: buttonBarKey,
                // buttonPadding set to zero to simplify test calculations.
                buttonPadding: EdgeInsets.zero,
                children: <Widget>[
                  Container(key: child0Key, width: 100.0, height: 100.0),
                  Container(key: child1Key, width: 100.0, height: 100.0),
                  Container(key: child2Key, width: 100.0, height: 100.0),
                ],
              ),
            ),
          ),
        ),
      );

      // ButtonBar should take up minimum space it requires.
      final Rect buttonBarRect = tester.getRect(find.byKey(buttonBarKey));
      expect(buttonBarRect.size.width, equals(300.0));
      expect(buttonBarRect.size.height, equals(100.0));

      Rect childRect;
      childRect = tester.getRect(find.byKey(child0Key));
      expect(childRect.size.width, equals(100.0));
      expect(childRect.size.height, equals(100.0));
      // Should be a center aligned because of [Center] widget.
      // First child is on the left side of the button bar.
      expect(childRect.left, (800.0 - buttonBarRect.width) / 2.0);

      childRect = tester.getRect(find.byKey(child1Key));
      expect(childRect.size.width, equals(100.0));
      expect(childRect.size.height, equals(100.0));
      // Should be a center aligned because of [Center] widget.
      // Second child is on the center the button bar.
      expect(childRect.left, ((800.0 - buttonBarRect.width) / 2.0) + 100.0);

      childRect = tester.getRect(find.byKey(child2Key));
      expect(childRect.size.width, equals(100.0));
      expect(childRect.size.height, equals(100.0));
      // Should be a center aligned because of [Center] widget.
      // Third child is on the right side of the button bar.
      expect(childRect.left, ((800.0 - buttonBarRect.width) / 2.0) + 200.0);
    });

    testWidgets('ButtonBar.mainAxisSize overrides ButtonBarTheme.mainAxisSize and default', (WidgetTester tester) async {
      const Key buttonBarKey = Key('row');
      const Key child0Key = Key('child0');
      const Key child1Key = Key('child1');
      const Key child2Key = Key('child2');
      await tester.pumpWidget(
        MaterialApp(
          home: ButtonBarTheme(
            data: const ButtonBarThemeData(
              mainAxisSize: MainAxisSize.min,
            ),
            child: Center(
              child: ButtonBar(
                key: buttonBarKey,
                // buttonPadding set to zero to simplify test calculations.
                buttonPadding: EdgeInsets.zero,
                mainAxisSize: MainAxisSize.max,
                children: <Widget>[
                  Container(key: child0Key, width: 100.0, height: 100.0),
                  Container(key: child1Key, width: 100.0, height: 100.0),
                  Container(key: child2Key, width: 100.0, height: 100.0),
                ],
              ),
            ),
          ),
        ),
      );

      // ButtonBar should take up all the space it is provided by its parent.
      final Rect buttonBarRect = tester.getRect(find.byKey(buttonBarKey));
      expect(buttonBarRect.size.width, equals(800.0));
      expect(buttonBarRect.size.height, equals(100.0));

      // The children of [ButtonBar] are aligned by [MainAxisAlignment.end] by
      // default.
      Rect childRect;
      childRect = tester.getRect(find.byKey(child0Key));
      expect(childRect.size.width, equals(100.0));
      expect(childRect.size.height, equals(100.0));
      expect(childRect.right, 800.0 - 200.0);

      childRect = tester.getRect(find.byKey(child1Key));
      expect(childRect.size.width, equals(100.0));
      expect(childRect.size.height, equals(100.0));
      expect(childRect.right, 800.0 - 100.0);

      childRect = tester.getRect(find.byKey(child2Key));
      expect(childRect.size.width, equals(100.0));
      expect(childRect.size.height, equals(100.0));
      expect(childRect.right, 800.0);
    });
  });

  group('button properties override ButtonTheme', () {

    testWidgets('default button properties override ButtonTheme properties', (WidgetTester tester) async {
      late BuildContext capturedContext;
      await tester.pumpWidget(
        MaterialApp(
          home: ButtonBar(
            children: <Widget>[
              Builder(builder: (BuildContext context) {
                capturedContext = context;
                return Container();
              }),
            ],
          ),
        ),
      );
      final ButtonThemeData buttonTheme = ButtonTheme.of(capturedContext);
      expect(buttonTheme.textTheme, equals(ButtonTextTheme.primary));
      expect(buttonTheme.minWidth, equals(64.0));
      expect(buttonTheme.height, equals(36.0));
      expect(buttonTheme.padding, equals(const EdgeInsets.symmetric(horizontal: 8.0)));
      expect(buttonTheme.alignedDropdown, equals(false));
      expect(buttonTheme.layoutBehavior, equals(ButtonBarLayoutBehavior.padded));
    });

    testWidgets('ButtonBarTheme button properties override defaults and ButtonTheme properties', (WidgetTester tester) async {
      late BuildContext capturedContext;
      await tester.pumpWidget(
        MaterialApp(
          home: ButtonBarTheme(
            data: const ButtonBarThemeData(
              buttonTextTheme: ButtonTextTheme.primary,
              buttonMinWidth: 42.0,
              buttonHeight: 84.0,
              buttonPadding: EdgeInsets.fromLTRB(10, 20, 30, 40),
              buttonAlignedDropdown: true,
              layoutBehavior: ButtonBarLayoutBehavior.constrained,
            ),
            child: ButtonBar(
              children: <Widget>[
                Builder(builder: (BuildContext context) {
                  capturedContext = context;
                  return Container();
                }),
              ],
            ),
          ),
        ),
      );
      final ButtonThemeData buttonTheme = ButtonTheme.of(capturedContext);
      expect(buttonTheme.textTheme, equals(ButtonTextTheme.primary));
      expect(buttonTheme.minWidth, equals(42.0));
      expect(buttonTheme.height, equals(84.0));
      expect(buttonTheme.padding, equals(const EdgeInsets.fromLTRB(10, 20, 30, 40)));
      expect(buttonTheme.alignedDropdown, equals(true));
      expect(buttonTheme.layoutBehavior, equals(ButtonBarLayoutBehavior.constrained));
    });

    testWidgets('ButtonBar button properties override ButtonBarTheme, defaults and ButtonTheme properties', (WidgetTester tester) async {
      late BuildContext capturedContext;
      await tester.pumpWidget(
        MaterialApp(
          home: ButtonBarTheme(
            data: const ButtonBarThemeData(
              buttonTextTheme: ButtonTextTheme.accent,
              buttonMinWidth: 4242.0,
              buttonHeight: 8484.0,
              buttonPadding: EdgeInsets.fromLTRB(50, 60, 70, 80),
              buttonAlignedDropdown: false,
              layoutBehavior: ButtonBarLayoutBehavior.padded,
            ),
            child: ButtonBar(
              buttonTextTheme: ButtonTextTheme.primary,
              buttonMinWidth: 42.0,
              buttonHeight: 84.0,
              buttonPadding: const EdgeInsets.fromLTRB(10, 20, 30, 40),
              buttonAlignedDropdown: true,
              layoutBehavior: ButtonBarLayoutBehavior.constrained,
              children: <Widget>[
                Builder(builder: (BuildContext context) {
                  capturedContext = context;
                  return Container();
                }),
              ],
            ),
          ),
        ),
      );
      final ButtonThemeData buttonTheme = ButtonTheme.of(capturedContext);
      expect(buttonTheme.textTheme, equals(ButtonTextTheme.primary));
      expect(buttonTheme.minWidth, equals(42.0));
      expect(buttonTheme.height, equals(84.0));
      expect(buttonTheme.padding, equals(const EdgeInsets.fromLTRB(10, 20, 30, 40)));
      expect(buttonTheme.alignedDropdown, equals(true));
      expect(buttonTheme.layoutBehavior, equals(ButtonBarLayoutBehavior.constrained));
    });

  });

  group('layoutBehavior', () {

    testWidgets('ButtonBar has a min height of 52 when using ButtonBarLayoutBehavior.constrained', (WidgetTester tester) async {
      await tester.pumpWidget(
        SingleChildScrollView(
          child: ListBody(
            children: const <Widget>[
              Directionality(
                textDirection: TextDirection.ltr,
                child: ButtonBar(
                  layoutBehavior: ButtonBarLayoutBehavior.constrained,
                  children: <Widget>[
                    SizedBox(width: 10.0, height: 10.0),
                  ],
                ),
              ),
            ],
          ),
        ),
      );

      final Finder buttonBar = find.byType(ButtonBar);
      expect(tester.getBottomRight(buttonBar).dy - tester.getTopRight(buttonBar).dy, 52.0);
    });

    testWidgets('ButtonBar has padding applied when using ButtonBarLayoutBehavior.padded', (WidgetTester tester) async {
      await tester.pumpWidget(
        SingleChildScrollView(
          child: ListBody(
            children: const <Widget>[
              Directionality(
                textDirection: TextDirection.ltr,
                child: ButtonBar(
                  layoutBehavior: ButtonBarLayoutBehavior.padded,
                  children: <Widget>[
                    SizedBox(width: 10.0, height: 10.0),
                  ],
                ),
              ),
            ],
          ),
        ),
      );

      final Finder buttonBar = find.byType(ButtonBar);
      expect(tester.getBottomRight(buttonBar).dy - tester.getTopRight(buttonBar).dy, 26.0);
    });
  });

  group("ButtonBar's children wrap when they overflow horizontally", () {
    testWidgets("ButtonBar's children wrap when buttons overflow", (WidgetTester tester) async {
      final Key keyOne = UniqueKey();
      final Key keyTwo = UniqueKey();
      await tester.pumpWidget(
        MaterialApp(
          home: ButtonBar(
            children: <Widget>[
              Container(key: keyOne, height: 50.0, width: 800.0),
              Container(key: keyTwo, height: 50.0, width: 800.0),
            ],
          ),
        ),
      );

      // Second [Container] should wrap around to the next column since
      // they take up max width constraint.
      final Rect containerOneRect = tester.getRect(find.byKey(keyOne));
      final Rect containerTwoRect = tester.getRect(find.byKey(keyTwo));
      expect(containerOneRect.bottom, containerTwoRect.top);
      expect(containerOneRect.left, containerTwoRect.left);
    });

    testWidgets(
      "ButtonBar's children overflow defaults - MainAxisAlignment.end", (WidgetTester tester) async {
        final Key keyOne = UniqueKey();
        final Key keyTwo = UniqueKey();
        await tester.pumpWidget(
          MaterialApp(
            home: ButtonBar(
              // Set padding to zero to align buttons with edge of button bar.
              buttonPadding: EdgeInsets.zero,
              children: <Widget>[
                Container(key: keyOne, height: 50.0, width: 500.0),
                Container(key: keyTwo, height: 50.0, width: 500.0),
              ],
            ),
          ),
        );

        final Rect buttonBarRect = tester.getRect(find.byType(ButtonBar));
        final Rect containerOneRect = tester.getRect(find.byKey(keyOne));
        final Rect containerTwoRect = tester.getRect(find.byKey(keyTwo));
        // Second [Container] should wrap around to the next row.
        expect(containerOneRect.bottom, containerTwoRect.top);
        // Second [Container] should align to the start of the ButtonBar.
        expect(containerOneRect.right, containerTwoRect.right);
        expect(containerOneRect.right, buttonBarRect.right);
      },
    );

    testWidgets("ButtonBar's children overflow - MainAxisAlignment.start", (WidgetTester tester) async {
      final Key keyOne = UniqueKey();
      final Key keyTwo = UniqueKey();
      await tester.pumpWidget(
        MaterialApp(
          home: ButtonBar(
            alignment: MainAxisAlignment.start,
            // Set padding to zero to align buttons with edge of button bar.
            buttonPadding: EdgeInsets.zero,
            children: <Widget>[
              Container(key: keyOne, height: 50.0, width: 500.0),
              Container(key: keyTwo, height: 50.0, width: 500.0),
            ],
          ),
        ),
      );

      final Rect buttonBarRect = tester.getRect(find.byType(ButtonBar));
      final Rect containerOneRect = tester.getRect(find.byKey(keyOne));
      final Rect containerTwoRect = tester.getRect(find.byKey(keyTwo));
      // Second [Container] should wrap around to the next row.
      expect(containerOneRect.bottom, containerTwoRect.top);
      // [Container]s should align to the end of the ButtonBar.
      expect(containerOneRect.left, containerTwoRect.left);
      expect(containerOneRect.left, buttonBarRect.left);
    });

    testWidgets("ButtonBar's children overflow - MainAxisAlignment.center", (WidgetTester tester) async {
      final Key keyOne = UniqueKey();
      final Key keyTwo = UniqueKey();
      await tester.pumpWidget(
        MaterialApp(
          home: ButtonBar(
            alignment: MainAxisAlignment.center,
            // Set padding to zero to align buttons with edge of button bar.
            buttonPadding: EdgeInsets.zero,
            children: <Widget>[
              Container(key: keyOne, height: 50.0, width: 500.0),
              Container(key: keyTwo, height: 50.0, width: 500.0),
            ],
          ),
        ),
      );

      final Rect buttonBarRect = tester.getRect(find.byType(ButtonBar));
      final Rect containerOneRect = tester.getRect(find.byKey(keyOne));
      final Rect containerTwoRect = tester.getRect(find.byKey(keyTwo));
      // Second [Container] should wrap around to the next row.
      expect(containerOneRect.bottom, containerTwoRect.top);
      // [Container]s should center themselves in the ButtonBar.
      expect(containerOneRect.center.dx, containerTwoRect.center.dx);
      expect(containerOneRect.center.dx, buttonBarRect.center.dx);
    });

    testWidgets(
      "ButtonBar's children default to MainAxisAlignment.start for horizontal "
      'alignment when overflowing in spaceBetween, spaceAround and spaceEvenly '
      'cases when overflowing.', (WidgetTester tester) async {
        final Key keyOne = UniqueKey();
        final Key keyTwo = UniqueKey();
        await tester.pumpWidget(
          MaterialApp(
            home: ButtonBar(
              alignment: MainAxisAlignment.spaceEvenly,
              // Set padding to zero to align buttons with edge of button bar.
              buttonPadding: EdgeInsets.zero,
              children: <Widget>[
                Container(key: keyOne, height: 50.0, width: 500.0),
                Container(key: keyTwo, height: 50.0, width: 500.0),
              ],
            ),
          ),
        );

        Rect buttonBarRect = tester.getRect(find.byType(ButtonBar));
        Rect containerOneRect = tester.getRect(find.byKey(keyOne));
        Rect containerTwoRect = tester.getRect(find.byKey(keyTwo));
        // Second [Container] should wrap around to the next row.
        expect(containerOneRect.bottom, containerTwoRect.top);
        // Should align horizontally to the start of the button bar.
        expect(containerOneRect.left, containerTwoRect.left);
        expect(containerOneRect.left, buttonBarRect.left);

        await tester.pumpWidget(
          MaterialApp(
            home: ButtonBar(
              alignment: MainAxisAlignment.spaceAround,
              // Set padding to zero to align buttons with edge of button bar.
              buttonPadding: EdgeInsets.zero,
              children: <Widget>[
                Container(key: keyOne, height: 50.0, width: 500.0),
                Container(key: keyTwo, height: 50.0, width: 500.0),
              ],
            ),
          ),
        );

        buttonBarRect = tester.getRect(find.byType(ButtonBar));
        containerOneRect = tester.getRect(find.byKey(keyOne));
        containerTwoRect = tester.getRect(find.byKey(keyTwo));
        // Second [Container] should wrap around to the next row.
        expect(containerOneRect.bottom, containerTwoRect.top);
        // Should align horizontally to the start of the button bar.
        expect(containerOneRect.left, containerTwoRect.left);
        expect(containerOneRect.left, buttonBarRect.left);
      },
    );

    testWidgets(
      "ButtonBar's children respects verticalDirection when overflowing",
      (WidgetTester tester) async {
        final Key keyOne = UniqueKey();
        final Key keyTwo = UniqueKey();
        await tester.pumpWidget(
          MaterialApp(
            home: ButtonBar(
              alignment: MainAxisAlignment.center,
              // Set padding to zero to align buttons with edge of button bar.
              buttonPadding: EdgeInsets.zero,
              // Set the vertical direction to start from the bottom and lay
              // out upwards.
              overflowDirection: VerticalDirection.up,
              children: <Widget>[
                Container(key: keyOne, height: 50.0, width: 500.0),
                Container(key: keyTwo, height: 50.0, width: 500.0),
              ],
            ),
          ),
        );

        final Rect containerOneRect = tester.getRect(find.byKey(keyOne));
        final Rect containerTwoRect = tester.getRect(find.byKey(keyTwo));
        // Second [Container] should appear above first container.
        expect(containerTwoRect.bottom, lessThanOrEqualTo(containerOneRect.top));
      },
    );

    testWidgets(
      'ButtonBar has no spacing by default when overflowing',
      (WidgetTester tester) async {
        final Key keyOne = UniqueKey();
        final Key keyTwo = UniqueKey();
        await tester.pumpWidget(
          MaterialApp(
            home: ButtonBar(
              alignment: MainAxisAlignment.center,
              // Set padding to zero to align buttons with edge of button bar.
              buttonPadding: EdgeInsets.zero,
              children: <Widget>[
                Container(key: keyOne, height: 50.0, width: 500.0),
                Container(key: keyTwo, height: 50.0, width: 500.0),
              ],
            ),
          ),
        );

        final Rect containerOneRect = tester.getRect(find.byKey(keyOne));
        final Rect containerTwoRect = tester.getRect(find.byKey(keyTwo));
        expect(containerOneRect.bottom, containerTwoRect.top);
      },
    );

    testWidgets(
      "ButtonBar's children respects overflowButtonSpacing when overflowing",
      (WidgetTester tester) async {
        final Key keyOne = UniqueKey();
        final Key keyTwo = UniqueKey();
        await tester.pumpWidget(
          MaterialApp(
            home: ButtonBar(
              alignment: MainAxisAlignment.center,
              // Set padding to zero to align buttons with edge of button bar.
              buttonPadding: EdgeInsets.zero,
              // Set the overflow button spacing to ensure add some space between
              // buttons in an overflow case.
              overflowButtonSpacing: 10.0,
              children: <Widget>[
                Container(key: keyOne, height: 50.0, width: 500.0),
                Container(key: keyTwo, height: 50.0, width: 500.0),
              ],
            ),
          ),
        );

        final Rect containerOneRect = tester.getRect(find.byKey(keyOne));
        final Rect containerTwoRect = tester.getRect(find.byKey(keyTwo));
        expect(containerOneRect.bottom, containerTwoRect.top - 10.0);
      },
    );
  });

  testWidgets('_RenderButtonBarRow.constraints does not work before layout', (WidgetTester tester) async {
    await tester.pumpWidget(
      const MaterialApp(home: ButtonBar()),
      Duration.zero,
      EnginePhase.build,
    );

    final Finder buttonBar = find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_ButtonBarRow');
    final RenderBox renderButtonBar = tester.renderObject(buttonBar) as RenderBox;

    expect(renderButtonBar.debugNeedsLayout, isTrue);
    expect(() => renderButtonBar.constraints, throwsStateError);
  });
}
