var __assign = (this && this.__assign) || function () {
    __assign = Object.assign || function(t) {
        for (var s, i = 1, n = arguments.length; i < n; i++) {
            s = arguments[i];
            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
                t[p] = s[p];
        }
        return t;
    };
    return __assign.apply(this, arguments);
};
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
    if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
        if (ar || !(i in from)) {
            if (!ar) ar = Array.prototype.slice.call(from, 0, i);
            ar[i] = from[i];
        }
    }
    return to.concat(ar || Array.prototype.slice.call(from));
};
import { always, assoc, assocPath, ifElse, intersperse, insert, last, map, pipe, path, remove, split } from 'ramda';
import { AddNodePosition } from './types';
export var DEPTH_LEVEL_INDENT = 16;
var DROP_INDICATOR_NODE_KEY = '-1';
var CONTAINER_PADDINGS = 29;
var NODE_TITLE_MAX_WIDTH = 204;
var INITIAL_NODE_DEPTH = 2;
export var defaultIsLeaf = function (_a) {
    var _b = _a.children, children = _b === void 0 ? [] : _b;
    return !children.length;
};
export var convertTreeData = function (_a) {
    var data = _a.data, isLeaf = _a.isLeaf;
    return data.map(function (item) {
        var _a;
        return __assign({ key: String(item.nodeId), title: (_a = item.title) !== null && _a !== void 0 ? _a : '', value: __assign({}, item), isLeaf: isLeaf(item) }, (item.children &&
            item.children.length && {
            children: convertTreeData({ data: item.children, isLeaf: isLeaf })
        }));
    });
};
var getPathFromPosition = pipe(split('-'), remove(0, 1), map(Number), intersperse('children'));
export var getNodeAtPosition = function (treeData, pos) {
    var nodePath = getPathFromPosition(pos);
    return nodePath.length > 0 ? path(nodePath, treeData) || null : null;
};
export var getParentPos = function (pos) {
    return pos.split('-').slice(0, -1).join('-');
};
export var getDropNodeParent = function (treeData, dropPositionInfo, dragNodePosition) {
    var parentNodePosition = dropPositionInfo.dropPosition === AddNodePosition.AsChild
        ? dropPositionInfo.pos
        : getParentPos(dropPositionInfo.pos);
    var parentNode = getNodeAtPosition(treeData, // current tree
    // position in the current tree
    slideNodePos(parentNodePosition, // position in the next tree
    dragNodePosition, // position of drag node in the current tree
    false));
    return parentNode;
};
export var changeNodeAtPosition = function (treeData, pos, changeNode) {
    var nodePath = getPathFromPosition(pos);
    var node = path(nodePath, treeData);
    return node ? assocPath(nodePath, changeNode(node), treeData) : treeData;
};
export var removeNodeAtPosition = function (treeData, pos) {
    var nodePath = getPathFromPosition(pos);
    var childrenPath = nodePath.slice(0, -1);
    var nodeIndex = Number(nodePath[nodePath.length - 1]);
    if (childrenPath.length > 0) {
        var children = path(childrenPath)(treeData);
        var newChildren = remove(nodeIndex, 1, children);
        return assocPath(childrenPath, newChildren, treeData);
    }
    return remove(nodeIndex, 1, treeData);
};
export var insertNodeAtPosition = function (treeData, pos, dropPosition, node) {
    var nodePath = getPathFromPosition(pos);
    var childrenPath = nodePath.slice(0, -1);
    var nodeIndex = Number(nodePath[nodePath.length - 1]);
    var newNodeIndex = dropPosition === AddNodePosition.Top ? nodeIndex : nodeIndex + 1;
    if (childrenPath.length > 0) {
        var children = path(childrenPath, treeData);
        if (!children)
            return treeData;
        var newChildren = insert(newNodeIndex, node, children);
        return assocPath(childrenPath, newChildren, treeData);
    }
    return insert(newNodeIndex, node, treeData);
};
export var moveNode = function (treeData, dragNode, dragNodePos, dropPositionInfo) {
    var pos = dropPositionInfo.pos, dropPosition = dropPositionInfo.dropPosition, _a = dropPositionInfo.addToEnd, addToEnd = _a === void 0 ? false : _a;
    var removeNode = function (treeData) { return removeNodeAtPosition(treeData, dragNodePos); };
    var addNode = function (treeData) {
        return dropPosition === AddNodePosition.AsChild
            ? changeNodeAtPosition(treeData, pos, function (node) {
                return pipe(ifElse(always(addToEnd), assoc('children', __spreadArray(__spreadArray([], (node.children || []), true), [dragNode], false)), assoc('children', __spreadArray([dragNode], (node.children || []), true))), assoc('expanded', true))(node);
            })
            : insertNodeAtPosition(treeData, pos, dropPosition, dragNode);
    };
    return pipe(removeNode, addNode)(treeData);
};
export var getExpandedKeys = function (nodes) {
    if (nodes === void 0) { nodes = []; }
    return nodes.reduce(function (expandedKeys, node) {
        return expandedKeys.concat(node.expanded ? [String(node.nodeId)] : []).concat(getExpandedKeys(node.children));
    }, []);
};
export var slideNodePos = function (dropNodePos, nodePos, backward) {
    if (backward === void 0) { backward = false; }
    var nodePosition = nodePos.split('-');
    var nodeDepth = nodePosition.length - 1;
    var nodeParentPosition = nodePosition.slice(0, -1);
    var nodeParentPos = nodeParentPosition.join('-');
    var dropNodePosition = dropNodePos.split('-');
    if (dropNodePos.startsWith(nodeParentPos)) {
        var nodeLastIndex = Number(last(nodePosition));
        var dropNodeIndexToChange = Number(dropNodePosition[nodeDepth]);
        if (backward) {
            if (nodeLastIndex < dropNodeIndexToChange) {
                return dropNodePos.replace([nodeParentPos, dropNodeIndexToChange].join('-'), [nodeParentPos, dropNodeIndexToChange - 1].join('-'));
            }
        }
        else {
            if (nodeLastIndex <= dropNodeIndexToChange) {
                return dropNodePos.replace([nodeParentPos, dropNodeIndexToChange].join('-'), [nodeParentPos, dropNodeIndexToChange + 1].join('-'));
            }
        }
    }
    return dropNodePos;
};
var getNodeTopElement = function (target) {
    return target.closest('.rc-tree-treenode');
};
var calcAddNodePosition = function (event) {
    var nodeElement = getNodeTopElement(event.target);
    if (!nodeElement)
        return null;
    var offsetY = event.clientY - nodeElement.getBoundingClientRect().top || 0;
    var treeNodeHeight = nodeElement.offsetHeight;
    return offsetY < treeNodeHeight / 2 ? AddNodePosition.Top : AddNodePosition.Bottom;
};
var findDepthPosition = function (event, dropNode) {
    var nodeElement = getNodeTopElement(event.target);
    if (!nodeElement)
        return null;
    var dropNodeLevel = dropNode.pos.split('-').length - 1;
    var leftPadding = parseFloat(window.getComputedStyle(nodeElement).paddingLeft) || 0;
    var offsetX = event.clientX - nodeElement.getBoundingClientRect().left - leftPadding || 0;
    return Math.min(dropNodeLevel + 1, Math.max(Math.round(offsetX / DEPTH_LEVEL_INDENT), dropNodeLevel));
};
var changePosLastIndex = function (pos, change) {
    var position = pos.split('-');
    var lastIndex = Number(last(position));
    return position
        .slice(0, -1)
        .concat(String(change(lastIndex)))
        .join('-');
};
var getCurrentDropNodePos = function (dropPositionInfo) {
    switch (dropPositionInfo.dropPosition) {
        case AddNodePosition.Top:
            return dropPositionInfo.pos;
        case AddNodePosition.Bottom:
            return changePosLastIndex(dropPositionInfo.pos, function (index) { return index + 1; });
        case AddNodePosition.AsChild:
            return "".concat(dropPositionInfo.pos, "-0");
    }
};
export var getDropNodePosition = function (event, dropNode, prevDropPositionInfo) {
    var addNodePosition = calcAddNodePosition(event);
    var depthPosition = findDepthPosition(event, dropNode);
    if (addNodePosition === null || depthPosition === null)
        return null;
    var pos = !prevDropPositionInfo || prevDropPositionInfo.addToEnd
        ? dropNode.pos
        : slideNodePos(dropNode.pos, getCurrentDropNodePos(prevDropPositionInfo), true);
    var position = pos.split('-');
    var dropNodeDepth = position.length - 1;
    var lastIndex = Number(last(position));
    if (depthPosition === dropNodeDepth) {
        return { pos: pos, dropPosition: addNodePosition };
    }
    if (addNodePosition === AddNodePosition.Top) {
        if (lastIndex > 0) {
            return {
                pos: position
                    .slice(0, -1)
                    .concat(String(lastIndex - 1))
                    .join('-'),
                dropPosition: AddNodePosition.AsChild,
                addToEnd: true
            };
        }
        if (position.length > 2)
            return { pos: position.slice(0, -1).join('-'), dropPosition: AddNodePosition.AsChild, addToEnd: false };
        return { pos: pos, dropPosition: AddNodePosition.Top };
    }
    return { pos: pos, dropPosition: AddNodePosition.AsChild };
};
export var isDropIndicatorNode = function (node) { return (node === null || node === void 0 ? void 0 : node.key) === DROP_INDICATOR_NODE_KEY; };
export var gatherNodeKeys = function (node) {
    return node ? __spreadArray([node.key], (node.children ? node.children.flatMap(gatherNodeKeys) : []), true) : [];
};
export var getLevelLinesData = function (treeData) {
    var getLinesData = function (data, prevLinesData) {
        return data.reduce(function (linesData, node, index) {
            var isLast = index === data.length - 1;
            linesData[String(node.nodeId)] = [prevLinesData, isLast];
            if (node.children && node.expanded) {
                var nextLinesData = prevLinesData.concat(!isLast);
                return __assign(__assign({}, linesData), getLinesData(node.children, nextLinesData));
            }
            return linesData;
        }, {});
    };
    return getLinesData(treeData, []);
};
var getTreeMaxDepth = function (data, depth) {
    if (depth === void 0) { depth = INITIAL_NODE_DEPTH; }
    return data.reduce(function (maxDepth, node) {
        return node.expanded && node.children
            ? Math.max(maxDepth, getTreeMaxDepth(node.children, depth + 1))
            : maxDepth;
    }, depth);
};
export var getTreeMaxWidth = function (data) {
    return getTreeMaxDepth(data) * DEPTH_LEVEL_INDENT + NODE_TITLE_MAX_WIDTH + CONTAINER_PADDINGS;
};
export var calcNodeWidth = function (depth, containerWidth) {
    if (depth === void 0) { depth = 0; }
    var leftIndent = (depth + INITIAL_NODE_DEPTH) * DEPTH_LEVEL_INDENT;
    var paddings = leftIndent + CONTAINER_PADDINGS;
    return Math.max(containerWidth - paddings, NODE_TITLE_MAX_WIDTH);
};
