/**
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */
'use strict';

const chalk = require('chalk');
const child_process = require('child_process');
const fs = require('fs');
const path = require('path');
const parseCommandLine = require('../util/parseCommandLine');
const isPackagerRunning = require('../util/isPackagerRunning');
const Promise = require('promise');

/**
 * Starts the app on a connected Android emulator or device.
 */
function runAndroid(argv, config) {
  return new Promise((resolve, reject) => {
    _runAndroid(argv, config, resolve, reject);
  });
}

function _runAndroid(argv, config, resolve, reject) {
  const args = parseCommandLine([{
    command: 'install-debug',
    type: 'string',
    required: false,
  }, {
    command: 'root',
    type: 'string',
    description: 'Override the root directory for the android build (which contains the android directory)',
  }, {
    command: 'flavor',
    type: 'string',
    required: false,
  }], argv);

  args.root = args.root || '';

  if (!checkAndroid(args)) {
    console.log(chalk.red('Android project not found. Maybe run react-native android first?'));
    return;
  }

  resolve(isPackagerRunning().then(result => {
    if (result === 'running') {
      console.log(chalk.bold('JS server already running.'));
    } else if (result === 'unrecognized') {
      console.warn(chalk.yellow('JS server not recognized, continuing with build...'));
    } else {
      // result == 'not_running'
      console.log(chalk.bold('Starting JS server...'));
      startServerInNewWindow();
    }
    buildAndRun(args, reject);
  }));
}

// Verifies this is an Android project
function checkAndroid(args) {
  return fs.existsSync(path.join(args.root, 'android/gradlew'));
}

// Builds the app and runs it on a connected emulator / device.
function buildAndRun(args, reject) {
  process.chdir(path.join(args.root, 'android'));
  try {
    const cmd = process.platform.startsWith('win')
      ? 'gradlew.bat'
      : './gradlew';

    const gradleArgs = [];
    if (args['flavor']) {
        gradleArgs.push('install' +
          args['flavor'][0].toUpperCase() + args['flavor'].slice(1)
        );
    } else {
        gradleArgs.push('installDebug');
    }

    if (args['install-debug']) {
      gradleArgs.push(args['install-debug']);
    }

    console.log(chalk.bold(
      'Building and installing the app on the device (cd android && ' + cmd +
      ' ' + gradleArgs.join(' ') + ')...'
    ));

    child_process.execFileSync(cmd, gradleArgs, {
      stdio: [process.stdin, process.stdout, process.stderr],
    });
  } catch (e) {
    console.log(chalk.red(
      'Could not install the app on the device, read the error above for details.\n' +
      'Make sure you have an Android emulator running or a device connected and have\n' +
      'set up your Android development environment:\n' +
      'https://facebook.github.io/react-native/docs/android-setup.html'
    ));
    // stderr is automatically piped from the gradle process, so the user
    // should see the error already, there is no need to do
    // `console.log(e.stderr)`
    reject();
    return;
  }

  try {
    const packageName = fs.readFileSync(
      'app/src/main/AndroidManifest.xml',
      'utf8'
    ).match(/package="(.+?)"/)[1];

    const adbPath = process.env.ANDROID_HOME
      ? process.env.ANDROID_HOME + '/platform-tools/adb'
      : 'adb';

    const adbArgs = [
      'shell', 'am', 'start', '-n', packageName + '/.MainActivity'
    ];

    console.log(chalk.bold(
      'Starting the app (' + adbPath + ' ' + adbArgs.join(' ') + ')...'
    ));

    child_process.spawnSync(adbPath, adbArgs, {stdio: 'inherit'});
  } catch (e) {
    console.log(chalk.red(
      'adb invocation failed. Do you have adb in your PATH?'
    ));
    // stderr is automatically piped from the gradle process, so the user
    // should see the error already, there is no need to do
    // `console.log(e.stderr)`
    reject();
    return;
  }
}

function startServerInNewWindow() {
  var yargV = require('yargs').argv;

  const launchPackagerScript = path.resolve(
    __dirname, '..', '..', 'packager', 'launchPackager.command'
  );

  if (process.platform === 'darwin') {
    if (yargV.open) {
      return child_process.spawnSync('open', ['-a', yargV.open, launchPackagerScript]);
    }
    return child_process.spawnSync('open', [launchPackagerScript]);

  } else if (process.platform === 'linux') {
    if (yargV.open){
      return child_process.spawn(yargV.open,['-e', 'sh', launchPackagerScript], {detached: true});
    }
    return child_process.spawn('xterm',['-e', 'sh', launchPackagerScript],{detached: true});

  } else if (/^win/.test(process.platform)) {
    console.log(chalk.yellow('Starting the packager in a new window ' +
      'is not supported on Windows yet.\nPlease start it manually using ' +
      '\'react-native start\'.'));
    console.log('We believe the best Windows ' +
      'support will come from a community of people\nusing React Native on ' +
      'Windows on a daily basis.\n' +
      'Would you be up for sending a pull request?');
  } else {
    console.log(chalk.red('Cannot start the packager. Unknown platform ' +
      process.platform));
  }
}

module.exports = runAndroid;
