'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _react = require('react');

var _react2 = _interopRequireDefault(_react);

var _reactAddonsPureRenderMixin = require('react-addons-pure-render-mixin');

var _reactAddonsPureRenderMixin2 = _interopRequireDefault(_reactAddonsPureRenderMixin);

var _reactCodemirror = require('react-codemirror');

var _reactCodemirror2 = _interopRequireDefault(_reactCodemirror);

var _codemirror = require('codemirror');

var _codemirror2 = _interopRequireDefault(_codemirror);

var _Rx = require('rxjs/Rx');

var _Rx2 = _interopRequireDefault(_Rx);

require('codemirror/addon/hint/show-hint');

require('codemirror/addon/hint/anyword-hint');

require('codemirror/addon/search/search');

require('codemirror/addon/search/searchcursor');

require('codemirror/addon/edit/matchbrackets');

require('codemirror/addon/edit/closebrackets');

require('codemirror/addon/dialog/dialog');

require('codemirror/mode/javascript/javascript');

require('codemirror/mode/css/css');

require('codemirror/mode/julia/julia');

require('codemirror/mode/r/r');

require('codemirror/mode/markdown/markdown');

require('./codemirror-ipython');

var _complete = require('./complete');

var _actions = require('../../../actions');

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

function goLineUpOrEmit(editor) {
  const cursor = editor.getCursor();
  if (cursor.line === 0 && cursor.ch === 0 && !editor.somethingSelected()) {
    _codemirror2.default.signal(editor, 'topBoundary');
  } else {
    editor.execCommand('goLineUp');
  }
}

// matching & closing parentheses and quotations


function goLineDownOrEmit(editor) {
  const cursor = editor.getCursor();
  const lastLineNumber = editor.lastLine();
  const lastLine = editor.getLine(lastLineNumber);
  if (cursor.line === lastLineNumber && cursor.ch === lastLine.length && !editor.somethingSelected()) {
    _codemirror2.default.signal(editor, 'bottomBoundary');
  } else {
    editor.execCommand('goLineDown');
  }
}

const excludedIntelliSenseTriggerKeys = {
  8: 'backspace',
  9: 'tab',
  13: 'enter',
  16: 'shift',
  17: 'ctrl',
  18: 'alt',
  19: 'pause',
  20: 'capslock',
  27: 'escape',
  32: 'space',
  33: 'pageup',
  34: 'pagedown',
  35: 'end',
  36: 'home',
  37: 'left',
  38: 'up',
  39: 'right',
  40: 'down',
  45: 'insert',
  46: 'delete',
  91: 'left window key',
  92: 'right window key',
  93: 'select',
  107: 'add',
  109: 'subtract',
  110: 'decimal point',
  111: 'divide',
  112: 'f1',
  113: 'f2',
  114: 'f3',
  115: 'f4',
  116: 'f5',
  117: 'f6',
  118: 'f7',
  119: 'f8',
  120: 'f9',
  121: 'f10',
  122: 'f11',
  123: 'f12',
  144: 'numlock',
  145: 'scrolllock',
  186: 'semicolon',
  187: 'equalsign',
  188: 'comma',
  189: 'dash',
  // 190: 'period',
  // 191: 'slash',
  192: 'graveaccent',
  220: 'backslash',
  222: 'quote'
};

_codemirror2.default.keyMap.basic.Up = 'goLineUpOrEmit';
_codemirror2.default.keyMap.basic.Down = 'goLineDownOrEmit';
_codemirror2.default.commands.goLineUpOrEmit = goLineUpOrEmit;
_codemirror2.default.commands.goLineDownOrEmit = goLineDownOrEmit;

class Editor extends _react2.default.Component {

  constructor(props) {
    super(props);
    this.shouldComponentUpdate = _reactAddonsPureRenderMixin2.default.shouldComponentUpdate.bind(this);
    this.state = {
      source: this.props.input
    };
    this.onChange = this.onChange.bind(this);

    this.hint = this.completions.bind(this);
    this.hint.async = true;

    // Remember the name of the theme that's applied so that when it changes we
    // can force codemirror to remeasure.
    this.theme = null;
  }

  componentDidMount() {
    // On first load, if focused, set codemirror to focus
    if (this.props.focused) {
      this.refs.codemirror.focus();
    }

    const cm = this.refs.codemirror.getCodeMirror();
    cm.on('topBoundary', this.props.focusAbove);
    cm.on('bottomBoundary', this.props.focusBelow);

    const keyupEvents = _Rx2.default.Observable.fromEvent(cm, 'keyup', (editor, ev) => ({ editor, ev }));

    keyupEvents.switchMap(i => _Rx2.default.Observable.of(i)).subscribe(({ editor, ev }) => {
      const cursor = editor.getDoc().getCursor();
      const token = editor.getTokenAt(cursor);

      if (!editor.state.completionActive && !excludedIntelliSenseTriggerKeys[(ev.keyCode || ev.which).toString()] && (token.type === 'tag' || token.type === 'variable' || token.string === ' ' || token.string === '<' || token.string === '/') && store.getState().app.executionState === 'idle') {
        editor.execCommand('autocomplete', { completeSingle: false });
      }
    });
  }

  componentWillReceiveProps(nextProps) {
    this.setState({
      source: nextProps.input
    });
  }

  componentDidUpdate(prevProps) {
    if (this.props.focused && prevProps.focused !== this.props.focused) {
      this.refs.codemirror.focus();
    } else if (!this.props.focused && prevProps.focused !== this.props.focused) {
      const cm = this.refs.codemirror.getCodeMirror();
      cm.getInputField().blur();
    }

    if (this.theme !== this.props.theme) {
      this.theme = this.props.theme;
      this.refs.codemirror.getCodeMirror().refresh();
    }
  }

  onChange(text) {
    if (this.props.onChange) {
      this.props.onChange(text);
    } else {
      this.setState({
        source: text
      });
      this.context.store.dispatch((0, _actions.updateCellSource)(this.props.id, text));
    }
  }

  completions(editor, callback) {
    if (!this.props.completion) {
      return;
    }

    const state = this.context.store.getState();
    const channels = state.app.channels;

    (0, _complete.codeComplete)(channels, editor).subscribe(callback);
  }

  render() {
    const options = {
      autoCloseBrackets: this.props.autoCloseBrackets,
      mode: this.props.language,
      lineNumbers: this.props.lineNumbers,
      lineWrapping: this.props.lineWrapping,
      matchBrackets: this.props.matchBrackets,
      theme: this.props.cmtheme,
      autofocus: false,
      hintOptions: {
        hint: this.hint,
        completeSingle: false, // In automatic autocomplete mode we don't want override
        extraKeys: {
          Right: _complete.pick
        }
      },
      extraKeys: {
        'Ctrl-Space': 'autocomplete'
      },
      indentUnit: this.props.tabSize,
      tabSize: this.props.tabSize
    };
    return _react2.default.createElement(
      'div',
      { className: 'input' },
      _react2.default.createElement(_reactCodemirror2.default, {
        value: this.state.source,
        ref: 'codemirror',
        className: 'cell_cm',
        options: options,
        onChange: this.onChange
      })
    );
  }
}
exports.default = Editor;
Editor.propTypes = {
  autoCloseBrackets: _react2.default.PropTypes.bool,
  id: _react2.default.PropTypes.string,
  input: _react2.default.PropTypes.any,
  completion: _react2.default.PropTypes.bool,
  language: _react2.default.PropTypes.string,
  tabSize: _react2.default.PropTypes.number,
  lineNumbers: _react2.default.PropTypes.bool,
  lineWrapping: _react2.default.PropTypes.bool,
  onChange: _react2.default.PropTypes.func,
  matchBrackets: _react2.default.PropTypes.bool,
  theme: _react2.default.PropTypes.string,
  cmtheme: _react2.default.PropTypes.string,
  focused: _react2.default.PropTypes.bool,
  focusAbove: _react2.default.PropTypes.func,
  focusBelow: _react2.default.PropTypes.func
};
Editor.contextTypes = {
  store: _react2.default.PropTypes.object
};
Editor.defaultProps = {
  autoCloseBrackets: true, // automatically closes opening parens and quotations
  language: 'python',
  lineNumbers: false,
  matchBrackets: true, // highlights parens that are paired
  cmtheme: 'composition',
  focused: false
};