// Copyright 2014 The Flutter 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 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart';
import 'package:flutter_driver/driver_extension.dart';

void main() {
  enableFlutterDriverExtension();
  debugPrint('Application starting...');
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  State createState() => MyAppState();
}

const MethodChannel channel = MethodChannel('texture');

enum FrameState { initial, slow, afterSlow, fast, afterFast }

class MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
  int _widgetBuilds = 0;
  FrameState _state;
  String _summary = '';
  IconData _icon;
  double _flutterFrameRate;

  Future<void> _summarizeStats() async {
    final double framesProduced = await channel.invokeMethod('getProducedFrameRate');
    final double framesConsumed = await channel.invokeMethod('getConsumedFrameRate');
    _summary = '''
Produced: ${framesProduced.toStringAsFixed(1)}fps
Consumed: ${framesConsumed.toStringAsFixed(1)}fps
Widget builds: $_widgetBuilds''';
  }

  Future<void> _nextState() async {
    switch (_state) {
      case FrameState.initial:
        debugPrint('Starting .5x speed test...');
        _widgetBuilds = 0;
        _summary = 'Producing texture frames at .5x speed...';
        _state = FrameState.slow;
        _icon = Icons.stop;
        channel.invokeMethod<void>('start', _flutterFrameRate ~/ 2);
        break;
      case FrameState.slow:
        debugPrint('Stopping .5x speed test...');
        await channel.invokeMethod<void>('stop');
        await _summarizeStats();
        _icon = Icons.fast_forward;
        _state = FrameState.afterSlow;
        break;
      case FrameState.afterSlow:
        debugPrint('Starting 2x speed test...');
        _widgetBuilds = 0;
        _summary = 'Producing texture frames at 2x speed...';
        _state = FrameState.fast;
        _icon = Icons.stop;
        channel.invokeMethod<void>('start', (_flutterFrameRate * 2).toInt());
        break;
      case FrameState.fast:
        debugPrint('Stopping 2x speed test...');
        await channel.invokeMethod<void>('stop');
        await _summarizeStats();
        _state = FrameState.afterFast;
        _icon = Icons.replay;
        break;
      case FrameState.afterFast:
        debugPrint('Test complete.');
        _summary = 'Press play to start again';
        _state = FrameState.initial;
        _icon = Icons.play_arrow;
        break;
    }
    setState(() {});
  }

  @override
  void initState() {
    super.initState();
    _calibrate();
  }

  static const int calibrationTickCount = 600;

  /// Measures Flutter's frame rate.
  Future<void> _calibrate() async {
    debugPrint('Awaiting calm (3 second pause)...');
    await Future<void>.delayed(const Duration(milliseconds: 3000));
    debugPrint('Calibrating...');
    DateTime startTime;
    int tickCount = 0;
    Ticker ticker;
    ticker = createTicker((Duration time) {
      tickCount += 1;
      if (tickCount == calibrationTickCount) { // about 10 seconds
        final Duration elapsed = DateTime.now().difference(startTime);
        ticker.stop();
        ticker.dispose();
        setState(() {
          _flutterFrameRate = tickCount * 1000 / elapsed.inMilliseconds;
          debugPrint('Calibrated: frame rate ${_flutterFrameRate.toStringAsFixed(1)}fps.');
          _summary = '''
Flutter frame rate is ${_flutterFrameRate.toStringAsFixed(1)}fps.
Press play to produce texture frames.''';
          _icon = Icons.play_arrow;
          _state = FrameState.initial;
        });
      } else {
        if ((tickCount % (calibrationTickCount ~/ 20)) == 0)
          debugPrint('Calibrating... ${(100.0 * tickCount / calibrationTickCount).floor()}%');
      }
    });
    ticker.start();
    startTime = DateTime.now();
    setState(() {
      _summary = 'Calibrating...';
      _icon = null;
    });
  }

  @override
  Widget build(BuildContext context) {
    _widgetBuilds += 1;
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Container(
                width: 300.0,
                height: 200.0,
                child: const Texture(textureId: 0),
              ),
              Container(
                width: 300.0,
                height: 60.0,
                color: Colors.grey,
                child: Center(
                  child: Text(
                    _summary,
                    key: const ValueKey<String>('summary'),
                  ),
                ),
              ),
            ],
          ),
        ),
        floatingActionButton: _icon == null ? null : FloatingActionButton(
          key: const ValueKey<String>('fab'),
          child: Icon(_icon),
          onPressed: _nextState,
        ),
      ),
    );
  }
}
