// 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/widgets.dart';

int globalGeneration = 0;

class GenerationText extends StatefulWidget {
  GenerationText(this.value);
  final int value;
  @override
  _GenerationTextState createState() => new _GenerationTextState();
}

class _GenerationTextState extends State<GenerationText> {
  _GenerationTextState() : generation = globalGeneration;
  final int generation;
  @override
  Widget build(BuildContext context) => new Text('${config.value}:$generation ');
}

Future<Null> test(WidgetTester tester, double offset, List<int> keys) {
  globalGeneration += 1;
  return tester.pumpWidget(new Viewport2(
    offset: new ViewportOffset.fixed(offset),
    slivers: <Widget>[
      new SliverBlock(
        delegate: new SliverChildListDelegate(keys.map((int key) {
          return new SizedBox(key: new GlobalObjectKey(key), height: 100.0, child: new GenerationText(key));
        }).toList()),
      ),
    ],
  ));
}

void verify(WidgetTester tester, List<Point> answerKey, String text) {
  List<Point> testAnswers = tester.renderObjectList<RenderBox>(find.byType(SizedBox)).map<Point>(
    (RenderBox target) => target.localToGlobal(const Point(0.0, 0.0))
  ).toList();
  expect(testAnswers, equals(answerKey));
  final String foundText =
    tester.widgetList<Text>(find.byType(Text))
    .map<String>((Text widget) => widget.data)
    .reduce((String value, String element) => value + element);
  expect(foundText, equals(text));
}

void main() {
  testWidgets('Viewport2+SliverBlock with GlobalKey reparenting', (WidgetTester tester) async {
    await test(tester, 0.0, <int>[1,2,3,4,5,6,7,8,9]);
    verify(tester, <Point>[
      const Point(0.0, 0.0),
      const Point(0.0, 100.0),
      const Point(0.0, 200.0),
      const Point(0.0, 300.0),
      const Point(0.0, 400.0),
      const Point(0.0, 500.0),
    ], '1:1 2:1 3:1 4:1 5:1 6:1 ');
    // gen 2 - flipping the order:
    await test(tester, 0.0, <int>[9,8,7,6,5,4,3,2,1]);
    verify(tester, <Point>[
      const Point(0.0, 0.0),
      const Point(0.0, 100.0),
      const Point(0.0, 200.0),
      const Point(0.0, 300.0),
      const Point(0.0, 400.0),
      const Point(0.0, 500.0),
    ], '9:2 8:2 7:2 6:1 5:1 4:1 ');
    // gen 3 - flipping the order back:
    await test(tester, 0.0, <int>[1,2,3,4,5,6,7,8,9]);
    verify(tester, <Point>[
      const Point(0.0, 0.0),
      const Point(0.0, 100.0),
      const Point(0.0, 200.0),
      const Point(0.0, 300.0),
      const Point(0.0, 400.0),
      const Point(0.0, 500.0),
    ], '1:3 2:3 3:3 4:1 5:1 6:1 ');
    // gen 4 - removal:
    await test(tester, 0.0, <int>[1,2,3,5,6,7,8,9]);
    verify(tester, <Point>[
      const Point(0.0, 0.0),
      const Point(0.0, 100.0),
      const Point(0.0, 200.0),
      const Point(0.0, 300.0),
      const Point(0.0, 400.0),
      const Point(0.0, 500.0),
    ], '1:3 2:3 3:3 5:1 6:1 7:4 ');
    // gen 5 - insertion:
    await test(tester, 0.0, <int>[1,2,3,4,5,6,7,8,9]);
    verify(tester, <Point>[
      const Point(0.0, 0.0),
      const Point(0.0, 100.0),
      const Point(0.0, 200.0),
      const Point(0.0, 300.0),
      const Point(0.0, 400.0),
      const Point(0.0, 500.0),
    ], '1:3 2:3 3:3 4:5 5:1 6:1 ');
    // gen 6 - adjacent reordering:
    await test(tester, 0.0, <int>[1,2,3,5,4,6,7,8,9]);
    verify(tester, <Point>[
      const Point(0.0, 0.0),
      const Point(0.0, 100.0),
      const Point(0.0, 200.0),
      const Point(0.0, 300.0),
      const Point(0.0, 400.0),
      const Point(0.0, 500.0),
    ], '1:3 2:3 3:3 5:1 4:5 6:1 ');
    // gen 7 - scrolling:
    await test(tester, 120.0, <int>[1,2,3,5,4,6,7,8,9]);
    verify(tester, <Point>[
      const Point(0.0, -20.0),
      const Point(0.0, 80.0),
      const Point(0.0, 180.0),
      const Point(0.0, 280.0),
      const Point(0.0, 380.0),
      const Point(0.0, 480.0),
      const Point(0.0, 580.0),
    ], '2:3 3:3 5:1 4:5 6:1 7:7 8:7 ');
  });
}