import _Object$keys from 'babel-runtime/core-js/object/keys';
import _getIterator from 'babel-runtime/core-js/get-iterator';
import _extends from 'babel-runtime/helpers/extends';
import _Object$getPrototypeOf from 'babel-runtime/core-js/object/get-prototype-of';
import _classCallCheck from 'babel-runtime/helpers/classCallCheck';
import _createClass from 'babel-runtime/helpers/createClass';
import _possibleConstructorReturn from 'babel-runtime/helpers/possibleConstructorReturn';
import _inherits from 'babel-runtime/helpers/inherits';
import React from 'react';
import PropTypes from 'prop-types';
import ClassNames from 'classnames';
import { omit, getDisplayName } from './utils';

import './react-dnd-ax.css';

var DragNDropItem = function DragNDropItem(WrappedComponent) {
  var Wrapper = function (_React$Component) {
    _inherits(Wrapper, _React$Component);

    function Wrapper() {
      var _ref;

      var _temp, _this, _ret;

      _classCallCheck(this, Wrapper);

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

      return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_ref = Wrapper.__proto__ || _Object$getPrototypeOf(Wrapper)).call.apply(_ref, [this].concat(args))), _this), _this.havePropsChanged = function (nextProps) {
        var propagatableProps = _extends({}, omit(nextProps, 'state', 'key', 'actions', 'onReorderItem', 'index'));

        var _iteratorNormalCompletion = true;
        var _didIteratorError = false;
        var _iteratorError = undefined;

        try {
          for (var _iterator = _getIterator(_Object$keys(propagatableProps)), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
            var key = _step.value;

            if (propagatableProps[key] !== _this.props[key]) {
              return true;
            }
          }
        } catch (err) {
          _didIteratorError = true;
          _iteratorError = err;
        } finally {
          try {
            if (!_iteratorNormalCompletion && _iterator.return) {
              _iterator.return();
            }
          } finally {
            if (_didIteratorError) {
              throw _iteratorError;
            }
          }
        }

        return false;
      }, _this.onSetImageDragStart = function (e) {
        var _this$props = _this.props,
            index = _this$props.index,
            actions = _this$props.actions;
        // hide the default drag preview image
        // IE and Edge do not have this method

        e.dataTransfer.setDragImage ? // eslint-disable-line no-unused-expressions
        e.dataTransfer.setDragImage(document.getElementsByClassName('dnd-drag-placeholder')[0], 0, 0) : '';
        e.dataTransfer.setData('text', ''); // make dnd work in FF, IE and Edge
        actions.onDragStart(e, index);
      }, _this.onTouchStart = function (e) {
        e.preventDefault();
        var _this$props2 = _this.props,
            index = _this$props2.index,
            actions = _this$props2.actions;


        actions.onDragStart(e, index);
      }, _this.onClick = function (e) {
        e.stopPropagation();
        var _this$props3 = _this.props,
            index = _this$props3.index,
            actions = _this$props3.actions,
            preview = _this$props3.preview;


        actions.onClickDrag(e, index, preview);
      }, _this.onDropNextIndex = function (e) {
        var _this$props4 = _this.props,
            index = _this$props4.index,
            actions = _this$props4.actions;


        actions.onDrop(e, index + 1);
      }, _this.onDragOverNextIndex = function (e) {
        var _this$props5 = _this.props,
            index = _this$props5.index,
            actions = _this$props5.actions;


        actions.onDragOver(e, index + 1);
      }, _this.onDragLeave = function (e) {
        var _this$props6 = _this.props,
            index = _this$props6.index,
            actions = _this$props6.actions;


        actions.onDragLeave(e, index);
      }, _this.onDragOver = function (e) {
        var _this$props7 = _this.props,
            index = _this$props7.index,
            actions = _this$props7.actions;


        actions.onDragOver(e, index);
      }, _this.onDrop = function (e) {
        var _this$props8 = _this.props,
            index = _this$props8.index,
            actions = _this$props8.actions;


        actions.onDrop(e, index);
      }, _temp), _possibleConstructorReturn(_this, _ret);
    }

    _createClass(Wrapper, [{
      key: 'componentDidMount',
      value: function componentDidMount() {
        var _this2 = this;

        var actions = this.props.actions;


        if (this.dragPointElem) {
          this.dragPointElem.addEventListener('touchstart', this.onTouchStart);
          this.dragPointElem.addEventListener('touchend', actions.onTouchDrop);
          this.dragPointElem.addEventListener('drag', function (e) {
            actions.onDrag(e, _this2.dragPreviewRef);
          });
          this.dragPointElem.addEventListener('dragstart', this.onSetImageDragStart);
          this.dragPointElem.addEventListener('dragend', actions.onDragEnd);
          this.dragPointElem.addEventListener('touchmove', function (e) {
            actions.onTouchMove(e, _this2.dragPreviewRef);
          });
          this.dragPointElem.addEventListener('click', this.onClick);
          this.itemRef.addEventListener('keydown', actions.onKeyChangeOrder);
        }
      }

      // only render necessary components

    }, {
      key: 'shouldComponentUpdate',
      value: function shouldComponentUpdate(nextProps) {
        var _props = this.props,
            state = _props.state,
            index = _props.index;

        // need to render all when toggling drag status

        if (nextProps.state.isDragging !== state.isDragging) {
          return true;
        }

        if (nextProps.state.isKeyboardMoving !== state.isKeyboardMoving) {
          return true;
        }

        if (state.isDragging) {
          // only render component around hot zone
          if (nextProps.state.sourceIndex === index || state.sourceIndex === index || nextProps.state.lastOverIndex === index || // as drag over does not happen sequentially, we need to render the last over element
          index >= nextProps.state.overIndex - 2 && index <= nextProps.state.overIndex + 2) {
            return true;
          }

          // when hovering on dragging element
          if (nextProps.state.overIndex === -1 && nextProps.state.overIndex !== state.overIndex) {
            if (index >= state.sourceIndex - 2 && index <= state.sourceIndex + 1) {
              return true;
            }
          }
        }

        // only render source and destination item
        if (state.isKeyboardMoving) {
          if (index + 1 === state.keyInsertIndex || index + 1 === nextProps.state.keyInsertIndex || index === 0) {
            // improve: should not always render index === 0
            return true;
          }
        }

        return this.havePropsChanged(nextProps);
      }
    }, {
      key: 'componentDidUpdate',
      value: function componentDidUpdate() {
        if (this.dragPointElem) {
          // we need to update index and preview value as we don't recreate dnd-item every time
          this.dragPointElem.removeEventListener('touchstart', this.onTouchStart);
          this.dragPointElem.addEventListener('touchstart', this.onTouchStart);
          this.dragPointElem.removeEventListener('dragstart', this.onSetImageDragStart);
          this.dragPointElem.addEventListener('dragstart', this.onSetImageDragStart);
          this.dragPointElem.removeEventListener('click', this.onClick);
          this.dragPointElem.addEventListener('click', this.onClick);
        }

        if (this.firstKeyInsertPlaceHolderRef && this.firstKeyInsertPlaceHolderRef.className.includes('show')) {
          this.firstKeyInsertPlaceHolderRef.focus();
        } else if (this.downKeyInsertPlaceHolderRef.className.includes('show')) {
          this.downKeyInsertPlaceHolderRef.focus();
        } else if (this.itemRef.className.includes('is-keyboard-moving') && this.itemRef.className.includes('should-on-focus')) {
          this.itemRef.focus();
        }
        if (this.dragPreviewRef.className.includes('show')) {
          this.dragPreviewRef.style.width = getComputedStyle(this.itemRef).getPropertyValue('width');
        }
      }
    }, {
      key: 'render',
      value: function render() {
        var _this3 = this;

        var _props2 = this.props,
            state = _props2.state,
            index = _props2.index,
            actions = _props2.actions,
            preview = _props2.preview;

        var itemSectionClass = ClassNames({
          'module-section': true,
          'is-dragging': state.isDragging && state.sourceIndex === index,
          'is-keyboard-moving': state.isKeyboardMoving && index === state.sourceIndex,
          'should-on-focus': state.isKeyboardMoving && (index === state.keyInsertIndex || index + 1 === state.keyInsertIndex)
        });
        var dropUpHalfClass = ClassNames({
          'drop-up-half': true,
          show: state.isDragging && index !== state.sourceIndex && index !== state.sourceIndex + 1
        });
        var dropDownHalfClass = ClassNames({
          'drop-down-half': true,
          show: state.isDragging && index !== state.sourceIndex && index !== state.sourceIndex - 1
        });
        var firstKeyInsertPlaceHolderClass = ClassNames({
          'key-insert-placeholder': true,
          show: state.isKeyboardMoving && state.keyInsertIndex === 0 && state.sourceIndex !== 0
        });
        var firstInsertPlaceHolderClass = ClassNames({
          'insert-placeholder first-insert-placeholder': true,
          show: state.isDragging && state.overIndex === 0
        });
        var insertPlaceholderClass = ClassNames({
          'insert-placeholder': true,
          show: state.isDragging && index === state.overIndex - 1
        });
        var dragPreviewItemClass = ClassNames({
          'drag-preview-item': true,
          show: state.isDragging && index === state.sourceIndex
        });
        var downKeyInsertPlaceHolderRef = ClassNames({
          'key-insert-placeholder': true,
          show: state.isKeyboardMoving && index + 1 === state.keyInsertIndex && index !== state.sourceIndex
        });

        return React.createElement(
          'div',
          { className: itemSectionClass, ref: function ref(_ref5) {
              _this3.itemRef = _ref5;
            }, tabIndex: '-1' },
          index === 0 ? React.createElement('div', { className: firstInsertPlaceHolderClass }) : '',
          index === 0 ? React.createElement(
            'div',
            {
              tabIndex: '-1',
              className: firstKeyInsertPlaceHolderClass,
              onKeyDown: actions.onKeyChangeOrder,
              ref: function ref(_ref2) {
                _this3.firstKeyInsertPlaceHolderRef = _ref2;
              }
            },
            state.curPreview
          ) : '',
          React.createElement(
            'div',
            { className: 'item-container' },
            React.createElement(WrappedComponent, _extends({}, omit(this.props, 'state', 'key', 'actions', 'onReorderItem'), {
              dragPointRef: function dragPointRef(el) {
                _this3.dragPointElem = el;
              }
            }))
          ),
          React.createElement('div', {
            className: dropUpHalfClass,
            'data-position': index,
            onDrop: this.onDrop,
            onDragOver: this.onDragOver,
            onDragLeave: this.onDragLeave
          }),
          React.createElement('div', {
            className: dropDownHalfClass,
            'data-position': index + 1,
            onDrop: this.onDropNextIndex,
            onDragOver: this.onDragOverNextIndex,
            onDragLeave: this.onDragLeave
          }),
          React.createElement('div', { className: insertPlaceholderClass }),
          React.createElement(
            'div',
            {
              className: dragPreviewItemClass,
              ref: function ref(_ref3) {
                _this3.dragPreviewRef = _ref3;
              }
            },
            preview
          ),
          React.createElement(
            'div',
            {
              tabIndex: '-1',
              className: downKeyInsertPlaceHolderRef,
              onKeyDown: actions.onKeyChangeOrder,
              ref: function ref(_ref4) {
                _this3.downKeyInsertPlaceHolderRef = _ref4;
              }
            },
            state.curPreview
          )
        );
      }
    }]);

    return Wrapper;
  }(React.Component);

  Wrapper.propTypes = {
    index: PropTypes.number.isRequired,
    actions: PropTypes.object.isRequired,
    state: PropTypes.object.isRequired,
    preview: PropTypes.element.isRequired
  };

  Wrapper.displayName = 'DragNDropItem(' + getDisplayName(WrappedComponent) + ')';

  return Wrapper;
};

export default DragNDropItem;