// Copyright 2015 The Chromium 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/rendering.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:test/test.dart';

import 'test_semantics.dart';

void main() {
  test('Does tooltip end up in the right place - top left', () {
    testWidgets((WidgetTester tester) {
      GlobalKey key = new GlobalKey();
      tester.pumpWidget(
        new Overlay(
          initialEntries: <OverlayEntry>[
            new OverlayEntry(
              builder: (BuildContext context) {
                return new Stack(
                  children: <Widget>[
                    new Positioned(
                      left: 0.0,
                      top: 0.0,
                      child: new Tooltip(
                        key: key,
                        message: 'TIP',
                        height: 20.0,
                        padding: const EdgeDims.all(5.0),
                        verticalOffset: 20.0,
                        screenEdgeMargin: const EdgeDims.all(10.0),
                        preferBelow: false,
                        fadeDuration: const Duration(seconds: 1),
                        showDuration: const Duration(seconds: 2),
                        child: new Container(
                          width: 0.0,
                          height: 0.0
                        )
                      )
                    ),
                  ]
                );
              }
            ),
          ]
        )
      );
      key.currentState.showTooltip();
      tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)

      /********************* 800x600 screen
       *o                  * y=0
       *|                  * }- 20.0 vertical offset, of which 10.0 is in the screen edge margin
       *+----+             * \- (5.0 padding in height)
       *|    |             * |- 20 height
       *+----+             * /- (5.0 padding in height)
       *                   *
       *********************/

      RenderBox tip = tester.findText('TIP').renderObject.parent.parent.parent.parent.parent;
      expect(tip.size.height, equals(20.0)); // 10.0 height + 5.0 padding * 2 (top, bottom)
      expect(tip.localToGlobal(tip.size.topLeft(Point.origin)), equals(const Point(10.0, 20.0)));
    });
  });

  test('Does tooltip end up in the right place - center prefer above fits', () {
    testWidgets((WidgetTester tester) {
      GlobalKey key = new GlobalKey();
      tester.pumpWidget(
        new Overlay(
          initialEntries: <OverlayEntry>[
            new OverlayEntry(
              builder: (BuildContext context) {
                return new Stack(
                  children: <Widget>[
                    new Positioned(
                      left: 400.0,
                      top: 300.0,
                      child: new Tooltip(
                        key: key,
                        message: 'TIP',
                        height: 100.0,
                        padding: const EdgeDims.all(0.0),
                        verticalOffset: 100.0,
                        screenEdgeMargin: const EdgeDims.all(100.0),
                        preferBelow: false,
                        fadeDuration: const Duration(seconds: 1),
                        showDuration: const Duration(seconds: 2),
                        child: new Container(
                          width: 0.0,
                          height: 0.0
                        )
                      )
                    ),
                  ]
                );
              }
            ),
          ]
        )
      );
      key.currentState.showTooltip();
      tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)

      /********************* 800x600 screen
       *        ___        * }-100.0 margin
       *       |___|       * }-100.0 height
       *         |         * }-100.0 vertical offset
       *         o         * y=300.0
       *                   *
       *                   *
       *                   *
       *********************/

      RenderBox tip = tester.findText('TIP').renderObject.parent;
      expect(tip.size.height, equals(100.0));
      expect(tip.localToGlobal(tip.size.topLeft(Point.origin)).y, equals(100.0));
      expect(tip.localToGlobal(tip.size.bottomRight(Point.origin)).y, equals(200.0));
    });
  });

  test('Does tooltip end up in the right place - center prefer above does not fit', () {
    testWidgets((WidgetTester tester) {
      GlobalKey key = new GlobalKey();
      tester.pumpWidget(
        new Overlay(
          initialEntries: <OverlayEntry>[
            new OverlayEntry(
              builder: (BuildContext context) {
                return new Stack(
                  children: <Widget>[
                    new Positioned(
                      left: 400.0,
                      top: 299.0,
                      child: new Tooltip(
                        key: key,
                        message: 'TIP',
                        height: 100.0,
                        padding: const EdgeDims.all(0.0),
                        verticalOffset: 100.0,
                        screenEdgeMargin: const EdgeDims.all(100.0),
                        preferBelow: false,
                        fadeDuration: const Duration(seconds: 1),
                        showDuration: const Duration(seconds: 2),
                        child: new Container(
                          width: 0.0,
                          height: 0.0
                        )
                      )
                    ),
                  ]
                );
              }
            ),
          ]
        )
      );
      key.currentState.showTooltip();
      tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)

      // we try to put it here but it doesn't fit:
      /********************* 800x600 screen
       *        ___        * }-100.0 margin
       *       |___|       * }-100.0 height (starts at y=99.0)
       *         |         * }-100.0 vertical offset
       *         o         * y=299.0
       *                   *
       *                   *
       *                   *
       *********************/

      // so we put it here:
      /********************* 800x600 screen
       *                   *
       *                   *
       *         o         * y=299.0
       *        _|_        * }-100.0 vertical offset
       *       |___|       * }-100.0 height
       *                   * }-100.0 margin
       *********************/

      RenderBox tip = tester.findText('TIP').renderObject.parent;
      expect(tip.size.height, equals(100.0));
      expect(tip.localToGlobal(tip.size.topLeft(Point.origin)).y, equals(399.0));
      expect(tip.localToGlobal(tip.size.bottomRight(Point.origin)).y, equals(499.0));
    });
  });

  test('Does tooltip end up in the right place - center prefer below fits', () {
    testWidgets((WidgetTester tester) {
      GlobalKey key = new GlobalKey();
      tester.pumpWidget(
        new Overlay(
          initialEntries: <OverlayEntry>[
            new OverlayEntry(
              builder: (BuildContext context) {
                return new Stack(
                  children: <Widget>[
                    new Positioned(
                      left: 400.0,
                      top: 300.0,
                      child: new Tooltip(
                        key: key,
                        message: 'TIP',
                        height: 100.0,
                        padding: const EdgeDims.all(0.0),
                        verticalOffset: 100.0,
                        screenEdgeMargin: const EdgeDims.all(100.0),
                        preferBelow: true,
                        fadeDuration: const Duration(seconds: 1),
                        showDuration: const Duration(seconds: 2),
                        child: new Container(
                          width: 0.0,
                          height: 0.0
                        )
                      )
                    ),
                  ]
                );
              }
            ),
          ]
        )
      );
      key.currentState.showTooltip();
      tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)

      /********************* 800x600 screen
       *                   *
       *                   *
       *         o         * y=300.0
       *        _|_        * }-100.0 vertical offset
       *       |___|       * }-100.0 height
       *                   * }-100.0 margin
       *********************/

      RenderBox tip = tester.findText('TIP').renderObject.parent;
      expect(tip.size.height, equals(100.0));
      expect(tip.localToGlobal(tip.size.topLeft(Point.origin)).y, equals(400.0));
      expect(tip.localToGlobal(tip.size.bottomRight(Point.origin)).y, equals(500.0));
    });
  });

  test('Does tooltip end up in the right place - way off to the right', () {
    testWidgets((WidgetTester tester) {
      GlobalKey key = new GlobalKey();
      tester.pumpWidget(
        new Overlay(
          initialEntries: <OverlayEntry>[
            new OverlayEntry(
              builder: (BuildContext context) {
                return new Stack(
                  children: <Widget>[
                    new Positioned(
                      left: 1600.0,
                      top: 300.0,
                      child: new Tooltip(
                        key: key,
                        message: 'TIP',
                        height: 10.0,
                        padding: const EdgeDims.all(0.0),
                        verticalOffset: 10.0,
                        screenEdgeMargin: const EdgeDims.all(10.0),
                        preferBelow: true,
                        fadeDuration: const Duration(seconds: 1),
                        showDuration: const Duration(seconds: 2),
                        child: new Container(
                          width: 0.0,
                          height: 0.0
                        )
                      )
                    ),
                  ]
                );
              }
            ),
          ]
        )
      );
      key.currentState.showTooltip();
      tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)

      /********************* 800x600 screen
       *                   *
       *                   *
       *                   * y=300.0;   target -->   o
       *              ___| * }-10.0 vertical offset
       *             |___| * }-10.0 height
       *                   *
       *                   * }-10.0 margin
       *********************/

      RenderBox tip = tester.findText('TIP').renderObject.parent;
      expect(tip.size.height, equals(10.0));
      expect(tip.localToGlobal(tip.size.topLeft(Point.origin)).y, equals(310.0));
      expect(tip.localToGlobal(tip.size.bottomRight(Point.origin)).x, equals(790.0));
      expect(tip.localToGlobal(tip.size.bottomRight(Point.origin)).y, equals(320.0));
    });
  });

  test('Does tooltip end up in the right place - near the edge', () {
    testWidgets((WidgetTester tester) {
      GlobalKey key = new GlobalKey();
      tester.pumpWidget(
        new Overlay(
          initialEntries: <OverlayEntry>[
            new OverlayEntry(
              builder: (BuildContext context) {
                return new Stack(
                  children: <Widget>[
                    new Positioned(
                      left: 780.0,
                      top: 300.0,
                      child: new Tooltip(
                        key: key,
                        message: 'TIP',
                        height: 10.0,
                        padding: const EdgeDims.all(0.0),
                        verticalOffset: 10.0,
                        screenEdgeMargin: const EdgeDims.all(10.0),
                        preferBelow: true,
                        fadeDuration: const Duration(seconds: 1),
                        showDuration: const Duration(seconds: 2),
                        child: new Container(
                          width: 0.0,
                          height: 0.0
                        )
                      )
                    ),
                  ]
                );
              }
            ),
          ]
        )
      );
      key.currentState.showTooltip();
      tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)

      /********************* 800x600 screen
       *                   *
       *                   *
       *                o  * y=300.0
       *              __|  * }-10.0 vertical offset
       *             |___| * }-10.0 height
       *                   *
       *                   * }-10.0 margin
       *********************/

      RenderBox tip = tester.findText('TIP').renderObject.parent;
      expect(tip.size.height, equals(10.0));
      expect(tip.localToGlobal(tip.size.topLeft(Point.origin)).y, equals(310.0));
      expect(tip.localToGlobal(tip.size.bottomRight(Point.origin)).x, equals(790.0));
      expect(tip.localToGlobal(tip.size.bottomRight(Point.origin)).y, equals(320.0));
    });
  });

  test('Does tooltip contribute semantics', () {
    testWidgets((WidgetTester tester) {
      TestSemanticsListener client = new TestSemanticsListener();
      GlobalKey key = new GlobalKey();
      tester.pumpWidget(
        new Overlay(
          initialEntries: <OverlayEntry>[
            new OverlayEntry(
              builder: (BuildContext context) {
                return new Stack(
                  children: <Widget>[
                    new Positioned(
                      left: 780.0,
                      top: 300.0,
                      child: new Tooltip(
                        key: key,
                        message: 'TIP',
                        fadeDuration: const Duration(seconds: 1),
                        showDuration: const Duration(seconds: 2),
                        child: new Container(width: 0.0, height: 0.0)
                      )
                    ),
                  ]
                );
              }
            ),
          ]
        )
      );
      expect(client.updates.length, equals(2));
      expect(client.updates[0].id, equals(0));
      expect(client.updates[0].flags.canBeTapped, isFalse);
      expect(client.updates[0].flags.canBeLongPressed, isFalse);
      expect(client.updates[0].flags.canBeScrolledHorizontally, isFalse);
      expect(client.updates[0].flags.canBeScrolledVertically, isFalse);
      expect(client.updates[0].flags.hasCheckedState, isFalse);
      expect(client.updates[0].flags.isChecked, isFalse);
      expect(client.updates[0].strings.label, equals('TIP'));
      expect(client.updates[0].geometry.transform, isNull);
      expect(client.updates[0].geometry.left, equals(0.0));
      expect(client.updates[0].geometry.top, equals(0.0));
      expect(client.updates[0].geometry.width, equals(800.0));
      expect(client.updates[0].geometry.height, equals(600.0));
      expect(client.updates[0].children.length, equals(0));
      expect(client.updates[1], isNull);
      client.updates.clear();

      key.currentState.showTooltip(); // this triggers a rebuild of the semantics because the tree changes

      tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)
      expect(client.updates.length, equals(2));
      expect(client.updates[0].id, equals(0));
      expect(client.updates[0].flags.canBeTapped, isFalse);
      expect(client.updates[0].flags.canBeLongPressed, isFalse);
      expect(client.updates[0].flags.canBeScrolledHorizontally, isFalse);
      expect(client.updates[0].flags.canBeScrolledVertically, isFalse);
      expect(client.updates[0].flags.hasCheckedState, isFalse);
      expect(client.updates[0].flags.isChecked, isFalse);
      expect(client.updates[0].strings.label, equals('TIP'));
      expect(client.updates[0].geometry.transform, isNull);
      expect(client.updates[0].geometry.left, equals(0.0));
      expect(client.updates[0].geometry.top, equals(0.0));
      expect(client.updates[0].geometry.width, equals(800.0));
      expect(client.updates[0].geometry.height, equals(600.0));
      expect(client.updates[0].children.length, equals(0));
      expect(client.updates[1], isNull);
      client.updates.clear();
    });
  });
}