text: /* jslint node: true */
  'use strict';
  
  //  ENiGMA½
  const TextView              = require('./text_view.js').TextView;
  const View                  = require('./view.js').View;
  const EditTextView          = require('./edit_text_view.js').EditTextView;
  const ButtonView            = require('./button_view.js').ButtonView;
  const VerticalMenuView      = require('./vertical_menu_view.js').VerticalMenuView;
  const HorizontalMenuView    = require('./horizontal_menu_view.js').HorizontalMenuView;
  const SpinnerMenuView       = require('./spinner_menu_view.js').SpinnerMenuView;
  const ToggleMenuView        = require('./toggle_menu_view.js').ToggleMenuView;
  const MaskEditTextView      = require('./mask_edit_text_view.js').MaskEditTextView;
  const KeyEntryView          = require('./key_entry_view.js');
  const MultiLineEditTextView = require('./multi_line_edit_text_view.js').MultiLineEditTextView;
  const getPredefinedMCIValue = require('./predefined_mci.js').getPredefinedMCIValue;
  const ansi                  = require('./ansi_term.js');
  
  //  deps
  const assert                    = require('assert');
  const _                     = require('lodash');
  
  exports.MCIViewFactory      = MCIViewFactory;
  
  function MCIViewFactory(client) {
      this.client = client;
  }
  
  MCIViewFactory.UserViewCodes = [
      'TL', 'ET', 'ME', 'MT', 'PL', 'BT', 'VM', 'HM', 'SM', 'TM', 'KE',
  
      //
      //  XY is a special MCI code that allows finding positions
      //  and counts for key lookup, but does not explicitly
      //  represent a visible View on it's own
      //
      'XY',
  ];
  
  MCIViewFactory.MovementCodes = [
      'CF', 'CB', 'CU', 'CD',
  ];
  
  MCIViewFactory.prototype.createFromMCI = function(mci) {
      assert(mci.code);
      assert(mci.id > 0);
      assert(mci.position);
  
      var view;
      var options = {
          client          : this.client,
          id              : mci.id,
          ansiSGR         : mci.SGR,
          ansiFocusSGR    : mci.focusSGR,
          position        : { row : mci.position[0], col : mci.position[1] },
      };
  
      //  :TODO: These should use setPropertyValue()!
      function setOption(pos, name) {
          if(mci.args.length > pos && mci.args[pos].length > 0) {
              options[name] = mci.args[pos];
          }
      }
  
      function setWidth(pos) {
          if(mci.args.length > pos && mci.args[pos].length > 0) {
              if(!_.isObject(options.dimens)) {
                  options.dimens = {};
              }
              options.dimens.width = parseInt(mci.args[pos], 10);
          }
      }
  
      function setFocusOption(pos, name) {
          if(mci.focusArgs && mci.focusArgs.length > pos && mci.focusArgs[pos].length > 0) {
              options[name] = mci.focusArgs[pos];
          }
      }
  
      //
      //  Note: Keep this in sync with UserViewCodes above!
      //
      switch(mci.code) {
          //  Text Label (Text View)
          case 'TL' :
              setOption(0,    'textStyle');
              setOption(1,    'justify');
              setWidth(2);
  
              view = new TextView(options);
              break;
  
              //  Edit Text
          case 'ET' :
              setWidth(0);
  
              setOption(1,        'textStyle');
              setFocusOption(0,   'focusTextStyle');
  
              view = new EditTextView(options);
              break;
  
              //  Masked Edit Text
          case 'ME' :
              setOption(0,        'textStyle');
              setFocusOption(0,   'focusTextStyle');
  
              view = new MaskEditTextView(options);
              break;
  
              //  Multi Line Edit Text
          case 'MT' :
              //  :TODO: apply params
              view = new MultiLineEditTextView(options);
              break;
  
              //  Pre-defined Label (Text View)
              //  :TODO: Currently no real point of PL -- @method replaces this pretty much... probably remove
          case 'PL' :
              if(mci.args.length > 0) {
                  options.text = getPredefinedMCIValue(this.client, mci.args[0]);
                  if(options.text) {
                      setOption(1, 'textStyle');
                      setOption(2, 'justify');
                      setWidth(3);
  
                      view = new TextView(options);
                  }
              }
              break;
  
              //  Button
          case 'BT' :
              if(mci.args.length > 0) {
                  options.dimens = { width : parseInt(mci.args[0], 10) };
              }
  
              setOption(1, 'textStyle');
              setOption(2, 'justify');
  
              setFocusOption(0, 'focusTextStyle');
  
              view = new ButtonView(options);
              break;
  
              //  Vertial Menu
          case 'VM' :
              setOption(0,        'itemSpacing');
              setOption(1,        'justify');
              setOption(2,        'textStyle');
  
              setFocusOption(0,   'focusTextStyle');
  
              view = new VerticalMenuView(options);
              break;
  
              //  Horizontal Menu
          case 'HM' :
              setOption(0,        'itemSpacing');
              setOption(1,        'textStyle');
  
              setFocusOption(0,   'focusTextStyle');
  
              view = new HorizontalMenuView(options);
              break;
  
          case 'SM' :
              setOption(0,        'textStyle');
              setOption(1,        'justify');
  
              setFocusOption(0,   'focusTextStyle');
  
              view = new SpinnerMenuView(options);
              break;
  
          case 'TM' :
              if(mci.args.length > 0) {
                  var styleSG1 = { fg : parseInt(mci.args[0], 10) };
                  if(mci.args.length > 1) {
                      styleSG1.bg = parseInt(mci.args[1], 10);
                  }
                  options.styleSG1 = ansi.getSGRFromGraphicRendition(styleSG1, true);
              }
  
              setFocusOption(0,   'focusTextStyle');
  
              view = new ToggleMenuView(options);
              break;
  
          case 'KE' :
              view = new KeyEntryView(options);
              break;
  
          case 'XY' :
              view = new View(options);
              break;
  
          default :
              if(!MCIViewFactory.MovementCodes.includes(mci.code)) {
                  options.text = getPredefinedMCIValue(this.client, mci.code);
                  if(_.isString(options.text)) {
                      setWidth(0);
  
                      setOption(1,    'textStyle');
                      setOption(2,    'justify');
  
                      view = new TextView(options);
                  }
              }
              break;
      }
  
      if(view) {
          view.mciCode = mci.code;
      }
  
      return view;
  };
  