"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var react_dnd_1 = require("react-dnd");
var react_dom_1 = require("react-dom");
var ramda_1 = require("ramda");
var tree_1 = require("./tree");
var memoizedTreeData_1 = require("./memoizedTreeData");
var DndManager = /** @class */ (function () {
    function DndManager(treeRef) {
        var _this = this;
        this.hoverHandler = function () {
            if (_this.hoverProps === null) {
                return;
            }
            var _a = _this.hoverProps, dropTargetProps = _a.dropTargetProps, monitor = _a.monitor, component = _a.component;
            var item = monitor.getItem();
            if (!item) {
                return;
            }
            var targetDepth = _this.getTargetDepth(dropTargetProps, monitor, component);
            var draggedNode = item.node;
            var needsRedraw = dropTargetProps.node !== _this.lastDropTargetNode || targetDepth !== _this.lastDropTargetDepth;
            if (!needsRedraw) {
                return;
            }
            var treeIndex = dropTargetProps.treeIndex, swapFrom = dropTargetProps.swapFrom;
            if (component) {
                var boundingRect = (0, react_dom_1.findDOMNode)(component).getBoundingClientRect(); // eslint-disable-line react/no-find-dom-node
                var ownMiddleY = (boundingRect.bottom - boundingRect.top) / 2;
                var clientOffset = monitor.getClientOffset();
                // Moving down: exit if we're in upper half
                if (swapFrom <= treeIndex && clientOffset.y < boundingRect.top + ownMiddleY) {
                    return;
                }
                // Moving up: exit if we're in lower half
                if (swapFrom >= treeIndex && clientOffset.y > boundingRect.top + ownMiddleY) {
                    return;
                }
            }
            _this.lastDropTargetNode = dropTargetProps.node;
            _this.lastDropTargetDepth = targetDepth;
            _this.dragHover({
                node: draggedNode,
                path: item.path,
                minimumTreeIndex: dropTargetProps.listIndex,
                depth: targetDepth
            });
            _this.hoverProps = null;
        };
        this.deferredCallHoverHandler = function () {
            if (_this.shouldSkipMoreFrames) {
                _this.shouldSkipMoreFrames = false;
                _this.rafId = requestAnimationFrame(_this.deferredCallHoverHandler);
                return;
            }
            _this.rafId = null;
            _this.hoverHandler();
        };
        this.treeRef = treeRef;
        this.resetDragVariables();
    }
    Object.defineProperty(DndManager.prototype, "startDrag", {
        get: function () {
            return this.treeRef.startDrag;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(DndManager.prototype, "dragHover", {
        get: function () {
            return this.treeRef.dragHover;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(DndManager.prototype, "endDrag", {
        get: function () {
            return this.treeRef.endDrag;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(DndManager.prototype, "drop", {
        get: function () {
            return this.treeRef.drop;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(DndManager.prototype, "treeId", {
        get: function () {
            return this.treeRef.treeId;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(DndManager.prototype, "dndType", {
        get: function () {
            return this.treeRef.dndType;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(DndManager.prototype, "treeData", {
        get: function () {
            return this.treeRef.state.draggingTreeData || this.treeRef.props.treeData;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(DndManager.prototype, "getNodeKey", {
        get: function () {
            return this.treeRef.props.getNodeKey;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(DndManager.prototype, "customCanDrop", {
        get: function () {
            return this.treeRef.props.canDrop;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(DndManager.prototype, "maxDepth", {
        get: function () {
            return this.treeRef.props.maxDepth;
        },
        enumerable: false,
        configurable: true
    });
    DndManager.prototype.resetDragVariables = function () {
        this.lastDropTargetNode = null;
        this.lastDropTargetDepth = null;
        this.hoverProps = null;
        this.lastCanDropResult = null;
        this.lastCanDropProps = null;
        this.shouldSkipMoreFrames = false;
        this.rafId = null;
    };
    DndManager.prototype.getTargetDepth = function (dropTargetProps, monitor, component) {
        var dropTargetDepth = 0;
        var rowAbove = dropTargetProps.getPrevRow();
        if (rowAbove) {
            var path = rowAbove.path;
            var aboveNodeCannotHaveChildren = !this.treeRef.canNodeHaveChildren(rowAbove.node);
            if (aboveNodeCannotHaveChildren) {
                path = path.slice(0, path.length - 1);
            }
            // Limit the length of the path to the deepest possible
            dropTargetDepth = Math.min(path.length, dropTargetProps.path.length);
        }
        var blocksOffset;
        var dragSourceInitialDepth = (monitor.getItem().path || []).length;
        // When adding node from external source
        if (monitor.getItem().treeId !== this.treeId) {
            // Ignore the tree depth of the source, if it had any to begin with
            dragSourceInitialDepth = 0;
            if (component) {
                var relativePosition = (0, react_dom_1.findDOMNode)(component).getBoundingClientRect(); // eslint-disable-line react/no-find-dom-node
                var leftShift = monitor.getSourceClientOffset().x - relativePosition.left;
                blocksOffset = Math.round(leftShift / dropTargetProps.scaffoldBlockPxWidth);
            }
            else {
                blocksOffset = dropTargetProps.path.length;
            }
        }
        else {
            // handle row direction support
            var direction = dropTargetProps.rowDirection === 'rtl' ? -1 : 1;
            blocksOffset = Math.round((direction * monitor.getDifferenceFromInitialOffset().x) / dropTargetProps.scaffoldBlockPxWidth);
        }
        var targetDepth = Math.min(dropTargetDepth, Math.max(0, dragSourceInitialDepth + blocksOffset - 1));
        // If a maxDepth is defined, constrain the target depth
        if (typeof this.maxDepth !== 'undefined' && this.maxDepth !== null) {
            var draggedNode = monitor.getItem().node;
            var draggedChildDepth = (0, tree_1.getDepth)(draggedNode);
            targetDepth = Math.max(0, Math.min(targetDepth, this.maxDepth - draggedChildDepth - 1));
        }
        return targetDepth;
    };
    DndManager.prototype.canDrop = function (dropTargetProps, monitor) {
        if (!monitor.isOver()) {
            return false;
        }
        var rowAbove = dropTargetProps.getPrevRow();
        var abovePath = rowAbove ? rowAbove.path : [];
        var aboveNode = rowAbove ? rowAbove.node : {};
        var blocksOffset = Math.round(monitor.getDifferenceFromInitialOffset().x / dropTargetProps.scaffoldBlockPxWidth);
        var newCanDropProps = {
            nodeId: dropTargetProps.node.nodeId !== undefined ? dropTargetProps.node.nodeId : dropTargetProps.node,
            rowAbove: rowAbove,
            abovePath: abovePath,
            aboveNodeId: aboveNode.nodeId !== undefined ? aboveNode.nodeId : aboveNode,
            blocksOffset: blocksOffset
        };
        if ((0, ramda_1.equals)(this.lastCanDropProps, newCanDropProps)) {
            return this.lastCanDropResult;
        }
        this.lastCanDropProps = newCanDropProps;
        var targetDepth = this.getTargetDepth(dropTargetProps, monitor, null);
        // Cannot drop if we're adding to the children of the row above and
        //  the row above is a function
        if (targetDepth >= abovePath.length && typeof aboveNode.children === 'function') {
            return (this.lastCanDropResult = false);
        }
        if (typeof this.customCanDrop === 'function') {
            var node = monitor.getItem().node;
            var addedResult = (0, memoizedTreeData_1.memoizedInsertNode)({
                treeData: this.treeData,
                newNode: node,
                depth: targetDepth,
                getNodeKey: this.getNodeKey,
                minimumTreeIndex: dropTargetProps.listIndex,
                expandParent: true
            });
            return (this.lastCanDropResult = this.customCanDrop({
                node: node,
                prevPath: monitor.getItem().path,
                prevParent: monitor.getItem().parentNode,
                prevTreeIndex: monitor.getItem().treeIndex, // Equals -1 when dragged from external tree
                nextPath: addedResult.path,
                nextParent: addedResult.parentNode,
                nextTreeIndex: addedResult.treeIndex
            }));
        }
        return (this.lastCanDropResult = true);
    };
    DndManager.prototype.wrapSource = function (el) {
        var _this = this;
        var nodeDragSource = {
            beginDrag: function (props) {
                _this.startDrag(props);
                return {
                    node: props.node,
                    parentNode: props.parentNode,
                    path: props.path,
                    treeIndex: props.treeIndex,
                    treeId: props.treeId
                };
            },
            endDrag: function (props, monitor) {
                if (_this.rafId) {
                    cancelAnimationFrame(_this.rafId);
                }
                _this.endDrag(monitor.getDropResult());
                _this.resetDragVariables();
            },
            isDragging: function (props, monitor) {
                var dropTargetNode = monitor.getItem().node;
                var draggedNode = props.node;
                return draggedNode === dropTargetNode;
            }
        };
        function nodeDragSourcePropInjection(connect, monitor) {
            return {
                connectDragSource: connect.dragSource(),
                connectDragPreview: connect.dragPreview(),
                isDragging: monitor.isDragging(),
                didDrop: monitor.didDrop()
            };
        }
        return (0, react_dnd_1.DragSource)(this.dndType, nodeDragSource, nodeDragSourcePropInjection)(el);
    };
    DndManager.prototype.wrapTarget = function (el) {
        var _this = this;
        var nodeDropTarget = {
            drop: function (dropTargetProps, monitor, component) {
                if (_this.rafId) {
                    cancelAnimationFrame(_this.rafId);
                }
                var result = {
                    node: monitor.getItem().node,
                    path: monitor.getItem().path,
                    treeIndex: monitor.getItem().treeIndex,
                    treeId: _this.treeId,
                    minimumTreeIndex: dropTargetProps.treeIndex,
                    depth: _this.getTargetDepth(dropTargetProps, monitor, component)
                };
                _this.lastDropTargetNode = null;
                _this.drop(result);
                return result;
            },
            hover: function (dropTargetProps, monitor, component) {
                if (_this.rafId) {
                    _this.shouldSkipMoreFrames = true;
                    return;
                }
                _this.shouldSkipMoreFrames = false;
                _this.hoverProps = {
                    dropTargetProps: dropTargetProps,
                    monitor: monitor,
                    component: component
                };
                _this.rafId = requestAnimationFrame(_this.deferredCallHoverHandler);
            },
            canDrop: this.canDrop.bind(this)
        };
        function nodeDropTargetPropInjection(connect, monitor) {
            var dragged = monitor.getItem();
            return {
                connectDropTarget: connect.dropTarget(),
                isOver: monitor.isOver(),
                canDrop: monitor.canDrop(),
                draggedNode: dragged ? dragged.node : null
            };
        }
        return (0, react_dnd_1.DropTarget)(this.dndType, nodeDropTarget, nodeDropTargetPropInjection)(el);
    };
    DndManager.prototype.wrapPlaceholder = function (el) {
        var _this = this;
        var placeholderDropTarget = {
            drop: function (dropTargetProps, monitor) {
                var _a = monitor.getItem(), node = _a.node, path = _a.path, treeIndex = _a.treeIndex;
                var result = {
                    node: node,
                    path: path,
                    treeIndex: treeIndex,
                    treeId: _this.treeId,
                    minimumTreeIndex: 0,
                    depth: 0
                };
                _this.drop(result);
                return result;
            }
        };
        function placeholderPropInjection(connect, monitor) {
            var dragged = monitor.getItem();
            return {
                connectDropTarget: connect.dropTarget(),
                isOver: monitor.isOver(),
                canDrop: monitor.canDrop(),
                draggedNode: dragged ? dragged.node : null
            };
        }
        return (0, react_dnd_1.DropTarget)(this.dndType, placeholderDropTarget, placeholderPropInjection)(el);
    };
    return DndManager;
}());
exports.default = DndManager;
