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

class StateMarker extends StatefulWidget {
  StateMarker({ Key key, this.child }) : super(key: key);

  final Widget child;

  @override
  StateMarkerState createState() => new StateMarkerState();
}

class StateMarkerState extends State<StateMarker> {
  String marker;

  @override
  Widget build(BuildContext context) {
    if (config.child != null)
      return config.child;
    return new Container();
  }
}

class DeactivateLogger extends StatefulWidget {
  DeactivateLogger({ Key key, this.log }) : super(key: key);

  final List<String> log;

  @override
  DeactivateLoggerState createState() => new DeactivateLoggerState();
}

class DeactivateLoggerState extends State<DeactivateLogger> {
  @override
  void deactivate() {
    config.log.add('deactivate');
    super.deactivate();
  }

  @override
  Widget build(BuildContext context) {
    config.log.add('build');
    return new Container();
  }
}

void main() {
  testWidgets('can reparent state', (WidgetTester tester) async {
    final GlobalKey left = new GlobalKey();
    final GlobalKey right = new GlobalKey();

    final StateMarker grandchild = new StateMarker();
    await tester.pumpWidget(
      new Stack(
        children: <Widget>[
          new Container(
            child: new StateMarker(key: left)
          ),
          new Container(
            child: new StateMarker(
              key: right,
              child: grandchild
            )
          ),
        ]
      )
    );

    final StateMarkerState leftState = left.currentState;
    leftState.marker = "left";
    final StateMarkerState rightState = right.currentState;
    rightState.marker = "right";

    final StateMarkerState grandchildState = tester.state(find.byConfig(grandchild));
    expect(grandchildState, isNotNull);
    grandchildState.marker = "grandchild";

    final StateMarker newGrandchild = new StateMarker();
    await tester.pumpWidget(
      new Stack(
        children: <Widget>[
          new Container(
            child: new StateMarker(
              key: right,
              child: newGrandchild
            )
          ),
          new Container(
            child: new StateMarker(key: left)
          ),
        ]
      )
    );

    expect(left.currentState, equals(leftState));
    expect(leftState.marker, equals("left"));
    expect(right.currentState, equals(rightState));
    expect(rightState.marker, equals("right"));

    final StateMarkerState newGrandchildState = tester.state(find.byConfig(newGrandchild));
    expect(newGrandchildState, isNotNull);
    expect(newGrandchildState, equals(grandchildState));
    expect(newGrandchildState.marker, equals("grandchild"));

    await tester.pumpWidget(
      new Center(
        child: new Container(
          child: new StateMarker(
            key: left,
            child: new Container()
          )
        )
      )
    );

    expect(left.currentState, equals(leftState));
    expect(leftState.marker, equals("left"));
    expect(right.currentState, isNull);
  });

  testWidgets('can reparent state with multichild widgets', (WidgetTester tester) async {
    final GlobalKey left = new GlobalKey();
    final GlobalKey right = new GlobalKey();

    final StateMarker grandchild = new StateMarker();
    await tester.pumpWidget(
      new Stack(
        children: <Widget>[
          new StateMarker(key: left),
          new StateMarker(
            key: right,
            child: grandchild
          )
        ]
      )
    );

    final StateMarkerState leftState = left.currentState;
    leftState.marker = "left";
    final StateMarkerState rightState = right.currentState;
    rightState.marker = "right";

    final StateMarkerState grandchildState = tester.state(find.byConfig(grandchild));
    expect(grandchildState, isNotNull);
    grandchildState.marker = "grandchild";

    final StateMarker newGrandchild = new StateMarker();
    await tester.pumpWidget(
      new Stack(
        children: <Widget>[
          new StateMarker(
            key: right,
            child: newGrandchild
          ),
          new StateMarker(key: left)
        ]
      )
    );

    expect(left.currentState, equals(leftState));
    expect(leftState.marker, equals("left"));
    expect(right.currentState, equals(rightState));
    expect(rightState.marker, equals("right"));

    final StateMarkerState newGrandchildState = tester.state(find.byConfig(newGrandchild));
    expect(newGrandchildState, isNotNull);
    expect(newGrandchildState, equals(grandchildState));
    expect(newGrandchildState.marker, equals("grandchild"));

    await tester.pumpWidget(
      new Center(
        child: new Container(
          child: new StateMarker(
            key: left,
            child: new Container()
          )
        )
      )
    );

    expect(left.currentState, equals(leftState));
    expect(leftState.marker, equals("left"));
    expect(right.currentState, isNull);
  });

  testWidgets('can with scrollable list', (WidgetTester tester) async {
    final GlobalKey key = new GlobalKey();

    await tester.pumpWidget(new StateMarker(key: key));

    final StateMarkerState keyState = key.currentState;
    keyState.marker = "marked";

    await tester.pumpWidget(new ListView(
      itemExtent: 100.0,
      children: <Widget>[
        new Container(
          key: const Key('container'),
          height: 100.0,
          child: new StateMarker(key: key),
        ),
      ],
    ));

    expect(key.currentState, equals(keyState));
    expect(keyState.marker, equals("marked"));

    await tester.pumpWidget(new StateMarker(key: key));

    expect(key.currentState, equals(keyState));
    expect(keyState.marker, equals("marked"));
  });

  testWidgets('Reparent during update children', (WidgetTester tester) async {
    final GlobalKey key = new GlobalKey();

    await tester.pumpWidget(new Stack(
      children: <Widget>[
        new StateMarker(key: key),
        new Container(width: 100.0, height: 100.0),
      ]
    ));

    final StateMarkerState keyState = key.currentState;
    keyState.marker = "marked";

    await tester.pumpWidget(new Stack(
      children: <Widget>[
        new Container(width: 100.0, height: 100.0),
        new StateMarker(key: key),
      ]
    ));

    expect(key.currentState, equals(keyState));
    expect(keyState.marker, equals("marked"));

    await tester.pumpWidget(new Stack(
      children: <Widget>[
        new StateMarker(key: key),
        new Container(width: 100.0, height: 100.0),
      ]
    ));

    expect(key.currentState, equals(keyState));
    expect(keyState.marker, equals("marked"));
  });

  testWidgets('Reparent to child during update children', (WidgetTester tester) async {
    final GlobalKey key = new GlobalKey();

    await tester.pumpWidget(new Stack(
      children: <Widget>[
        new Container(width: 100.0, height: 100.0),
        new StateMarker(key: key),
        new Container(width: 100.0, height: 100.0),
      ]
    ));

    final StateMarkerState keyState = key.currentState;
    keyState.marker = "marked";

    await tester.pumpWidget(new Stack(
      children: <Widget>[
        new Container(width: 100.0, height: 100.0, child: new StateMarker(key: key)),
        new Container(width: 100.0, height: 100.0),
      ]
    ));

    expect(key.currentState, equals(keyState));
    expect(keyState.marker, equals("marked"));

    await tester.pumpWidget(new Stack(
      children: <Widget>[
        new Container(width: 100.0, height: 100.0),
        new StateMarker(key: key),
        new Container(width: 100.0, height: 100.0),
      ]
    ));

    expect(key.currentState, equals(keyState));
    expect(keyState.marker, equals("marked"));

    await tester.pumpWidget(new Stack(
      children: <Widget>[
        new Container(width: 100.0, height: 100.0),
        new Container(width: 100.0, height: 100.0, child: new StateMarker(key: key)),
      ]
    ));

    expect(key.currentState, equals(keyState));
    expect(keyState.marker, equals("marked"));

    await tester.pumpWidget(new Stack(
      children: <Widget>[
        new Container(width: 100.0, height: 100.0),
        new StateMarker(key: key),
        new Container(width: 100.0, height: 100.0),
      ]
    ));

    expect(key.currentState, equals(keyState));
    expect(keyState.marker, equals("marked"));
  });

  testWidgets('Deactivate implies build', (WidgetTester tester) async {
    final GlobalKey key = new GlobalKey();
    final List<String> log = <String>[];
    final DeactivateLogger logger = new DeactivateLogger(key: key, log: log);

    await tester.pumpWidget(
      new Container(key: new UniqueKey(), child: logger)
    );

    expect(log, equals(<String>['build']));

    await tester.pumpWidget(
      new Container(key: new UniqueKey(), child: logger)
    );

    expect(log, equals(<String>['build', 'deactivate', 'build']));
    log.clear();

    await tester.pump();
    expect(log, isEmpty);
  });

  testWidgets('Reparenting with multiple moves', (WidgetTester tester) async {
    final GlobalKey key1 = new GlobalKey();
    final GlobalKey key2 = new GlobalKey();
    final GlobalKey key3 = new GlobalKey();

    await tester.pumpWidget(
      new Row(
        children: <Widget>[
          new StateMarker(
            key: key1,
            child: new StateMarker(
              key: key2,
              child: new StateMarker(
                key: key3,
                child: new StateMarker(child: new Container(width: 100.0))
              )
            )
          )
        ]
      )
    );

    await tester.pumpWidget(
      new Row(
        children: <Widget>[
          new StateMarker(
            key: key2,
            child: new StateMarker(child: new Container(width: 100.0))
          ),
          new StateMarker(
            key: key1,
            child: new StateMarker(
              key: key3,
              child: new StateMarker(child: new Container(width: 100.0))
            )
          ),
        ]
      )
    );
  });
}