/**
 * Utility functions
 */

"use strict";
var _ = require("lodash");
var chalk = require("chalk");
var rx = require("rx");
var figures = require("figures");


/**
 * Module exports
 */

var utils = module.exports;


/**
 * Run a function asynchronously or synchronously
 * @param   {Function} func  Function to run
 * @param   {Function} cb    Callback function passed the `func` returned value
 * @...rest {Mixed}    rest  Arguments to pass to `func`
 * @return  {Null}
 */

utils.runAsync = function( func, cb ) {
  var async = false;
  var isValid = func.apply({
    async: function() {
      async = true;
      return _.once(cb);
    }
  }, Array.prototype.slice.call(arguments, 2) );

  if ( !async ) {
    cb(isValid);
  }
};


/**
 * Create an oversable returning the result of a function runned in sync or async mode.
 * @param  {Function} func Function to run
 * @return {rx.Observable} Observable emitting when value is known
 */

utils.createObservableFromAsync = function( func ) {
  return rx.Observable.defer(function() {
    return rx.Observable.create(function( obs ) {
      utils.runAsync( func, function( value ) {
        obs.onNext( value );
        obs.onCompleted();
      });
    });
  });
};


/**
 * Resolve a question property value if it is passed as a function.
 * This method will overwrite the property on the question object with the received value.
 * @param  {Object} question - Question object
 * @param  {String} prop     - Property to fetch name
 * @param  {Object} answers  - Answers object
 * @...rest {Mixed} rest     - Arguments to pass to `func`
 * @return {rx.Obsersable}   - Observable emitting once value is known
 */

utils.fetchAsyncQuestionProperty = function( question, prop, answers ) {
  if ( !_.isFunction(question[prop]) ) return rx.Observable.return(question);

  return utils.createObservableFromAsync(function() {
    var done = this.async();
    utils.runAsync( question[prop], function( value ) {
      question[prop] = value;
      done( question );
    }, answers );
  });
};


/**
 * Get the pointer char
 * @return {String}   the pointer char
 */

utils.getPointer = function() {
  return figures.pointer;
};


/**
 * Get the checkbox
 * @param  {Boolean} checked - add a X or not to the checkbox
 * @param  {String}  after   - Text to append after the check char
 * @return {String}          - Composited checkbox string
 */

utils.getCheckbox = function( checked, after ) {
  var checked = checked ? chalk.green( figures.radioOn ) : figures.radioOff;
  return checked + " " + ( after || "" );
};

/**
 * Helper for writing message in Prompt
 * @param {Prompt} prompt  - The Prompt object that extends tty
 * @param {String} message - The message to be output
 */
utils.writeMessage = function ( prompt, message ) {
  var msgLines = message.split(/\n/);
  prompt.height = msgLines.length;

  // Write message to screen and setPrompt to control backspace
  prompt.rl.setPrompt( _.last(msgLines) );

  if ( process.stdout.rows === 0 && process.stdout.columns === 0 ) {
    /* When it's a tty through serial port there's no terminal info and the render will malfunction,
       so we need enforce the cursor to locate to the leftmost position for rendering. */
    prompt.left( message.length + prompt.rl.line.length );
  }
  prompt.write( message );
};
