// 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/rendering.dart';
import 'package:flutter/physics.dart';
import 'package:flutter/material.dart';

class TestSliverPersistentHeaderDelegate extends SliverPersistentHeaderDelegate {
  TestSliverPersistentHeaderDelegate(this._maxExtent);

  final double _maxExtent;

  @override
  double get maxExtent => _maxExtent;

  @override
  double get minExtent => 16.0;

  @override
  Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
    return new Column(
      children: <Widget>[
        new Container(height: minExtent),
        new Expanded(child: new Container()),
      ],
    );
  }

  @override
  bool shouldRebuild(TestSliverPersistentHeaderDelegate oldDelegate) => false;
}

class TestBehavior extends ScrollBehavior {
  @override
  Widget buildViewportChrome(BuildContext context, Widget child, AxisDirection axisDirection) {
    return new GlowingOverscrollIndicator(
      child: child,
      axisDirection: axisDirection,
      color: const Color(0xFFFFFFFF),
    );
  }
}

class TestScrollPhysics extends ClampingScrollPhysics {
  const TestScrollPhysics({ ScrollPhysics parent }) : super(parent: parent);

  @override
  TestScrollPhysics applyTo(ScrollPhysics ancestor) {
    return new TestScrollPhysics(parent: parent?.applyTo(ancestor) ?? ancestor);
  }

  @override
  Tolerance get tolerance => const Tolerance(velocity: 20.0, distance: 1.0);
}

class TestViewportScrollPosition extends ScrollPositionWithSingleContext {
  TestViewportScrollPosition({
    ScrollPhysics physics,
    ScrollContext context,
    ScrollPosition oldPosition,
  }) : super(physics: physics, context: context, oldPosition: oldPosition);

  @override
  bool applyContentDimensions(double minScrollExtent, double maxScrollExtent) {
    expect(minScrollExtent, moreOrLessEquals(-3895.0));
    expect(maxScrollExtent, moreOrLessEquals(8575.0));
    return super.applyContentDimensions(minScrollExtent, maxScrollExtent);
  }
}

void main() {
  testWidgets('Evil test of sliver features - 1', (WidgetTester tester) async {
    final GlobalKey centerKey = new GlobalKey();
    await tester.pumpWidget(
      new ScrollConfiguration(
        behavior: new TestBehavior(),
        child: new Scrollbar(
          child: new Scrollable(
            axisDirection: AxisDirection.down,
            physics: const TestScrollPhysics(),
            viewportBuilder: (BuildContext context, ViewportOffset offset) {
              return new Viewport(
                axisDirection: AxisDirection.down,
                anchor: 0.25,
                offset: offset,
                center: centerKey,
                slivers: <Widget>[
                  new SliverToBoxAdapter(child: new Container(height: 5.0)),
                  new SliverToBoxAdapter(child: new Container(height: 520.0)),
                  new SliverToBoxAdapter(child: new Container(height: 520.0)),
                  new SliverToBoxAdapter(child: new Container(height: 520.0)),
                  new SliverPersistentHeader(delegate: new TestSliverPersistentHeaderDelegate(150.0), pinned: true),
                  new SliverToBoxAdapter(child: new Container(height: 520.0)),
                  new SliverPadding(
                    padding: const EdgeInsets.all(50.0),
                    sliver: new SliverToBoxAdapter(child: new Container(height: 520.0)),
                  ),
                  new SliverToBoxAdapter(child: new Container(height: 520.0)),
                  new SliverPersistentHeader(delegate: new TestSliverPersistentHeaderDelegate(150.0), floating: true),
                  new SliverToBoxAdapter(child: new Container(height: 520.0)),
                  new SliverToBoxAdapter(key: centerKey, child: new Container(height: 520.0)), // ------------------------ CENTER ------------------------
                  new SliverPersistentHeader(delegate: new TestSliverPersistentHeaderDelegate(150.0), pinned: true),
                  new SliverToBoxAdapter(child: new Container(height: 520.0)),
                  new SliverToBoxAdapter(child: new Container(height: 520.0)),
                  new SliverToBoxAdapter(child: new Container(height: 520.0)),
                  new SliverPadding(
                    padding: const EdgeInsets.all(50.0),
                    sliver: new SliverPersistentHeader(delegate: new TestSliverPersistentHeaderDelegate(250.0), pinned: true),
                  ),
                  new SliverToBoxAdapter(child: new Container(height: 520.0)),
                  new SliverPersistentHeader(delegate: new TestSliverPersistentHeaderDelegate(250.0), pinned: true),
                  new SliverToBoxAdapter(child: new Container(height: 5.0)),
                  new SliverPersistentHeader(delegate: new TestSliverPersistentHeaderDelegate(250.0), pinned: true),
                  new SliverToBoxAdapter(child: new Container(height: 5.0)),
                  new SliverPersistentHeader(delegate: new TestSliverPersistentHeaderDelegate(250.0), pinned: true),
                  new SliverPersistentHeader(delegate: new TestSliverPersistentHeaderDelegate(250.0), pinned: true),
                  new SliverToBoxAdapter(child: new Container(height: 5.0)),
                  new SliverPersistentHeader(delegate: new TestSliverPersistentHeaderDelegate(250.0), pinned: true),
                  new SliverPersistentHeader(delegate: new TestSliverPersistentHeaderDelegate(250.0)),
                  new SliverToBoxAdapter(child: new Container(height: 520.0)),
                  new SliverPersistentHeader(delegate: new TestSliverPersistentHeaderDelegate(150.0), floating: true),
                  new SliverToBoxAdapter(child: new Container(height: 520.0)),
                  new SliverPersistentHeader(delegate: new TestSliverPersistentHeaderDelegate(150.0), floating: true),
                  new SliverToBoxAdapter(child: new Container(height: 5.0)),
                  new SliverList(
                    delegate: new SliverChildListDelegate(<Widget>[
                      new Container(height: 50.0),
                      new Container(height: 50.0),
                      new Container(height: 50.0),
                      new Container(height: 50.0),
                      new Container(height: 50.0),
                      new Container(height: 50.0),
                      new Container(height: 50.0),
                      new Container(height: 50.0),
                      new Container(height: 50.0),
                      new Container(height: 50.0),
                      new Container(height: 50.0),
                      new Container(height: 50.0),
                      new Container(height: 50.0),
                      new Container(height: 50.0),
                      new Container(height: 50.0),
                    ]),
                  ),
                  new SliverPersistentHeader(delegate: new TestSliverPersistentHeaderDelegate(250.0)),
                  new SliverPersistentHeader(delegate: new TestSliverPersistentHeaderDelegate(250.0)),
                  new SliverPersistentHeader(delegate: new TestSliverPersistentHeaderDelegate(250.0)),
                  new SliverPadding(
                    padding: const EdgeInsets.symmetric(horizontal: 50.0),
                    sliver: new SliverToBoxAdapter(child: new Container(height: 520.0)),
                  ),
                  new SliverToBoxAdapter(child: new Container(height: 520.0)),
                  new SliverToBoxAdapter(child: new Container(height: 520.0)),
                  new SliverToBoxAdapter(child: new Container(height: 5.0)),
                ],
              );
            },
          ),
        ),
      ),
    );
    final ScrollPosition position = tester.state<ScrollableState>(find.byType(Scrollable)).position;

    position.animateTo(10000.0, curve: Curves.linear, duration: const Duration(minutes: 1));
    await tester.pump(const Duration(milliseconds: 10));
    await tester.pump(const Duration(milliseconds: 10));
    await tester.pump(const Duration(milliseconds: 50));
    await tester.pumpAndSettle(const Duration(milliseconds: 122));

    position.animateTo(-10000.0, curve: Curves.linear, duration: const Duration(minutes: 1));
    await tester.pump(const Duration(milliseconds: 10));
    await tester.pump(const Duration(milliseconds: 10));
    await tester.pump(const Duration(milliseconds: 50));
    await tester.pumpAndSettle(const Duration(milliseconds: 122));

    position.animateTo(10000.0, curve: Curves.linear, duration: const Duration(minutes: 1));
    await tester.pump(const Duration(milliseconds: 10));
    await tester.pump(const Duration(milliseconds: 10));
    await tester.pump(const Duration(milliseconds: 50));
    await tester.pumpAndSettle(const Duration(milliseconds: 122));

    position.animateTo(-10000.0, curve: Curves.linear, duration: const Duration(seconds: 1));
    await tester.pump(const Duration(milliseconds: 10));
    await tester.pump(const Duration(milliseconds: 10));
    await tester.pump(const Duration(milliseconds: 50));
    await tester.pumpAndSettle(const Duration(milliseconds: 122));

    position.animateTo(10000.0, curve: Curves.linear, duration: const Duration(seconds: 1));
    await tester.pump(const Duration(milliseconds: 10));
    await tester.pump(const Duration(milliseconds: 10));
    await tester.pump(const Duration(milliseconds: 50));
    await tester.pumpAndSettle(const Duration(milliseconds: 122));

  });
}