// 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:io';

import 'package:flutter_devicelab/framework/devices.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart' as utils;
import 'package:flutter_devicelab/tasks/perf_tests.dart' show ListStatistics;
import 'package:path/path.dart' as path;

const String _bundleName = 'dev.flutter.multipleflutters';
const String _activityName = 'MainActivity';
const int _numberOfIterations = 10;

Future<void> _withApkInstall(
    String apkPath, String bundleName, Function(AndroidDevice) body) async {
  final DeviceDiscovery devices = DeviceDiscovery();
  final AndroidDevice device = await devices.workingDevice as AndroidDevice;
  await device.unlock();
  await device.adb(<String>['install', '-r', apkPath]);
  try {
    await body(device);
  } finally {
    await device.adb(<String>['uninstall', bundleName]);
  }
}

/// Since we don't check the gradle wrapper in with the android host project we
/// yank the gradle wrapper from the module (which is added by the Flutter tool).
void _copyGradleFromModule(String source, String destination) {
  print('copying gradle from module $source to $destination');
  final String wrapperPath = path.join(source, '.android', 'gradlew');
  final String windowsWrapperPath = path.join(source, '.android', 'gradlew.bat');
  final String wrapperDestinationPath = path.join(destination, 'gradlew');
  final String windowsWrapperDestinationPath = path.join(destination, 'gradlew.bat');
  File(wrapperPath).copySync(wrapperDestinationPath);
  File(windowsWrapperPath).copySync(windowsWrapperDestinationPath);
  final Directory gradleDestinationDirectory = Directory(path.join(destination, 'gradle', 'wrapper'));
  if (!gradleDestinationDirectory.existsSync()) {
    gradleDestinationDirectory.createSync(recursive: true);
  }
  final String gradleDestinationPath = path.join(gradleDestinationDirectory.path, 'gradle-wrapper.jar');
  final String gradlePath = path.join(source, '.android', 'gradle', 'wrapper', 'gradle-wrapper.jar');
  File(gradlePath).copySync(gradleDestinationPath);
}

Future<TaskResult> _doTest() async {
  try {
    final String flutterDirectory = utils.flutterDirectory.path;
    final String multipleFluttersPath =
        path.join(flutterDirectory, 'dev', 'benchmarks', 'multiple_flutters');
    final String modulePath = path.join(multipleFluttersPath, 'module');
    final String androidPath = path.join(multipleFluttersPath, 'android');

    final String gradlew = Platform.isWindows ? 'gradlew.bat' : 'gradlew';
    final String gradlewExecutable =
        Platform.isWindows ? '.\\$gradlew' : './$gradlew';
    final String flutterPath = path.join(flutterDirectory, 'bin', 'flutter');
    await utils.eval(flutterPath, <String>['precache', '--android'],
        workingDirectory: modulePath);
    await utils.eval(flutterPath, <String>['pub', 'get'],
        workingDirectory: modulePath);
    _copyGradleFromModule(modulePath, androidPath);

    await utils.eval(gradlewExecutable, <String>['assembleRelease'],
        workingDirectory: androidPath);

    final String apkPath = path.join(multipleFluttersPath, 'android', 'app',
        'build', 'outputs', 'apk', 'release', 'app-release.apk');

    TaskResult? result;
    await _withApkInstall(apkPath, _bundleName, (AndroidDevice device) async {
      final List<int> totalMemorySamples = <int>[];
      for (int i = 0; i < _numberOfIterations; ++i) {
        await device.adb(<String>[
          'shell',
          'am',
          'start',
          '-n',
          '$_bundleName/$_bundleName.$_activityName'
        ]);
        await Future<void>.delayed(const Duration(seconds: 10));
        final Map<String, dynamic> memoryStats =
            await device.getMemoryStats(_bundleName);
        final int totalMemory = memoryStats['total_kb'] as int;
        totalMemorySamples.add(totalMemory);
        await device.stop(_bundleName);
      }
      final ListStatistics totalMemoryStatistics =
          ListStatistics(totalMemorySamples);

      final Map<String, dynamic> results = <String, dynamic>{
        ...totalMemoryStatistics.asMap('totalMemory')
      };
      result = TaskResult.success(results,
          benchmarkScoreKeys: results.keys.toList());
    });

    return result ?? TaskResult.failure('no results found');
  } catch (ex) {
    return TaskResult.failure(ex.toString());
  }
}

Future<void> main() async {
  await task(_doTest);
}