import 'package:sky/src/fn3.dart';
import 'package:test/test.dart';

import '../fn3/widget_tester.dart';
import '../fn3/test_widgets.dart';

class ProbeWidget extends StatefulComponent {
  ProbeWidgetState createState() => new ProbeWidgetState();
}

class ProbeWidgetState extends State<ProbeWidget> {
  static int buildCount = 0;

  void initState(BuildContext context) {
    super.initState(context);
    setState(() {});
  }

  void didUpdateConfig(ProbeWidget oldConfig) {
    setState(() {});
  }

  Widget build(BuildContext context) {
    setState(() {});
    buildCount++;
    return new Container();
  }
}

class BadWidget extends StatelessComponent {
  BadWidget(this.parentState);

  final State parentState;

  Widget build(BuildContext context) {
    parentState.setState(() {});
    return new Container();
  }
}

class BadWidgetParent extends StatefulComponent {
  BadWidgetParentState createState() => new BadWidgetParentState();
}

class BadWidgetParentState extends State<BadWidgetParent> {
  Widget build(BuildContext context) {
    return new BadWidget(this);
  }
}

class BadDisposeWidget extends StatefulComponent {
  BadDisposeWidgetState createState() => new BadDisposeWidgetState();
}

class BadDisposeWidgetState extends State<BadDisposeWidget> {
  Widget build(BuildContext context) {
    return new Container();
  }

  void dispose() {
    setState(() {});
    super.dispose();
  }
}

void main() {
  dynamic cachedException;

  setUp(() {
    assert(cachedException == null);
    debugWidgetsExceptionHandler = (String context, dynamic exception, StackTrace stack) {
      cachedException = exception;
    };
  });

  tearDown(() {
    assert(cachedException == null);
    cachedException = null;
    debugWidgetsExceptionHandler = null;
  });

  test('Legal times for setState', () {
    WidgetTester tester = new WidgetTester();

    GlobalKey flipKey = new GlobalKey();
    expect(ProbeWidgetState.buildCount, equals(0));
    tester.pumpFrame(new ProbeWidget());
    expect(ProbeWidgetState.buildCount, equals(1));
    tester.pumpFrame(new ProbeWidget());
    expect(ProbeWidgetState.buildCount, equals(2));
    tester.pumpFrame(new FlipComponent(
      key: flipKey,
      left: new Container(),
      right: new ProbeWidget()
    ));
    expect(ProbeWidgetState.buildCount, equals(2));
    (flipKey.currentState as FlipComponentState).flip();
    tester.pumpFrameWithoutChange();
    expect(ProbeWidgetState.buildCount, equals(3));
    (flipKey.currentState as FlipComponentState).flip();
    tester.pumpFrameWithoutChange();
    expect(ProbeWidgetState.buildCount, equals(3));
    tester.pumpFrame(new Container());
    expect(ProbeWidgetState.buildCount, equals(3));
  });

  test('Setting parent state during build is forbidden', () {
    WidgetTester tester = new WidgetTester();

    expect(cachedException, isNull);
    tester.pumpFrame(new BadWidgetParent());
    expect(cachedException, isNotNull);
    cachedException = null;
    tester.pumpFrame(new Container());
    expect(cachedException, isNull);
  });

  test('Setting state during dispose is forbidden', () {
    WidgetTester tester = new WidgetTester();

    tester.pumpFrame(new BadDisposeWidget());
    expect(() {
      tester.pumpFrame(new Container());
    }, throws);
  });
}