text: /**
   * `rawlist` type prompt
   */
  
  var _ = require('lodash');
  var util = require('util');
  var chalk = require('chalk');
  var Base = require('./base');
  var Separator = require('../objects/separator');
  var observe = require('../utils/events');
  var Paginator = require('../utils/paginator');
  
  /**
   * Module exports
   */
  
  module.exports = Prompt;
  
  /**
   * Constructor
   */
  
  function Prompt() {
    Base.apply(this, arguments);
  
    if (!this.opt.choices) {
      this.throwParamError('choices');
    }
  
    this.validateChoices(this.opt.choices);
  
    // Add the default `help` (/expand) option
    this.opt.choices.push({
      key: 'h',
      name: 'Help, list all options',
      value: 'help'
    });
  
    this.opt.validate = function (choice) {
      if (choice == null) {
        return 'Please enter a valid command';
      }
  
      return choice !== 'help';
    };
  
    // Setup the default string (capitalize the default key)
    this.opt.default = this.generateChoicesString(this.opt.choices, this.opt.default);
  
    this.paginator = new Paginator();
  }
  util.inherits(Prompt, Base);
  
  /**
   * Start the Inquiry session
   * @param  {Function} cb      Callback when prompt is done
   * @return {this}
   */
  
  Prompt.prototype._run = function (cb) {
    this.done = cb;
  
    // Save user answer and update prompt to show selected option.
    var events = observe(this.rl);
    var validation = this.handleSubmitEvents(
      events.line.map(this.getCurrentValue.bind(this))
    );
    validation.success.forEach(this.onSubmit.bind(this));
    validation.error.forEach(this.onError.bind(this));
    this.keypressObs = events.keypress.takeUntil(validation.success)
      .forEach(this.onKeypress.bind(this));
  
    // Init the prompt
    this.render();
  
    return this;
  };
  
  /**
   * Render the prompt to screen
   * @return {Prompt} self
   */
  
  Prompt.prototype.render = function (error, hint) {
    var message = this.getQuestion();
    var bottomContent = '';
  
    if (this.status === 'answered') {
      message += chalk.cyan(this.answer);
    } else if (this.status === 'expanded') {
      var choicesStr = renderChoices(this.opt.choices, this.selectedKey);
      message += this.paginator.paginate(choicesStr, this.selectedKey, this.opt.pageSize);
      message += '\n  Answer: ';
    }
  
    message += this.rl.line;
  
    if (error) {
      bottomContent = chalk.red('>> ') + error;
    }
  
    if (hint) {
      bottomContent = chalk.cyan('>> ') + hint;
    }
  
    this.screen.render(message, bottomContent);
  };
  
  Prompt.prototype.getCurrentValue = function (input) {
    if (!input) {
      input = this.rawDefault;
    }
    var selected = this.opt.choices.where({key: input.toLowerCase().trim()})[0];
    if (!selected) {
      return null;
    }
  
    return selected.value;
  };
  
  /**
   * Generate the prompt choices string
   * @return {String}  Choices string
   */
  
  Prompt.prototype.getChoices = function () {
    var output = '';
  
    this.opt.choices.forEach(function (choice) {
      output += '\n  ';
  
      if (choice.type === 'separator') {
        output += ' ' + choice;
        return;
      }
  
      var choiceStr = choice.key + ') ' + choice.name;
      if (this.selectedKey === choice.key) {
        choiceStr = chalk.cyan(choiceStr);
      }
      output += choiceStr;
    }.bind(this));
  
    return output;
  };
  
  Prompt.prototype.onError = function (state) {
    if (state.value === 'help') {
      this.selectedKey = '';
      this.status = 'expanded';
      this.render();
      return;
    }
    this.render(state.isValid);
  };
  
  /**
   * When user press `enter` key
   */
  
  Prompt.prototype.onSubmit = function (state) {
    this.status = 'answered';
    var choice = this.opt.choices.where({value: state.value})[0];
    this.answer = choice.short || choice.name;
  
    // Re-render prompt
    this.render();
    this.screen.done();
    this.done(state.value);
  };
  
  /**
   * When user press a key
   */
  
  Prompt.prototype.onKeypress = function () {
    this.selectedKey = this.rl.line.toLowerCase();
    var selected = this.opt.choices.where({key: this.selectedKey})[0];
    if (this.status === 'expanded') {
      this.render();
    } else {
      this.render(null, selected ? selected.name : null);
    }
  };
  
  /**
   * Validate the choices
   * @param {Array} choices
   */
  
  Prompt.prototype.validateChoices = function (choices) {
    var formatError;
    var errors = [];
    var keymap = {};
    choices.filter(Separator.exclude).forEach(function (choice) {
      if (!choice.key || choice.key.length !== 1) {
        formatError = true;
      }
      if (keymap[choice.key]) {
        errors.push(choice.key);
      }
      keymap[choice.key] = true;
      choice.key = String(choice.key).toLowerCase();
    });
  
    if (formatError) {
      throw new Error('Format error: `key` param must be a single letter and is required.');
    }
    if (keymap.h) {
      throw new Error('Reserved key error: `key` param cannot be `h` - this value is reserved.');
    }
    if (errors.length) {
      throw new Error('Duplicate key error: `key` param must be unique. Duplicates: ' +
          _.uniq(errors).join(', '));
    }
  };
  
  /**
   * Generate a string out of the choices keys
   * @param  {Array}  choices
   * @param  {Number} defaultIndex - the choice index to capitalize
   * @return {String} The rendered choices key string
   */
  Prompt.prototype.generateChoicesString = function (choices, defaultIndex) {
    var defIndex = choices.realLength - 1;
    if (_.isNumber(defaultIndex) && this.opt.choices.getChoice(defaultIndex)) {
      defIndex = defaultIndex;
    }
    var defStr = this.opt.choices.pluck('key');
    this.rawDefault = defStr[defIndex];
    defStr[defIndex] = String(defStr[defIndex]).toUpperCase();
    return defStr.join('');
  };
  
  /**
   * Function for rendering checkbox choices
   * @param  {String} pointer Selected key
   * @return {String}         Rendered content
   */
  
  function renderChoices(choices, pointer) {
    var output = '';
  
    choices.forEach(function (choice) {
      output += '\n  ';
  
      if (choice.type === 'separator') {
        output += ' ' + choice;
        return;
      }
  
      var choiceStr = choice.key + ') ' + choice.name;
      if (pointer === choice.key) {
        choiceStr = chalk.cyan(choiceStr);
      }
      output += choiceStr;
    });
  
    return output;
  }
  