import React from 'react';

import ReactDraggable from 'react-draggable';
/* eslint-disable */
import Resize from './resize';
class Draggable extends React.Component {
  constructor(props) {
    super(props);
    this.resizing = false;
    this.resizingPosition = {
      x: 0,
      y: 0,
    };
    this.offsetFromParent = {
      left: 0,
      top: 0,
    };
    this.resizableElement = {
      current: null,
    };

    this.refDraggable = ref => {
      ref && (this.draggable = ref);
    };
    this.refResizable = resize => {
      resize &&
        ((this.resizable = resize),
        (this.resizableElement.current = resize.resizable));
    };
    this.state = {
      original: {
        x: 0,
        y: 0,
      },
      bounds: {
        top: 0,
        right: 0,
        bottom: 0,
        left: 0,
      },
      maxWidth: props.maxWidth,
      maxHeight: props.maxHeight,
    };
    this.onResizeStart = this.onResizeStart.bind(this);
    this.onResize = this.onResize.bind(this);
    this.onResizeStop = this.onResizeStop.bind(this);
    this.onDragStart = this.onDragStart.bind(this);
    this.onDrag = this.onDrag.bind(this);
    this.onDragStop = this.onDragStop.bind(this);
    this.getMaxSizesFromProps = this.getMaxSizesFromProps.bind(this);
  }

  componentDidMount() {
    this.updateOffsetFromParent();
    const { left, top } = this.offsetFromParent;
    const { x, y } = this.getDraggablePosition();
    this.draggable.setState({
      x: x - left,
      y: y - top,
    });
    this.forceUpdate();
  }

  getDraggablePosition() {
    const { state } = this.draggable;
    return {
      x: state.x,
      y: state.y,
    };
  }

  getParent() {
    return this.resizable && this.resizable.parentNode;
  }

  getParentSize() {
    return this.resizable.getParentSize();
  }

  getMaxSizesFromProps() {
    return {
      maxWidth:
        void 0 === this.props.maxWidth
          ? Number.MAX_SAFE_INTEGER
          : this.props.maxWidth,
      maxHeight:
        void 0 === this.props.maxHeight
          ? Number.MAX_SAFE_INTEGER
          : this.props.maxHeight,
    };
  }

  getSelfElement() {
    return this.resizable && this.resizable.resizable;
  }

  getOffsetHeight(event) {
    const { scale } = this.props;
    switch (this.props.bounds) {
      case 'window':
        return window.innerHeight / scale;
      case 'body':
        return document.body.offsetHeight / scale;
      default:
        return event.offsetHeight;
    }
  }

  getOffsetWidth(event) {
    const { scale } = this.props;
    switch (this.props.bounds) {
      case 'window':
        return window.innerWidth / scale;
      case 'body':
        return document.body.offsetWidth / scale;
      default:
        return event.offsetWidth;
    }
  }

  onDragStart(event, position) {
    if (
      (this.props.onDragStart && this.props.onDragStart(event, position),
      this.props.bounds)
    ) {
      const parent = this.getParent();
      const { scale } = this.props;
      // eslint-disable-next-line no-underscore-dangle
      let _parent;
      if (this.props.bounds === 'parent') {
        _parent = parent;
      } else {
        if (this.props.bounds === 'body') {
          const rect = parent.getBoundingClientRect();
          const { left, top } = rect;
          const domRect = document.body.getBoundingClientRect();
          const deltLeft =
            -(left - parent.offsetLeft * scale - domRect.left) / scale;
          const deltTop =
            -(top - parent.offsetTop * scale - domRect.top) / scale;
          const deltRight =
            (document.body.offsetWidth - this.resizable.size.width * scale) /
              scale +
            deltLeft;
          const deltBottom =
            (document.body.offsetHeight - this.resizable.size.height * scale) /
              scale +
            deltTop;
          return this.setState({
            bounds: {
              top: deltTop,
              right: deltRight,
              left: deltLeft,
              bottom: deltBottom,
            },
          });
        }

        if (this.props.bounds === 'window') {
          if (!this.resizable) return;
          const rect = parent.getBoundingClientRect();
          const deltLeft = -(rect.left - parent.offsetLeft * scale) / scale;
          const deltTop = -(rect.top - parent.offsetTop * scale) / scale;
          const deltRight =
            (window.innerWidth - this.resizable.size.width * scale) / scale +
            deltLeft;
          const deltBottom =
            (window.innerHeight - this.resizable.size.height * scale) / scale +
            deltTop;
          return this.setState({
            bounds: {
              top: deltTop,
              right: deltRight,
              left: deltLeft,
              bottom: deltBottom,
            },
          });
        }
        _parent = document.querySelector(this.props.bounds);
      }
      if (_parent instanceof HTMLElement && parent instanceof HTMLElement) {
        const rect = _parent.getBoundingClientRect();
        const { left, top } = rect;
        const parentRect = parent.getBoundingClientRect();
        let deltLeft = (left - parentRect.left) / scale;
        const deltTop = top - deltLeft.top;
        if (this.resizable) {
          this.updateOffsetFromParent();
          const fromParent = this.offsetFromParent;
          this.setState({
            bounds: {
              top: deltTop - fromParent.top,
              right:
                deltLeft +
                (_parent.offsetWidth - this.resizable.size.width) -
                fromParent.left / scale,
              bottom:
                deltTop +
                (_parent.offsetHeight - this.resizable.size.height) -
                fromParent.top,
              // eslint-disable-next-line no-const-assign
              left: (deltLeft = fromParent.left / scale),
            },
          });
        }
      }
    }
  }

  onDrag(event, position) {
    if (this.props.onDrag) {
      const fromParent = this.offsetFromParent;
      return this.props.onDrag(
        event,
        Object.assign(Object.assign({}, position), {
          x: position.x - fromParent.left,
          y: position.y - fromParent.top,
        }),
      );
    }
  }

  onDragStop(event, position) {
    if (this.props.onDragStop) {
      const fromParent = this.offsetFromParent;
      return this.props.onDragStop(
        event,
        Object.assign(Object.assign({}, position), {
          x: position.y + fromParent.left,
          y: position.y + fromParent.top,
        }),
      );
    }
  }

  onResizeStart(event, placment, callback) {
    event.stopPropagation();
    this.resizing = !0;
    const { scale } = this.props;
    const fromParent = this.offsetFromParent;
    const dragPosition = this.getDraggablePosition();
    if (
      ((this.resizingPosition = {
        x: dragPosition.x + fromParent.left,
        y: dragPosition.y + fromParent.top,
      }),
      this.setState({
        original: dragPosition,
      }),
      this.props.bounds)
    ) {
      const parent = this.getParent();
      let element = void 0;
      element =
        this.props.bounds === 'parent'
          ? parent
          : this.props.bounds === 'body'
          ? document.body
          : this.props.bounds === 'window'
          ? window
          : document.querySelector(this.props.bounds);
      const selfElement = this.getSelfElement();
      if (
        selfElement instanceof Element &&
        (element instanceof HTMLElement || element === window) &&
        parent instanceof HTMLElement
      ) {
        const maxSizeProps = this.getMaxSizesFromProps();
        const parentSize = this.getParentSize();
        let { maxWidth, maxHeight } = maxSizeProps;
        if (maxWidth && typeof maxWidth === 'string') {
          if (maxWidth.endsWith('%')) {
            const delt = Number(maxWidth.replace('%', '')) / 100;
            maxWidth = parentSize.width * delt;
          } else {
            maxWidth.endsWith('px') &&
              (maxWidth = Number(maxWidth.replace('px', '')));
          }
        }

        if (maxHeight && typeof maxHeight === 'string') {
          if (maxHeight.endsWith('%')) {
            const delt = Number(maxHeight.replace('%', '')) / 100;
            maxHeight = parentSize.width * delt;
          } else {
            maxHeight.endsWith('px') &&
              (maxHeight = Number(maxHeight.replace('px', '')));
            maxHeight.endsWith('px') &&
              (maxHeight = Number(maxHeight.replace('px', '')));
          }
        }

        const rect = selfElement.getBoundingClientRect();

        // eslint-disable-next-line no-underscore-dangle
        const _rect_ =
          this.props.bounds === 'window'
            ? {
              left: 0,
              top: 0,
            }
            : element.getBoundingClientRect();

        const offsetWidth = this.getOffsetWidth();
        const offsetHeight = this.getOffsetHeight();
        const convertLeft = placment.toLowerCase().endsWith('left');
        const convertRight = placment.toLowerCase().endsWith('right');

        if (convertLeft && this.resizable) {
          const deltWidth =
            (rect.left - _rect_.left) / scale + this.resizable.size.width;
          this.setState({
            maxWidth: deltWidth > Number(maxWidth) ? maxWidth : deltWidth,
          });
        }

        if (convertRight || (this.props.lockAspectRatio && !convertLeft)) {
          const deltWidth = offsetWidth + (_rect_.left - rect.left) / scale;
          this.setState({
            maxWidth: deltWidth > Number(maxWidth) ? maxWidth : deltWidth,
          });
        }

        if (placment.startsWith('top') && this.resizable) {
          const deltHeight = rect.top - _rect_.top + this.resizable.size.height;
          this.setState({
            maxHeight: deltHeight > Number(maxHeight) ? maxHeight : deltHeight,
          });
        }

        if (
          placment.startsWith('bottom') ||
          (this.props.lockAspectRatio && !placment.startsWith('top'))
        ) {
          const deltHeight = offsetHeight + (_rect_.top - rect.top) / scale;
          this.setState({
            maxHeight: deltHeight > Number(maxHeight) ? maxHeight : deltHeight,
          });
        }
      }
    } else {
      this.setState({
        maxWidth: this.props.maxWidth,
        maxHeight: this.props.maxHeight,
      });
    }
    this.props.onResizeStart &&
      this.props.onResizeStart(event, placment, callback);
  }

  onResize(event, placment, position, target) {
    const original = {
      x: this.state.original.x,
      y: this.state.original.y,
    };
    const width = -target.width;
    const height = -target.height;
    ['top', 'left', 'topLeft', 'bottomLeft', 'topRight'].indexOf(placment) !==
      -1 &&
      (placment === 'bottomLeft'
        ? (original.x += width)
        : (placment === 'topRight' || (original.x += width),
          (original.y += height)));
    (original.x === this.draggable.state.x &&
      original.y === this.draggable.state.y) ||
      this.draggable.setState(original);
    this.updateOffsetFromParent();
    const fromParent = this.offsetFromParent;
    const x = this.getDraggablePosition().x + fromParent.left;
    const y = this.getDraggablePosition().y + fromParent.top;
    this.resizingPosition = {
      x,
      y,
    };
    this.props.onResize &&
      this.props.onResize(event, placment, position, target, {
        x,
        y,
      });
  }

  onResizeStop(event, placment, position, target) {
    this.resizing = !1;
    const { maxWidth, maxHeight } = this.getMaxSizesFromProps();
    this.setState({
      maxHeight,
      maxWidth,
    });
    this.props.onResizeStop &&
      this.props.onResizeStop(
        event,
        placment,
        position,
        target,
        this.resizingPosition,
      );
  }

  updateSize(target) {
    this.resizable &&
      this.resizable.updateSize({
        width: target.width,
        height: target.height,
      });
  }

  updatePosition(position) {
    this.draggable.setState(position);
  }

  updateOffsetFromParent() {
    const { scale } = this.props;
    const parent = this.getParent();
    const selfElement = this.getSelfElement();
    if (!parent || selfElement === null) {
      return {
        top: 0,
        left: 0,
      };
    }
    const rect = parent.getBoundingClientRect();
    const selfRect = selfElement.getBoundingClientRect();
    const dragPosition = this.getDraggablePosition();
    this.offsetFromParent = {
      left: selfRect.left - rect.left - dragPosition.x * scale,
      top: selfRect.top - rect.top - dragPosition.y * scale,
    };
  }

  render() {
    const { props } = this;

    const {
      disableDragging,
      style,
      dragHandleClassName,
      position,
      onMouseDown,
      onMouseUp,
      dragAxis,
      dragGrid,
      bounds,
      enableUserSelectHack,
      cancel,
      children,
      resizeHandleClasses,
      resizeHandleComponent,
      enableResizing,
      resizeGrid,
      resizeHandleWrapperClass,
      resizeHandleWrapperStyle,
      scale = 1,
      allowAnyClick,
    } = props;
    const event = (props.onResize,
    props.onResizeStop,
    props.onDragStart,
    props.onDrag,
    props.onDragStop,
    props.resizeHandleStyles);
    const _ = (function(e, t) {
      const n = {};
      // eslint-disable-next-line guard-for-in
      for (var r in e)
        Object.prototype.hasOwnProperty.call(e, r) &&
          t.indexOf(r) < 0 &&
          (n[r] = e[r]);
      if (e != null && typeof Object.getOwnPropertySymbols === 'function') {
        let o = 0;
        // eslint-disable-next-line block-scoped-var,no-plusplus
        for (r = Object.getOwnPropertySymbols(e); o < r.length; o++)
          t.indexOf(r[o]) < 0 &&
          // eslint-disable-next-line block-scoped-var
            Object.prototype.propertyIsEnumerable.call(e, r[o]) &&
          // eslint-disable-next-line block-scoped-var
            (n[r[o]] = e[r[o]]);
      }
      return n;
    })(props, [
      'disableDragging',
      'style',
      'dragHandleClassName',
      'position',
      'onMouseDown',
      'onMouseUp',
      'dragAxis',
      'dragGrid',
      'bounds',
      'enableUserSelectHack',
      'cancel',
      'children',
      'onResizeStart',
      'onResize',
      'onResizeStop',
      'onDragStart',
      'onDrag',
      'onDragStop',
      'resizeHandleStyles',
      'resizeHandleClasses',
      'resizeHandleComponent',
      'enableResizing',
      'resizeGrid',
      'resizeHandleWrapperClass',
      'resizeHandleWrapperStyle',
      'scale',
      'allowAnyClick',
    ]);
    const defaultPosition = this.props.default
      ? Object.assign({}, this.props.default)
      : void 0;
    delete _.default;
    let calcPostion;

    position &&
      (calcPostion = {
        x: position.x - this.offsetFromParent.left,
        y: position.y - this.offsetFromParent.top,
      });
    let _enableResizing;
    const _position_ = this.resizing ? void 0 : calcPostion;
    const styles = Object.assign(
      Object.assign(
        Object.assign(
          {},
          {
            width: this.props.default.width,
            height: this.props.default.height,
            display: 'inline-block',
            position: 'absolute',
            top: 0,
            left: 0,
            zIndex: 5000,
            boxShadow: 'rgba(0, 0, 0, 0.21) 0px 4px 16px 0px',
            backgroundColor: 'rgb(255, 255, 255)',
            overflow: 'hidden',
          },
        ),
        disableDragging || dragHandleClassName
          ? { cursor: 'auto' }
          : { cursor: 'move' },
      ),
      this.props.style,
    );

    return (
      <ReactDraggable
        ref={e => this.refDraggable(e)}
        handle={dragHandleClassName ? `.${dragHandleClassName}` : void 0}
        defaultPosition={defaultPosition}
        onMouseDown={onMouseDown}
        onMouseUp={onMouseUp}
        onStart={this.onDragStart}
        onDrag={this.onDrag}
        onStop={this.onDragStop}
        axis={dragAxis || 'both'}
        grid={dragGrid}
        position={_position_}
        bounds={bounds ? this.state.bounds : void 0}
        enableUserSelectHack={enableUserSelectHack}
        cancel={cancel}
        scale={scale}
        allowAnyClick={allowAnyClick}
        nodeRef={this.resizableElement}
      >
        <Resize
          ref={e => this.refResizable(e)}
          defaultSize={defaultPosition}
          size={this.props.size}
          enable={
            typeof enableResizing === 'boolean'
              ? ((_enableResizing = enableResizing),
                {
                  bottom: _enableResizing,
                  bottomLeft: _enableResizing,
                  bottomRight: _enableResizing,
                  left: _enableResizing,
                  right: _enableResizing,
                  top: _enableResizing,
                topLeft: _enableResizing,
                  topRight: _enableResizing,
              })
              : enableResizing
          }
          onResizeStart={this.onResizeStart}
          onResize={this.onResize}
          onResizeStop={this.onResizeStop}
          style={styles}
          minWidth={this.props.minWidth}
          minHeight={this.props.minHeight}
          maxWidth={this.resizing ? this.state.maxWidth : this.props.maxWidth}
          maxHeight={
            this.resizing ? this.state.maxHeight : this.props.maxHeight
          }
          grid={dragGrid}
          handleWrapperClass={resizeHandleWrapperClass}
          handleWrapperStyle={resizeHandleWrapperStyle}
          lockAspectRatio={this.props.lockAspectRatio}
          lockAspectRatioExtraWidth={this.props.lockAspectRatioExtraWidth}
          lockAspectRatioExtraHeight={this.props.lockAspectRatioExtraHeight}
          handleStyles={event}
          handleClasses={resizeHandleClasses}
          handleComponent={resizeHandleComponent}
          scale={this.props.scale}
        >
          {props.children}
        </Resize>
      </ReactDraggable>
    );
  }
}

Draggable.defaultProps = {
  maxWidth: Number.MAX_SAFE_INTEGER,
  maxHeight: Number.MAX_SAFE_INTEGER,
  scale: 1,
  onResizeStart() {},
  onResize() {},
  onResizeStop() {},
  onDragStart() {},
  onDrag() {},
  onDragStop() {},
};
export default Draggable;
