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

import 'package:path/path.dart' as path;

import '../application_package.dart';
import '../base/logging.dart';
import '../device.dart';
import '../runner/flutter_command.dart';
import 'build.dart';
import 'install.dart';
import 'stop.dart';

// We don't yet support iOS here. https://github.com/flutter/flutter/issues/1036

abstract class StartCommandBase extends FlutterCommand {
  StartCommandBase() {
    argParser.addFlag('checked',
        negatable: true,
        defaultsTo: true,
        help: 'Toggle Dart\'s checked mode.');
    argParser.addFlag('trace-startup',
        negatable: true,
        defaultsTo: false,
        help: 'Start tracing during startup.');
    argParser.addOption('target',
        defaultsTo: '',
        abbr: 't',
        help: 'Target app path or filename to start.');
    argParser.addOption('route',
        help: 'Which route to load when starting the app.');
  }

  /// Given the value of the --target option, return the path of the Dart file
  /// where the app's main function should be.
  static String findMainDartFile(String target) {
    String targetPath = path.absolute(target);
    if (FileSystemEntity.isDirectorySync(targetPath)) {
      return path.join(targetPath, 'lib', 'main.dart');
    } else {
      return targetPath;
    }
  }

  Future<int> startApp({
    bool stop: true,
    bool install: true,
    bool poke: false,
    bool clearLogs: false
  }) async {

    String mainPath = findMainDartFile(argResults['target']);
    if (!FileSystemEntity.isFileSync(mainPath)) {
      String message = 'Tried to run $mainPath, but that file does not exist.';
      if (!argResults.wasParsed('target'))
        message += '\nConsider using the -t option to specify that Dart file to start.';
      logging.severe(message);
      return 1;
    }

    if (stop) {
      logging.fine('Running stop command.');
      StopCommand stopper = new StopCommand();
      stopper.inheritFromParent(this);
      stopper.stop();
    }

    if (install) {
      logging.fine('Running install command.');
      InstallCommand installer = new InstallCommand();
      installer.inheritFromParent(this);
      installer.install();
    }

    bool startedSomething = false;

    for (Device device in devices.all) {
      ApplicationPackage package = applicationPackages.getPackageForPlatform(device.platform);
      if (package == null || !device.isConnected())
        continue;

      logging.fine('Running build command for $device.');
      BuildCommand builder = new BuildCommand();
      builder.inheritFromParent(this);
      await builder.buildInTempDir(
        mainPath: mainPath,
        onBundleAvailable: (String localBundlePath) {
          logging.fine('Starting bundle for $device.');
          final AndroidDevice androidDevice = device; // https://github.com/flutter/flutter/issues/1035
          if (androidDevice.startBundle(package, localBundlePath,
                                        poke: poke,
                                        checked: argResults['checked'],
                                        traceStartup: argResults['trace-startup'],
                                        route: argResults['route'],
                                        clearLogs: clearLogs))
            startedSomething = true;
        }
      );
    }

    if (!startedSomething) {
      if (!devices.all.any((device) => device.isConnected())) {
        logging.severe('Unable to run application - no connected devices.');
      } else {
        logging.severe('Unable to run application.');
      }
    }

    return startedSomething ? 0 : 2;
  }
}

class StartCommand extends StartCommandBase {
  final String name = 'start';
  final String description = 'Start your Flutter app on attached devices (Android only).';

  StartCommand() {
    argParser.addFlag('poke',
        negatable: false,
        help: 'Restart the connection to the server.');
    argParser.addFlag('clear-logs',
        defaultsTo: true,
        help: 'Clear log history before starting the app.');
  }

  @override
  Future<int> runInProject() async {
    logging.fine('Downloading toolchain.');

    await Future.wait([
      downloadToolchain(),
      downloadApplicationPackagesAndConnectToDevices(),
    ]);

    bool poke = argResults['poke'];
    bool clearLogs = argResults['clear-logs'];

    // Only stop and reinstall if the user did not specify a poke
    int result = await startApp(stop: !poke, install: !poke, poke: poke, clearLogs: clearLogs);

    logging.fine('Finished start command.');
    return result;
  }
}