import "core-js/modules/es.array.iterator";
import "core-js/modules/es.object.to-string";
import "core-js/modules/es.string.iterator";
import "core-js/modules/es.weak-map";
import "core-js/modules/web.dom-collections.iterator";

function _classCallCheck(instance, Constructor) {
  if (!(instance instanceof Constructor)) {
    throw new TypeError("Cannot call a class as a function");
  }
}

function _defineProperties(target, props) {
  for (var i = 0; i < props.length; i++) {
    var descriptor = props[i];
    descriptor.enumerable = descriptor.enumerable || false;
    descriptor.configurable = true;
    if ("value" in descriptor) descriptor.writable = true;
    Object.defineProperty(target, descriptor.key, descriptor);
  }
}

function _createClass(Constructor, protoProps, staticProps) {
  if (protoProps) _defineProperties(Constructor.prototype, protoProps);
  if (staticProps) _defineProperties(Constructor, staticProps);
  return Constructor;
}

import {
  CellCoords
} from './3rdparty/walkontable/src';
import {
  KEY_CODES,
  isMetaKey,
  isCtrlMetaKey
} from './helpers/unicode';
import {
  stopPropagation,
  stopImmediatePropagation,
  isImmediatePropagationStopped
} from './helpers/dom/event';
import {
  getEditorInstance
} from './editors';
import EventManager from './eventManager';
import {
  EditorState
} from './editors/_baseEditor';
import {
  getParentWindow
} from './helpers/dom/element';

var EditorManager =
  /*#__PURE__*/
  function() {
    /**
     * @param {Handsontable} instance
     * @param {GridSettings} priv
     * @param {Selection} selection
     */
    function EditorManager(instance, priv, selection) {
      var _this = this;

      _classCallCheck(this, EditorManager);

      /**
       * Instance of {@link Handsontable}
       *
       * @private
       * @type {Handsontable}
       */
      this.instance = instance;
      /**
       * Reference to an instance's private GridSettings object.
       *
       * @private
       * @type {GridSettings}
       */

      this.priv = priv;
      /**
       * Instance of {@link Selection}
       *
       * @private
       * @type {Selection}
       */

      this.selection = selection;
      /**
       * Instance of {@link EventManager}.
       *
       * @private
       * @type {EventManager}
       */

      this.eventManager = new EventManager(instance);
      /**
       * Determines if EditorManager is destroyed.
       *
       * @private
       * @type {Boolean}
       */

      this.destroyed = false;
      /**
       * Determines if EditorManager is locked.
       *
       * @private
       * @type {Boolean}
       */

      this.lock = false;
      /**
       * A reference to an instance of the activeEditor.
       *
       * @private
       * @type {*}
       */

      this.activeEditor = void 0;
      /**
       * Keeps a reference to the cell's properties object.
       *
       * @type {Object}
       */

      this.cellProperties = void 0;
      this.instance.addHook('afterDocumentKeyDown', function(event) {
        return _this.onAfterDocumentKeyDown(event);
      });
      var frame = this.instance.rootWindow;

      //先判断浏览器是不是万恶的IE，没办法，写的东西也有IE使用者
      var bind_name = 'input';
      if (navigator.userAgent.indexOf("MSIE") != -1) {
        bind_name = 'propertychange';
      }
      var et = 1;

     

      while (frame) {
        this.eventManager.addEventListener(frame.document.documentElement, 'compositionend', function(event) {
          if (event && event.target && event.target.className && event.target.className.indexOf(
              'handsontableInput') > -1) {
            if (!_this.destroyed) {
              var activeTD=_this.instance.getActiveEditor();
              if(activeTD){
                var v=activeTD.getValue();
                if(activeTD.__proto__.hasOwnProperty('queryChoices') && v){
                  activeTD.queryChoices(v);
                }
              }
            }
          }
        });

        this.eventManager.addEventListener(frame.document.documentElement, 'keydown', function(event) {
          if (!_this.destroyed) {
            _this.instance.runHooks('afterDocumentKeyDown', event);
          }
        });
        frame = getParentWindow(frame);
      } // Open editor when text composition is started (IME editor)



      this.eventManager.addEventListener(this.instance.rootDocument.documentElement, 'compositionstart', function(
        event) {
        if (!_this.destroyed && _this.activeEditor && !_this.activeEditor.isOpened() && _this.instance.isListening()) {
          _this.openEditor('', event);
        }
      });
      this.instance.view.wt.update('onCellDblClick', function(event, coords, elem) {
        return _this.onCellDblClick(event, coords, elem);
      });
    }
    /**
     * Lock the editor from being prepared and closed. Locking the editor prevents its closing and
     * reinitialized after selecting the new cell. This feature is necessary for a mobile editor.
     */


    _createClass(EditorManager, [{
      key: "lockEditor",
      value: function lockEditor() {
        this.lock = true;
      }
      /**
       * Unlock the editor from being prepared and closed. This method restores the original behavior of
       * the editors where for every new selection its instances are closed.
       */

    }, {
      key: "unlockEditor",
      value: function unlockEditor() {
        this.lock = false;
      }
      /**
       * Destroy current editor, if exists.
       *
       * @param {Boolean} revertOriginal
       */

    }, {
      key: "destroyEditor",
      value: function destroyEditor(revertOriginal) {
        if (!this.lock) {
          this.closeEditor(revertOriginal);
        }
      }
      /**
       * Get active editor.
       *
       * @returns {*}
       */

    }, {
      key: "getActiveEditor",
      value: function getActiveEditor() {
        return this.activeEditor;
      }
      /**
       * Prepare text input to be displayed at given grid cell.
       */

    }, {
      key: "prepareEditor",
      value: function prepareEditor() {
        var _this2 = this;

        if (this.lock) {
          return;
        }

        if (this.activeEditor && this.activeEditor.isWaiting()) {
          this.closeEditor(false, false, function(dataSaved) {
            if (dataSaved) {
              _this2.prepareEditor();
            }
          });
          return;
        }

        var _this$instance$select = this.instance.selection.selectedRange.current().highlight,
          row = _this$instance$select.row,
          col = _this$instance$select.col;
        this.cellProperties = this.instance.getCellMeta(row, col);

        if (this.cellProperties.readOnly) {
          this.clearActiveEditor();
          return;
        }

        var editorClass = this.instance.getCellEditor(this.cellProperties);
        var td = this.instance.getCell(row, col, true);

        if (editorClass && td) {
          var prop = this.instance.colToProp(col);
          var originalValue = this.instance.getSourceDataAtCell(this.instance.runHooks('modifyRow', row), col);
          this.activeEditor = getEditorInstance(editorClass, this.instance);
          this.activeEditor.prepare(row, col, prop, td, originalValue, this.cellProperties);
        } else {
          this.clearActiveEditor();
        }
      }
      /**
       * Check is editor is opened/showed.
       *
       * @returns {Boolean}
       */

    }, {
      key: "isEditorOpened",
      value: function isEditorOpened() {
        return this.activeEditor && this.activeEditor.isOpened();
      }
      /**
       * Open editor with initial value.
       *
       * @param {null|String} newInitialValue new value from which editor will start if handled property it's not the `null`.
       * @param {Event} event
       */

    }, {
      key: "openEditor",
      value: function openEditor(newInitialValue, event) {
        if (!this.activeEditor) {
          return;
        }

        this.activeEditor.beginEditing(newInitialValue, event);
      }
      /**
       * Close editor, finish editing cell.
       *
       * @param {Boolean} restoreOriginalValue
       * @param {Boolean} [isCtrlPressed]
       * @param {Function} [callback]
       */

    }, {
      key: "closeEditor",
      value: function closeEditor(restoreOriginalValue, isCtrlPressed, callback) {
        if (this.activeEditor) {
          this.activeEditor.finishEditing(restoreOriginalValue, isCtrlPressed, callback);
        } else if (callback) {
          callback(false);
        }
      }
      /**
       * Close editor and save changes.
       *
       * @param {Boolean} isCtrlPressed
       */

    }, {
      key: "closeEditorAndSaveChanges",
      value: function closeEditorAndSaveChanges(isCtrlPressed) {
        this.closeEditor(false, isCtrlPressed);
      }
      /**
       * Close editor and restore original value.
       *
       * @param {Boolean} isCtrlPressed
       */

    }, {
      key: "closeEditorAndRestoreOriginalValue",
      value: function closeEditorAndRestoreOriginalValue(isCtrlPressed) {
        return this.closeEditor(true, isCtrlPressed);
      }
      /**
       * Clears reference to an instance of the active editor.
       *
       * @private
       */

    }, {
      key: "clearActiveEditor",
      value: function clearActiveEditor() {
        this.activeEditor = void 0;
      }
      /**
       * Controls selection's behaviour after clicking `Enter`.
       *
       * @private
       * @param {Boolean} isShiftPressed
       */

    }, {
      key: "moveSelectionAfterEnter",
      value: function moveSelectionAfterEnter(isShiftPressed) {
        var enterMoves = typeof this.priv.settings.enterMoves === 'function' ? this.priv.settings.enterMoves(
          event) : this.priv.settings.enterMoves;

        if (isShiftPressed) {
          // move selection up
          this.selection.transformStart(-enterMoves.row, -enterMoves.col);
        } else {
          // move selection down (add a new row if needed)
          this.selection.transformStart(enterMoves.row, enterMoves.col, true);
        }
      }
      /**
       * Controls selection behaviour after clicking `arrow up`.
       *
       * @private
       * @param {Boolean} isShiftPressed
       */

    }, {
      key: "moveSelectionUp",
      value: function moveSelectionUp(isShiftPressed) {
        if (isShiftPressed) {
          this.selection.transformEnd(-1, 0);
        } else {
          this.selection.transformStart(-1, 0);
        }
      }
      /**
       * Controls selection's behaviour after clicking `arrow down`.
       *
       * @private
       * @param {Boolean} isShiftPressed
       */

    }, {
      key: "moveSelectionDown",
      value: function moveSelectionDown(isShiftPressed) {
        if (isShiftPressed) {
          // expanding selection down with shift
          this.selection.transformEnd(1, 0);
        } else {
          this.selection.transformStart(1, 0);
        }
      }
      /**
       * Controls selection's behaviour after clicking `arrow right`.
       *
       * @private
       * @param {Boolean} isShiftPressed
       */

    }, {
      key: "moveSelectionRight",
      value: function moveSelectionRight(isShiftPressed) {
        if (isShiftPressed) {
          this.selection.transformEnd(0, 1);
        } else {
          this.selection.transformStart(0, 1);
        }
      }
      /**
       * Controls selection's behaviour after clicking `arrow left`.
       *
       * @private
       * @param {Boolean} isShiftPressed
       */

    }, {
      key: "moveSelectionLeft",
      value: function moveSelectionLeft(isShiftPressed) {
        if (isShiftPressed) {
          this.selection.transformEnd(0, -1);
        } else {
          this.selection.transformStart(0, -1);
        }
      }
      /**
       * onAfterDocumentKeyDown callback.
       *
       * @private
       * @param {KeyboardEvent} event
       */

    }, {
      key: "onAfterDocumentKeyDown",
      value: function onAfterDocumentKeyDown(event) {
        if (!this.instance.isListening()) {
          return;
        }

        this.instance.runHooks('beforeKeyDown', event); // keyCode 229 aka 'uninitialized' doesn't take into account with editors. This key code is produced when unfinished
        // character is entering (using IME editor). It is fired mainly on linux (ubuntu) with installed ibus-pinyin package.

        if (this.destroyed || event.keyCode === 229) {
          return;
        }

        if (isImmediatePropagationStopped(event)) {
          return;
        }

        this.priv.lastKeyCode = event.keyCode;

        if (!this.selection.isSelected()) {
          return;
        } // catch CTRL but not right ALT (which in some systems triggers ALT+CTRL)


        var isCtrlPressed = (event.ctrlKey || event.metaKey) && !event.altKey;

        if (this.activeEditor && !this.activeEditor.isWaiting()) {
          if (!isMetaKey(event.keyCode) && !isCtrlMetaKey(event.keyCode) && !isCtrlPressed && !this.isEditorOpened()) {
            this.openEditor('', event);
            return;
          }
        }

        var isShiftPressed = event.shiftKey;
        var rangeModifier = isShiftPressed ? this.selection.setRangeEnd : this.selection.setRangeStart;
        var tabMoves;

        switch (event.keyCode) {
          case KEY_CODES.A:
            if (!this.isEditorOpened() && isCtrlPressed) {
              this.instance.selectAll();
              event.preventDefault();
              stopPropagation(event);
            }

            break;

          case KEY_CODES.ARROW_UP:
            if (this.isEditorOpened() && !this.activeEditor.isWaiting()) {
              this.closeEditorAndSaveChanges(isCtrlPressed);
            }

            this.moveSelectionUp(isShiftPressed);
            event.preventDefault();
            stopPropagation(event);
            break;

          case KEY_CODES.ARROW_DOWN:
            if (this.isEditorOpened() && !this.activeEditor.isWaiting()) {
              this.closeEditorAndSaveChanges(isCtrlPressed);
            }

            this.moveSelectionDown(isShiftPressed);
            event.preventDefault();
            stopPropagation(event);
            break;

          case KEY_CODES.ARROW_RIGHT:
            if (this.isEditorOpened() && !this.activeEditor.isWaiting()) {
              this.closeEditorAndSaveChanges(isCtrlPressed);
            }

            this.moveSelectionRight(isShiftPressed);
            event.preventDefault();
            stopPropagation(event);
            break;

          case KEY_CODES.ARROW_LEFT:
            if (this.isEditorOpened() && !this.activeEditor.isWaiting()) {
              this.closeEditorAndSaveChanges(isCtrlPressed);
            }

            this.moveSelectionLeft(isShiftPressed);
            event.preventDefault();
            stopPropagation(event);
            break;

          case KEY_CODES.TAB:
            tabMoves = typeof this.priv.settings.tabMoves === 'function' ? this.priv.settings.tabMoves(event) :
              this.priv.settings.tabMoves;

            if (isShiftPressed) {
              // move selection left
              this.selection.transformStart(-tabMoves.row, -tabMoves.col);
            } else {
              // move selection right (add a new column if needed)
              this.selection.transformStart(tabMoves.row, tabMoves.col, true);
            }

            event.preventDefault();
            stopPropagation(event);
            break;

          case KEY_CODES.BACKSPACE:
          case KEY_CODES.DELETE:
            this.instance.emptySelectedCells();
            this.prepareEditor();
            event.preventDefault();
            break;

          case KEY_CODES.F2:
            /* F2 */
            if (this.activeEditor) {
              this.activeEditor.enableFullEditMode();
            }

            this.openEditor(null, event);
            event.preventDefault(); // prevent Opera from opening 'Go to Page dialog'

            break;

          case KEY_CODES.ENTER:
            /* return/enter */
            if (this.isEditorOpened()) {
              if (this.activeEditor && this.activeEditor.state !== EditorState.WAITING) {
                this.closeEditorAndSaveChanges(isCtrlPressed);
              }

              this.moveSelectionAfterEnter(isShiftPressed);
            } else if (this.instance.getSettings().enterBeginsEditing) {
              if (this.cellProperties.readOnly) {
                this.moveSelectionAfterEnter();
              } else if (this.activeEditor) {
                this.activeEditor.enableFullEditMode();
                this.openEditor(null, event);
              }
            } else {
              this.moveSelectionAfterEnter(isShiftPressed);
            }

            event.preventDefault(); // don't add newline to field

            stopImmediatePropagation(event); // required by HandsontableEditor

            break;

          case KEY_CODES.ESCAPE:
            if (this.isEditorOpened()) {
              this.closeEditorAndRestoreOriginalValue(isCtrlPressed);
              this.activeEditor.focus();
            }

            event.preventDefault();
            break;

          case KEY_CODES.HOME:
            if (event.ctrlKey || event.metaKey) {
              rangeModifier.call(this.selection, new CellCoords(0, this.selection.selectedRange.current().from
                .col));
            } else {
              rangeModifier.call(this.selection, new CellCoords(this.selection.selectedRange.current().from.row,
                0));
            }

            event.preventDefault(); // don't scroll the window

            stopPropagation(event);
            break;

          case KEY_CODES.END:
            if (event.ctrlKey || event.metaKey) {
              rangeModifier.call(this.selection, new CellCoords(this.instance.countRows() - 1, this.selection
                .selectedRange
                .current().from.col));
            } else {
              rangeModifier.call(this.selection, new CellCoords(this.selection.selectedRange.current().from.row,
                this.instance.countCols() - 1));
            }

            event.preventDefault(); // don't scroll the window

            stopPropagation(event);
            break;

          case KEY_CODES.PAGE_UP:
            this.selection.transformStart(-this.instance.countVisibleRows(), 0);
            event.preventDefault(); // don't page up the window

            stopPropagation(event);
            break;

          case KEY_CODES.PAGE_DOWN:
            this.selection.transformStart(this.instance.countVisibleRows(), 0);
            event.preventDefault(); // don't page down the window

            stopPropagation(event);
            break;

          default:
            break;
        }
      }
      /**
       * onCellDblClick callback.
       *
       * @private
       * @param {MouseEvent} event
       * @param {Object} coords
       * @param {HTMLTableCellElement|HTMLTableHeaderCellElement} elem
       */

    }, {
      key: "onCellDblClick",
      value: function onCellDblClick(event, coords, elem) {
        // may be TD or TH
        if (elem.nodeName === 'TD') {
          if (this.activeEditor) {
            this.activeEditor.enableFullEditMode();
          }

          this.openEditor(null, event);
        }
      }
      /**
       * Destroy the instance.
       */

    }, {
      key: "destroy",
      value: function destroy() {
        this.destroyed = true;
        this.eventManager.destroy();
      }
    }]);

    return EditorManager;
  }();

var instances = new WeakMap();
/**
 * @param {Handsontable} hotInstance
 * @param {GridSettings} hotSettings
 * @param {Selection} selection
 * @param {DataMap} datamap
 */

EditorManager.getInstance = function(hotInstance, hotSettings, selection, datamap) {
  var editorManager = instances.get(hotInstance);

  if (!editorManager) {
    editorManager = new EditorManager(hotInstance, hotSettings, selection, datamap);
    instances.set(hotInstance, editorManager);
  }

  return editorManager;
};

export default EditorManager;
