import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
import _createClass from "@babel/runtime/helpers/createClass";
import _possibleConstructorReturn from "@babel/runtime/helpers/possibleConstructorReturn";
import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf";
import _inherits from "@babel/runtime/helpers/inherits";
import _assertThisInitialized from "@babel/runtime/helpers/assertThisInitialized";
import _defineProperty from "@babel/runtime/helpers/defineProperty";

var _dec, _class, _class2, _temp;

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { JSHINT } from 'jshint';
import deepEqual from 'deep-equal';
import { reduce, includes, isEqualWith } from 'lodash';
import CodeMirror from 'codemirror';
import 'codemirror/mode/meta';
import 'codemirror/mode/htmlmixed/htmlmixed';
import 'codemirror/mode/css/css';
import 'codemirror/mode/javascript/javascript';
import 'codemirror/mode/yaml/yaml';
import 'codemirror/mode/markdown/markdown';
import 'codemirror/addon/hint/show-hint';
import 'codemirror/addon/hint/html-hint';
import 'codemirror/addon/hint/css-hint';
import 'codemirror/addon/edit/closebrackets';
import 'codemirror/addon/edit/matchbrackets';
import 'codemirror/addon/comment/comment';
import 'codemirror/addon/dialog/dialog';
import 'codemirror/addon/search/search';
import 'codemirror/addon/search/searchcursor'; // import 'codemirror/addon/scroll/simplescrollbars';

import 'codemirror/addon/fold/foldcode';
import 'codemirror/addon/fold/foldgutter';
import 'codemirror/addon/fold/brace-fold';
import 'codemirror/keymap/sublime';
import 'codemirror/lib/codemirror.css';
import 'codemirror/addon/hint/show-hint.css';
import 'codemirror/addon/dialog/dialog.css'; // import 'codemirror/addon/scroll/simplescrollbars.css';

import 'codemirror/addon/fold/foldgutter.css';
import glslMode from 'glsl-editor/glsl';
import './codemirror-hint-extension';
import CodeMirrorComponent from '../../utils/CodeMirrorComponent';
import withFiles from '../../store/withFiles';
glslMode(CodeMirror);
CodeMirror.modeInfo.push({
  name: 'glsl',
  mime: 'text/x-glsl',
  mode: 'glsl'
}); // YAML のエイリアス (.yml) (text/yaml)

CodeMirror.modeInfo.push({
  name: 'YAML',
  mimes: ['text/yaml', 'text/x-yaml'],
  mode: 'yaml',
  ext: ['yml', 'yaml'],
  alias: ['yml']
}); // segments の参照と現在の line との関係を一旦保持するマップ

var segmentsLineMap = new WeakMap();

var shallowEqual = function shallowEqual(a, b) {
  return a === b;
};

var Editor = (_dec = withFiles({}), _dec(_class = (_temp = _class2 =
/*#__PURE__*/
function (_Component) {
  _inherits(Editor, _Component);

  function Editor() {
    var _getPrototypeOf2;

    var _this;

    _classCallCheck(this, Editor);

    for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
      args[_key] = arguments[_key];
    }

    _this = _possibleConstructorReturn(this, (_getPrototypeOf2 = _getPrototypeOf(Editor)).call.apply(_getPrototypeOf2, [this].concat(args)));

    _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "state", {
      jshintrc: null,
      dropdowns: [],
      dropdownLineWidgets: [],
      links: [],
      linkLineWidgets: []
    });

    _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "handleCodemirror", function (ref) {
      if (!ref) return;
      var cm = ref.getCodeMirror();

      if (cm) {
        _this.showHint(cm);

        _this.props.codemirrorRef(cm); // ドロップダウンウィジェット


        cm.on('changes', _this.handleUpdateDropdown);
        cm.on('changes', _this.handleUpdateLink);
        cm.on('swapDoc', function () {
          _this.clearAllWidgets();

          _this.handleUpdateDropdown(cm, []);

          _this.handleUpdateLink(cm, []);
        });

        _this.handleUpdateDropdown(cm, []);

        _this.handleUpdateLink(cm, []);

        cm.on('unfold', function (cm) {
          _this.clearAllWidgets();

          _this.handleUpdateDropdown(cm, []);

          _this.handleUpdateLink(cm, []);
        });
      }
    });

    _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "handleValueClick", function (event) {
      // Put cursor into editor
      if (_this.codemirror) {
        var locate = {
          left: event.x,
          top: event.y
        };

        var pos = _this.codemirror.coordsChar(locate);

        _this.codemirror.focus();

        _this.codemirror.setCursor(pos);
      }
    });

    _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "handleUpdateDropdown", function (cm, batch) {
      var origin = batch[0] && batch[0].origin; // e.g. '+input'

      var dropdowns = reduce(cm.getValue('\n').split('\n'), function (prev, text, line) {
        // Syntax: ('▼ スキン', _kきし)
        var segments = /^(.*\(['"])(▼[^'"]*)(['"],\s*)([^)]*)\)/.exec(text);

        if (segments) {
          // line は独立して保持（異なる行でも移動しただけなら問題ない）
          segmentsLineMap.set(segments, line); // 新しいデータを格納

          prev = prev.concat([segments]);
        }

        return prev;
      }, []); // 中身が変わっていたら更新

      if (includes(['setValue', 'undo'], origin) || !deepEqual(dropdowns, _this.state.dropdowns)) {
        // 前回の LineWidget を消去
        var _iteratorNormalCompletion = true;
        var _didIteratorError = false;
        var _iteratorError = undefined;

        try {
          for (var _iterator = _this.state.dropdownLineWidgets[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
            var item = _step.value;
            item.clear(); // remove element
          } // 今回の LineWidget を追加

        } catch (err) {
          _didIteratorError = true;
          _iteratorError = err;
        } finally {
          try {
            if (!_iteratorNormalCompletion && _iterator.return != null) {
              _iterator.return();
            }
          } finally {
            if (_didIteratorError) {
              throw _iteratorError;
            }
          }
        }

        var dropdownLineWidgets = dropdowns.map(function (segments) {
          var line = segmentsLineMap.get(segments); // さっき保持した line

          return _this.renderDropdown(cm, segments, line);
        });

        _this.setState({
          dropdowns: dropdowns,
          dropdownLineWidgets: dropdownLineWidgets
        });
      }
    });

    _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "clearAllWidgets", function () {
      // 全ての LineWidget を消去
      var _iteratorNormalCompletion2 = true;
      var _didIteratorError2 = false;
      var _iteratorError2 = undefined;

      try {
        for (var _iterator2 = _this.state.dropdownLineWidgets[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
          var item = _step2.value;
          item.clear(); // remove element
        }
      } catch (err) {
        _didIteratorError2 = true;
        _iteratorError2 = err;
      } finally {
        try {
          if (!_iteratorNormalCompletion2 && _iterator2.return != null) {
            _iterator2.return();
          }
        } finally {
          if (_didIteratorError2) {
            throw _iteratorError2;
          }
        }
      }

      var _iteratorNormalCompletion3 = true;
      var _didIteratorError3 = false;
      var _iteratorError3 = undefined;

      try {
        for (var _iterator3 = _this.state.linkLineWidgets[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
          var _item = _step3.value;

          _item.clear(); // remove element

        }
      } catch (err) {
        _didIteratorError3 = true;
        _iteratorError3 = err;
      } finally {
        try {
          if (!_iteratorNormalCompletion3 && _iterator3.return != null) {
            _iterator3.return();
          }
        } finally {
          if (_didIteratorError3) {
            throw _iteratorError3;
          }
        }
      }

      _this.setState({
        dropdowns: [],
        dropdownLineWidgets: [],
        links: [],
        linkLineWidgets: []
      });
    });

    _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "renderDropdown", function (cm, segments, line) {
      var _segments = _slicedToArray(segments, 5),
          _prefix = _segments[1],
          _label = _segments[2],
          _right = _segments[3],
          _value = _segments[4];

      var label = document.createElement('span');
      label.textContent = _label;
      label.classList.add('Feeles-dropdown-label');
      var right = document.createElement('span');
      right.textContent = _right;
      right.classList.add('Feeles-dropdown-blank');
      var value = document.createElement('span');
      value.textContent = _value;
      value.classList.add('Feeles-dropdown-value');
      value.addEventListener('click', _this.handleValueClick);
      var button = document.createElement('span');
      button.appendChild(label); // "▼ スキン"

      button.appendChild(right); // "', "

      button.appendChild(value); // _kきし

      button.classList.add('Feeles-dropdown-button');
      var allOfLeft = _prefix + _label + _right; // value より左の全て

      var ch = allOfLeft.length;
      var shadow = document.createElement('span');
      shadow.appendChild(button);
      shadow.classList.add('Feeles-dropdown-shadow');
      var parent = document.createElement('div');
      parent.classList.add('Feeles-widget', 'Feeles-dropdown');
      parent.appendChild(shadow);
      var pos = {
        line: line,
        ch: _prefix.length
      };

      var _cm$charCoords = cm.charCoords(pos, 'local'),
          left = _cm$charCoords.left;

      parent.style.transform = "translate(".concat(left, "px, -1.3rem)"); // ウィジェット追加

      var widget = cm.addLineWidget(line, parent, {
        insertAt: 0
      }); // クリックイベント追加

      button.addEventListener('click', function () {
        var dropdownConfig = _this.state.dropdownConfig;
        if (!dropdownConfig) return;
        var line = cm.doc.getLineNumber(widget.line); // Open dropdown menu

        var listName = _label.substr(1).trim();

        var list = dropdownConfig[listName];

        if (list) {
          var _hint = {
            from: {
              line: line,
              ch: ch
            },
            to: {
              line: line,
              ch: ch + _value.length
            },
            list: list.map(function (item) {
              return {
                text: item.body,
                displayText: "".concat(item.body, " ").concat(item.label || '')
              };
            }) // CodeMirror.on(hint, 'pick', this.handleSaveAndRun);

          };
          cm.showHint({
            completeSingle: false,
            hint: function hint() {
              return _hint;
            }
          });
          cm.focus();
        }
      }, true);
      return widget;
    });

    _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "handleUpdateLink", function (cm, batch) {
      var origin = batch[0] && batch[0].origin; // e.g. '+input'

      var eachLines = cm.getValue('\n').split('\n');
      var links = [];
      eachLines.map(function (currentLine, line) {
        // Syntax: // [で囲むと[リンク]になります
        // Syntax: // `[これ]`はリンクにはなりません. `[これ]はなります
        var commentStart = currentLine.indexOf('//');
        var outerPrefix = currentLine.substr(0, commentStart);
        var commentLine = currentLine.substr(commentStart);

        if (commentStart > -1) {
          commentLine.split('`').map(function (text, index, array) {
            var innerPrefix = '';

            if (index % 2 === 0 || index === array.length - 1) {
              var isFirst = true;
              var _iteratorNormalCompletion4 = true;
              var _didIteratorError4 = false;
              var _iteratorError4 = undefined;

              try {
                for (var _iterator4 = text.split('[')[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
                  var linkAndMore = _step4.value;

                  if (!isFirst && includes(linkAndMore, ']')) {
                    var linkText = linkAndMore.split(']')[0];
                    var segments = {
                      prefix: outerPrefix + innerPrefix,
                      linkText: linkText // line は独立して保持（異なる行でも移動しただけなら問題ない）

                    };
                    segmentsLineMap.set(segments, line); // 新しいデータを格納

                    links.push(segments);
                  }

                  isFirst = false;
                  innerPrefix += "".concat(linkAndMore, "[");
                }
              } catch (err) {
                _didIteratorError4 = true;
                _iteratorError4 = err;
              } finally {
                try {
                  if (!_iteratorNormalCompletion4 && _iterator4.return != null) {
                    _iterator4.return();
                  }
                } finally {
                  if (_didIteratorError4) {
                    throw _iteratorError4;
                  }
                }
              }
            }

            outerPrefix += text + '`';
          });
        }
      }); // 中身が変わっていたら更新

      if (includes(['setValue', 'undo'], origin) || !deepEqual(links, _this.state.links)) {
        // 前回の LineWidget を消去
        var _iteratorNormalCompletion5 = true;
        var _didIteratorError5 = false;
        var _iteratorError5 = undefined;

        try {
          for (var _iterator5 = _this.state.linkLineWidgets[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
            var item = _step5.value;
            item.clear(); // remove element
          } // 今回の LineWidget を追加

        } catch (err) {
          _didIteratorError5 = true;
          _iteratorError5 = err;
        } finally {
          try {
            if (!_iteratorNormalCompletion5 && _iterator5.return != null) {
              _iterator5.return();
            }
          } finally {
            if (_didIteratorError5) {
              throw _iteratorError5;
            }
          }
        }

        var linkLineWidgets = links.map(function (segments) {
          var line = segmentsLineMap.get(segments); // さっき保持した line

          return _this.renderlink(cm, segments, line);
        });

        _this.setState({
          links: links,
          linkLineWidgets: linkLineWidgets
        }); // ShotCard に Scrapbox 風のリンクを表示するために
        // link object を ShotCard に渡す


        if (_this.props.handleSetLinkObjects) {
          _this.props.handleSetLinkObjects(links);
        }
      }
    });

    return _this;
  }

  _createClass(Editor, [{
    key: "shouldComponentUpdate",
    value: function shouldComponentUpdate(nextProps, nextState) {
      if (this.props.file !== nextProps.file) return true;
      if (!isEqualWith(this.state, nextState, shallowEqual)) return true;
      return false;
    }
  }, {
    key: "componentDidMount",
    value: function componentDidMount() {
      var _this$props$glob = this.props.glob('**/.jshintrc'),
          _this$props$glob2 = _slicedToArray(_this$props$glob, 1),
          jshintrc = _this$props$glob2[0];

      if (jshintrc) {
        // .jshintrc があれば JSHint でチェック
        window.JSHINT = JSHINT;

        try {
          this.setState({
            jshintrc: JSON.parse(jshintrc.text)
          });
        } catch (e) {// continue regardless of error
        }
      }

      this.setState({
        dropdownConfig: this.props.loadConfig('dropdown')
      });
    }
  }, {
    key: "componentDidUpdate",
    value: function componentDidUpdate(prevProps) {
      if (prevProps.files !== this.props.files) {
        this.setState({
          dropdownConfig: this.props.loadConfig('dropdown')
        });
      }
    }
  }, {
    key: "renderlink",
    value: function renderlink(cm, segments, line) {
      var prefix = segments.prefix,
          linkText = segments.linkText;
      var linkElement = document.createElement('a');
      linkElement.href = "https://scrapbox.io/hackforplay/".concat(encodeURIComponent(linkText));
      linkElement.target = '_blank';
      linkElement.textContent = linkText;
      linkElement.classList.add('Feeles-link');
      var pos = {
        line: line,
        ch: prefix.length
      };

      var _cm$charCoords2 = cm.charCoords(pos, 'local'),
          left = _cm$charCoords2.left;

      linkElement.style.transform = "translate(".concat(left, "px, -1.2rem)"); // ウィジェット追加

      var widget = cm.addLineWidget(line, linkElement);
      return widget;
    }
  }, {
    key: "showHint",
    value: function showHint(cm) {
      var _this2 = this;

      if (!this.props.showHint) {
        return;
      }

      cm.on('change', function (_cm, change) {
        if (change.origin === 'setValue' || change.origin === 'complete') return;
        var cursor = cm.getCursor();
        cm.scrollIntoView(cursor);
        cm.showHint({
          completeSingle: false,
          files: _this2.props.files,
          snippets: _this2.props.snippets
        });
      });
    }
  }, {
    key: "render",
    value: function render() {
      var _this$props = this.props,
          file = _this$props.file,
          lineNumbers = _this$props.lineNumbers;
      var meta = CodeMirror.findModeByMIME(file.type);
      var mode = meta && meta.mode;
      return React.createElement(CodeMirrorComponent, {
        id: file.name,
        value: file.text,
        mode: mode,
        lineNumbers: lineNumbers,
        keyMap: "sublime",
        foldGutter: true,
        foldOptions: this.props.foldOptions,
        extraKeys: this.props.extraKeys,
        ref: this.handleCodemirror
      });
    }
  }]);

  return Editor;
}(Component), _defineProperty(_class2, "propTypes", {
  file: PropTypes.object.isRequired,
  codemirrorRef: PropTypes.func.isRequired,
  snippets: PropTypes.array.isRequired,
  showHint: PropTypes.bool.isRequired,
  extraKeys: PropTypes.object.isRequired,
  foldOptions: PropTypes.object,
  lineNumbers: PropTypes.bool.isRequired,
  loadConfig: PropTypes.func.isRequired,
  handleSetLinkObjects: PropTypes.func,
  // Injected by withFiles (for JSX)
  files: PropTypes.array.isRequired,
  glob: PropTypes.func.isRequired
}), _defineProperty(_class2, "defaultProps", {
  codemirrorRef: function codemirrorRef() {},
  snippets: [],
  showHint: true,
  extraKeys: {},
  lineNumbers: true,
  foldOptions: {}
}), _temp)) || _class);
export { Editor as default };