// Copyright 2016 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_test/flutter_test.dart';
import 'package:flutter/widgets.dart';

// Assuming that the test container is 800x600. The height of the
// viewport's contents is 650.0, the top and bottom text children
// are 100 pixels high and top/left edge of both widgets are visible.
// The top of the bottom widget is at 550 (the top of the top widget
// is at 0). The top of the bottom widget is 500 when it has been
// scrolled completely into view.
Widget buildFrame(ScrollableEdge clampedEdge) {
  return new ClampOverscrolls(
    edge: clampedEdge,
    child: new ScrollableViewport(
      key: new UniqueKey(),
      child: new SizedBox(
        height: 650.0,
        child: new Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            new SizedBox(height: 100.0, child: new Text('top')),
            new Expanded(child: new Container()),
            new SizedBox(height: 100.0, child: new Text('bottom')),
          ]
        )
      )
    )
  );
}

void main() {
  testWidgets('ClampOverscrolls', (WidgetTester tester) async {

    // Scroll the target text widget by offset and then return its origin
    // in global coordinates.
    Future<Point> locationAfterScroll(String target, Offset offset) async {
      await tester.scrollAt(tester.getTopLeft(find.text(target)), offset);
      await tester.pump();
      final RenderBox textBox = tester.renderObject(find.text(target));
      final Point widgetOrigin = textBox.localToGlobal(Point.origin);
      await tester.pump(const Duration(seconds: 1)); // Allow overscroll to settle
      return new Future<Point>.value(widgetOrigin);
    }

    // Each of the blocks below test overscrolling the top and bottom
    // with a value for ClampOverscrolls.edge.

    await tester.pumpWidget(buildFrame(ScrollableEdge.none));
    Point origin = await locationAfterScroll('top', const Offset(0.0, 400.0));
    expect(origin.y, greaterThan(0.0));
    origin = await locationAfterScroll('bottom', const Offset(0.0, -400.0));
    expect(origin.y, lessThan(500.0));


    await tester.pumpWidget(buildFrame(ScrollableEdge.both));
    origin = await locationAfterScroll('top', const Offset(0.0, 400.0));
    expect(origin.y, equals(0.0));
    origin = await locationAfterScroll('bottom', const Offset(0.0, -400.0));
    expect(origin.y, equals(500.0));

    await tester.pumpWidget(buildFrame(ScrollableEdge.leading));
    origin = await locationAfterScroll('top', const Offset(0.0, 400.0));
    expect(origin.y, equals(0.0));
    origin = await locationAfterScroll('bottom', const Offset(0.0, -400.0));
    expect(origin.y, lessThan(500.0));

    await tester.pumpWidget(buildFrame(ScrollableEdge.trailing));
    origin = await locationAfterScroll('top', const Offset(0.0, 400.0));
    expect(origin.y, greaterThan(0.0));
    origin = await locationAfterScroll('bottom', const Offset(0.0, -400.0));
    expect(origin.y, equals(500.0));
  });

  testWidgets('ClampOverscrolls affects scrollOffset not virtualScrollOffset', (WidgetTester tester) async {

    // ClampOverscrolls.edge == ScrollableEdge.none

    await tester.pumpWidget(buildFrame(ScrollableEdge.none));
    StatefulElement statefulElement = tester.element(find.byType(Scrollable));
    ScrollableState scrollable = statefulElement.state;

    await tester.scrollAt(tester.getTopLeft(find.text('top')), const Offset(0.0, 400.0));
    await tester.pump();
    expect(scrollable.scrollOffset, lessThan(0.0));
    expect(scrollable.virtualScrollOffset, equals(scrollable.scrollOffset));
    await tester.pump(const Duration(seconds: 1)); // Allow overscroll to settle

    await tester.scrollAt(tester.getTopLeft(find.text('bottom')), const Offset(0.0, -400.0));
    await tester.pump();
    expect(scrollable.scrollOffset, greaterThan(0.0));
    expect(scrollable.virtualScrollOffset, equals(scrollable.scrollOffset));
    await tester.pump(const Duration(seconds: 1)); // Allow overscroll to settle

    // ClampOverscrolls.edge == ScrollableEdge.both

    await tester.pumpWidget(buildFrame(ScrollableEdge.both));
    statefulElement = tester.element(find.byType(Scrollable));
    scrollable = statefulElement.state;

    await tester.scrollAt(tester.getTopLeft(find.text('top')), const Offset(0.0, 400.0));
    await tester.pump();
    expect(scrollable.scrollOffset, equals(0.0));
    expect(scrollable.virtualScrollOffset, lessThan(0.0));
    await tester.pump(const Duration(seconds: 1)); // Allow overscroll to settle

    await tester.scrollAt(tester.getTopLeft(find.text('bottom')), const Offset(0.0, -400.0));
    await tester.pump();
    expect(scrollable.scrollOffset, equals(50.0));
    expect(scrollable.virtualScrollOffset, greaterThan(50.0));

    // ClampOverscrolls.edge == ScrollableEdge.leading

    await tester.pumpWidget(buildFrame(ScrollableEdge.leading));
    statefulElement = tester.element(find.byType(Scrollable));
    scrollable = statefulElement.state;

    await tester.scrollAt(tester.getTopLeft(find.text('top')), const Offset(0.0, 400.0));
    await tester.pump();
    expect(scrollable.scrollOffset, equals(0.0));
    expect(scrollable.virtualScrollOffset, lessThan(0.0));
    await tester.pump(const Duration(seconds: 1)); // Allow overscroll to settle

    await tester.scrollAt(tester.getTopLeft(find.text('bottom')), const Offset(0.0, -400.0));
    await tester.pump();
    expect(scrollable.scrollOffset, greaterThan(0.0));
    expect(scrollable.virtualScrollOffset, equals(scrollable.scrollOffset));

    // ClampOverscrolls.edge == ScrollableEdge.trailing

    await tester.pumpWidget(buildFrame(ScrollableEdge.trailing));
    statefulElement = tester.element(find.byType(Scrollable));
    scrollable = statefulElement.state;

    await tester.scrollAt(tester.getTopLeft(find.text('top')), const Offset(0.0, 400.0));
    await tester.pump();
    expect(scrollable.scrollOffset, lessThan(0.0));
    expect(scrollable.virtualScrollOffset, equals(scrollable.scrollOffset));
    expect(scrollable.virtualScrollOffset, equals(scrollable.scrollOffset));
    await tester.pump(const Duration(seconds: 1)); // Allow overscroll to settle

    await tester.scrollAt(tester.getTopLeft(find.text('bottom')), const Offset(0.0, -400.0));
    await tester.pump();
    expect(scrollable.scrollOffset, equals(50.0));
    expect(scrollable.virtualScrollOffset, greaterThan(50.0));
  });
}
