"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.initHeadless = initHeadless;
exports.main = exports.createWindow = void 0;

var _debug = _interopRequireDefault(require("debug"));

var _exec = require("../repl/exec");

var _mimicDom = _interopRequireDefault(require("../util/mimic-dom"));

var _plugins = require("../plugins/plugins");

var _usageError = _interopRequireDefault(require("../core/usage-error"));

var _capabilities = require("../core/capabilities");

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/*
 * Copyright 2017-18 IBM Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
var __awaiter = void 0 && (void 0).__awaiter || function (thisArg, _arguments, P, generator) {
  function adopt(value) {
    return value instanceof P ? value : new P(function (resolve) {
      resolve(value);
    });
  }

  return new (P || (P = Promise))(function (resolve, reject) {
    function fulfilled(value) {
      try {
        step(generator.next(value));
      } catch (e) {
        reject(e);
      }
    }

    function rejected(value) {
      try {
        step(generator["throw"](value));
      } catch (e) {
        reject(e);
      }
    }

    function step(result) {
      result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
    }

    step((generator = generator.apply(thisArg, _arguments || [])).next());
  });
};
/* eslint-disable prefer-rest-params, prefer-spread */


const debug = (0, _debug.default)('main/headless');
debug('loading');
(0, _capabilities.setMedia)(_capabilities.Media.Headless); // by default, we'll exit with an exit code of 0 when success is
// called; this bit is necessary, as process.exit doesn't seem to
// really exit the first time

let exitCode = 0; // electron pops up a window by default, for uncaught exceptions

process.on('uncaughtException', err => __awaiter(void 0, void 0, void 0, function* () {
  debug('uncaughtException');
  debug(err);
  const colors = yield Promise.resolve().then(() => require('colors/safe'));
  console.error(colors.red(err.toString()));
  process.exit(1);
}));
process.on('exit', code => {
  debug('exiting', code);
});
let argStart = 1;

if (process.env.DEVMODE) {
  // then we're running in dev mode, where the app is started with
  // an extra argument, e.g. "electron ."
  argStart = 2;
}

const commandContextPattern = /--command-context/;
let commandContext;
const log = console.log;
const error = console.error;
debug('modules loaded');
/**
 * Certain commands may open the graphical shell; remember this, so
 * we know not to process.exit
 *
 */

let graphicalShellIsOpen = false;
/**
 * Are we in the middle of a hasty retreat?
 *
 */

const noAuth = false;
/** completion handlers for success and failure */

const success = quit => out => __awaiter(void 0, void 0, void 0, function* () {
  debug('success!');

  if (process.env.KUI_REPL_MODE) {
    debug('returning repl mode result');
    return out;
  }

  const {
    print
  } = yield Promise.resolve().then(() => require('./headless-pretty-print'));
  yield print(out, log, process.stdout);

  if (!graphicalShellIsOpen) {
    quit();
    process.exit(exitCode);
  } else {
    // log('The graphical shell should now be open. This process will stay alive till you close the window.'.red)
    // log('You may background this process, but do not kill it, unless you also want to kill the graphical shell.'.red)
    debug('graphical shell is open');
  }
});

const failure = (quit, execOptions) => err => __awaiter(void 0, void 0, void 0, function* () {
  if (execOptions && execOptions.rethrowErrors) {
    throw err;
  }

  const code = err.code || err.statusCode;
  debug('failure', code, err);
  let completion = Promise.resolve();

  if (!noAuth) {
    // we're not in a corner case of having no credentials, so print
    // the error
    if (process.env.TEST_CODE_ONLY && err.statusCode) {
      // client asked us to emit only the error code
      error(err.statusCode);
    } else {
      let msg;

      if (_usageError.default.isUsageError(err)) {
        msg = _usageError.default.getFormattedMessage(err);
      } else {
        const {
          oopsMessage
        } = yield Promise.resolve().then(() => require('../core/oops'));
        msg = oopsMessage(err);
      }

      if (typeof msg === 'string') {
        const colors = yield Promise.resolve().then(() => require('colors/safe'));

        if (process.env.TEST_INCLUDE_CODE && err.statusCode) {
          // client asked for the code and the message
          error(colors.blue(err.statusCode.toString()) + ' ' + colors.red(msg));
        } else {
          // client did not ask for the error code
          error(colors.red(msg));
        }
      } else {
        const {
          print
        } = yield Promise.resolve().then(() => require('./headless-pretty-print'));
        completion = print(msg, error, process.stderr, undefined, 'error') || Promise.resolve();
      }
    }
  } else {
    error(`No credentials found. Consider trying again with "kui help" command.`);
  }

  return completion.then(() => {
    if (!graphicalShellIsOpen) {
      // if the graphical shell isn't open, then we're done here
      exitCode = typeof code === 'number' ? code : 1;

      if (!process.env.KUI_REPL_MODE) {
        process.exit(exitCode > 128 ? exitCode - 256 : exitCode); // eslint-disable-next-line no-unreachable

        if (quit) {
          quit();
        }
      }
    }

    return false;
  });
});
/**
 * Insufficient arguments provided?
 *
 */


const insufficientArgs = argv => argv.length === 0 || argv.length === 1 && /(-h)|(--help)/.test(argv[0]);
/**
 * Opens the full UI
 *
 */


let electronCreateWindowFn;

const createWindow = (argv, subwindowPlease, subwindowPrefs) => __awaiter(void 0, void 0, void 0, function* () {
  try {
    graphicalShellIsOpen = true;
    const {
      setGraphicalShellIsOpen
    } = yield Promise.resolve().then(() => require('./headless-pretty-print'));
    setGraphicalShellIsOpen();
    const commandLine = argv;

    if (commandContext) {
      commandLine.push(commandContext);
    }

    electronCreateWindowFn(commandLine, subwindowPlease, subwindowPrefs);
  } catch (err) {
    error(err);
  }
});
/**
 *
 *
 */


exports.createWindow = createWindow;

const initCommandContext = commandContext => __awaiter(void 0, void 0, void 0, function* () {
  if (commandContext) {
    try {
      debug('setting command context', commandContext);
      (yield Promise.resolve().then(() => require('../commands/context'))).setDefaultCommandContext(JSON.parse(commandContext.substring(commandContext.indexOf('=') + 1)));
    } catch (err) {
      debug('Error initializing command context', err);
    }
  }
});

function initCommandRegistrar() {
  return __awaiter(this, void 0, void 0, function* () {
    const {
      init
    } = yield Promise.resolve().then(() => require('../commands/tree'));
    yield init();
  });
}
/**
 * Initialize headless mode
 *
 */


const main = (app, mainFunctions, rawArgv = process.argv, execOptions) => __awaiter(void 0, void 0, void 0, function* () {
  debug('main'); // get this started right away, to amortize the cost of loading the
  // prescan model; this saves us around 5ms as of 20190709

  const waitForPlugins = (0, _plugins.init)();
  const waitForCommands = yield initCommandRegistrar();
  const ourCommandContext = rawArgv.find(_ => commandContextPattern.test(_));
  debug('commandContext', ourCommandContext);

  if (!commandContext && ourCommandContext) {
    commandContext = ourCommandContext;
  }

  const argv = rawArgv.slice(argStart).filter(arg => !commandContextPattern.test(arg) && arg !== '--kui-headless' && arg !== '-v' && arg !== '--raw-output' && arg !== '--no-color' && arg !== '--no-colors' && arg !== '--color=always' && arg !== '--ui');
  debug('argv', argv);
  const {
    quit
  } = app; // installOopsHandler(() => failure(quit, execOptions)) // TODO should be repl.installOopsHandler

  electronCreateWindowFn = mainFunctions.createWindow; // set up the fake dom

  (0, _mimicDom.default)();
  /**
   * Evaluate the given command
   *
   */

  const evaluate = cmd => Promise.resolve((0, _exec.exec)(cmd, execOptions)).then(success(quit));
  /** main work starts here */


  debug('bootstrap');
  return Promise.all([waitForPlugins, waitForCommands]).then(([initWasPerformed]) => __awaiter(void 0, void 0, void 0, function* () {
    debug('plugins initialized');

    if (initWasPerformed) {
      yield initCommandContext(commandContext);
    }

    const maybeRetry = err => {
      // nothing, yet
      return failure(quit, execOptions)(err);
    }; // warning: this must occur before insufficientArgs as,
    // currently, the help command is registered as a preload
    // handler. i'm not sure why, but that's how it works right now.


    if (initWasPerformed) {
      debug('invoking plugin preloader');
      yield (0, _plugins.preload)();
      debug('invoking plugin preloader... done');
    }

    if (insufficientArgs(argv)) {
      debug('insufficient args, invoking help command');
      return evaluate('help');
    } //
    // execute a single command from the CLI
    //


    const cmd = argv.map(_ => _.match(/\s+/) ? `"${_}"` : _).join(' ').trim();

    if (cmd && cmd.length > 0) {
      debug('about to execute command');
      return evaluate(cmd).catch(maybeRetry);
    } else {
      debug('exiting, no command');

      if (!process.env.KUI_REPL_MODE) {
        process.exit(0);
      }
    }
  })).catch(failure(quit, execOptions));
});
/**
 * Bootstrap headless mode
 *
 */


exports.main = main;

function initHeadless(argv, force = false, isRunningHeadless = false, execOptions) {
  if (
  /* noHeadless !== true && */
  force || isRunningHeadless) {
    debug('initHeadless');
    const app = {
      quit: () => process.exit(0)
    }; //
    // HEADLESS MODE
    //

    try {
      return main(app, {
        createWindow: (executeThisArgvPlease, subwindowPlease, subwindowPrefs) => __awaiter(this, void 0, void 0, function* () {
          // craft a createWindow that has a first argument of true, which will indicate `noHeadless`
          // because this will be called for cases where we want a headless -> GUI transition
          const {
            createWindow: createElectronWindow
          } = yield Promise.resolve().then(() => require('./spawn-electron'));
          return createElectronWindow(true, executeThisArgvPlease, subwindowPlease, subwindowPrefs);
        })
      }, argv, execOptions);
    } catch (err) {
      // oof, something real bad happened
      console.error('Internal Error, please report this bug:');
      console.error(err);

      if (!process.env.KUI_REPL_MODE) {
        process.exit(1);
      } else {
        throw err;
      }
    }
  } else {// in case the second argument isn't undefined...

    /* if (noHeadless !== true) {
      executeThisArgvPlease = undefined
    } */
  }
}
/* initHeadless */