import React from 'react';

import ReactDraggable from 'react-draggable';

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.state;
      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.scale;
      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.scale;
      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.scale;
        let _parent;
        if("parent" === this.props.bounds) {
            _parent = parent
        } else {
            if("body" === this.props.bounds) {
                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("window" === this.props.bounds) {
                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();
            const 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,
                        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.scale;
    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 = "parent" === this.props.bounds ? parent: "body" === this.props.bounds ? document.body: "window" === this.props.bounds ? 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 && "string" == typeof maxWidth) {
                if(maxWidth.endsWith("%")) {
                    const delt = Number(maxWidth.replace("%", "")) / 100;
                    maxWidth = parentSize.width * delt;
                } else {
                    maxWidth.endsWith("px") && (maxWidth = Number(maxWidth.replace("px", "")))
                }
            }

            if(maxHeight && "string" == typeof maxHeight) {
                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();

            const _rect_ = "window" === this.props.bounds ? {
                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;
      -1 !== ["top", "left", "topLeft", "bottomLeft", "topRight"].indexOf(placment) && ("bottomLeft" === placment ? original.x += width: ("topRight" === placment || (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: x,
        y: y
      };
      this.props.onResize && this.props.onResize(event, placment, position, target, {
        x: x,
        y: y
      })

  }

  onResizeStop(event, placment, position, target) {
    this.resizing = !1;
    const { maxWidth, maxHeight } = this.getMaxSizesFromProps();
    this.setState({
        maxHeight: maxHeight,
        maxWidth: 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.scale;
      const parent = this.getParent();
      const selfElement = this.getSelfElement();
      if(!parent || null === selfElement) {
          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.props;
      
      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) {
        var n = {};
        for (var r in e)
            Object.prototype.hasOwnProperty.call(e, r) && t.indexOf(r) < 0 && (n[r] = e[r]);
        if (null != e && "function" == typeof Object.getOwnPropertySymbols) {
            var o = 0;
            for (r = Object.getOwnPropertySymbols(e); o < r.length; o++)
                t.indexOf(r[o]) < 0 && Object.prototype.propertyIsEnumerable.call(e, r[o]) && (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={"boolean" == typeof enableResizing ? (
                        _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: function() {},
  onResize: function() {},
  onResizeStop: function() {},
  onDragStart: function() {},
  onDrag: function() {},
  onDragStop: function() {},
};
export default Draggable;
