<template>
    <div class="w-100 position-relative">
        <tree :data="treeData"
              :options="treeOptions"
              ref="tree">
            <div slot-scope="a"
                 class="node-container">
                <div class="node-text">
                    <slot name="text"
                          v-bind:node="a.node"></slot>
                </div>
                <div class="node-controls">
                    <slot name="controls"
                          v-bind:node="a.node"></slot>
                </div>
            </div>
        </tree>
        <j-loading v-model="isMoving"></j-loading>
    </div>
</template>

<script>
import { guid } from "libj-core";
import LiquorTree from 'liquor-tree';
import { cultureManager } from "libj-core";
export default {
    components: {
        [LiquorTree.name]: LiquorTree
    },
    props: {
        items: {
            default() {
                return [];
            }
        },
        emptyParentId: {
            default: guid.empty()
        },
        idProp: {
            default: 'id'
        },
        parentIdProp: {
            default: 'parentId'
        },
        /**
         * set to null to show that items have no sequence
         */
        orderProp: {
            default: 'order'
        },
        expandedFirst: {
            default: true
        },
    },
    data() {
        return {
            treeData: [],
            treeOptions: {
                direction: cultureManager.getActiveCulture().direction,
                checkbox: false,
                expanded: true,
                dnd: {
                    onDragOn: this.onDrag,
                    onDragFinish: this.onDrop
                },
            },
            dragginNodeId: null,
            isMoving: false,
        };
    },
    mounted() {
        this.$nextTick(() => {
            setTimeout(() => {
                this.reload();
            }, 150);
        });
    },
    methods: {
        reload() {
            // clear tree nodes
            this.$refs.tree.remove(/.+/i, true);

            // add items as nodes
            var rootItems = this.getItemChildren(this.emptyParentId);
            var rootNodes = rootItems.map(rootItem => this.createNodeRecursive(rootItem));
            rootNodes.forEach(n => this.$refs.tree.append(n));
        },
        createNodeRecursive(item) {
            // create node
            var node = this.item_to_tree_node(item);
            // find child items
            var childItems = this.getItemChildren(item[this.idProp]);
            childItems.forEach(childItem => {
                var childNode = this.createNodeRecursive(childItem);
                node.children.push(childNode);
            });
            // return node
            return node;
        },
        getItemChildren(parentId) {
            var res = this.items.filter(a => a[this.parentIdProp] === parentId);
            if (this.orderProp !== undefined && this.orderProp !== null) {
                res.sort((a, b) => {
                    var aa = a[this.orderProp];
                    var bb = b[this.orderProp];
                    if (aa < bb) {
                        return -1;
                    } else if (aa > bb) {
                        return 1;
                    }
                    return 0;
                });
            }
            return res;
        },
        item_to_tree_node(item) {
            return {
                text: item[this.idProp],
                data: item,
                children: [],
                state: {
                    expanded: this.expandedFirst
                }
            };
        },
        add(parentNode, item) {
            var node = this.item_to_tree_node(item);
            if (parentNode === undefined || parentNode === null) {
                //add to root
                this.$refs.tree.append(node);
            } else {
                parentNode.append(node);
            }
        },
        update(node, item) {
            node.data = Object.assign({}, node.data, item);
        },
        delete(id) {
            this.$refs.tree.remove({
                text: id
            });
        },
        getNodeOrder(node) {
            return node.data[this.orderProp];
        },
        getRootNodes() {
            return this.$refs.tree.model;
        },
        findById(id) {
            var roots = this.getRootNodes();
            for (let i = 0; i < roots.length; i++) {
                const root = roots[i];
                var res = this.BFS_Search(root, a => a.data[this.idProp] === id);
                if (res !== null && res !== undefined) {
                    return res;
                }
            }
        },
        BFS_Search(node, predicate) {
            if (predicate(node) === true) {
                return node;
            }
            for (let i = 0; i < node.children.length; i++) {
                const child = node.children[i];
                var res = this.BFS_Search(child, predicate);
                if (res !== null && res !== undefined) {
                    return res;
                }
            }
            return null;
        },
        onDrag(node) {
            this.dragginNodeId = node.data[this.idProp];
        },
        onDrop(node, destNode) {
            if (this.dragginNodeId === undefined || this.dragginNodeId === null) {
                return;
            }
            this.isMoving = true;
            var nodeId = this.dragginNodeId;
            this.dragginNodeId = null;
            setTimeout(() => {
                var finalNode = this.findById(nodeId);
                var parentNode = finalNode.parent;
                var parentChildren = parentNode === undefined || parentNode === null ?
                    this.getRootNodes() : parentNode.children;
                this.$emit('move', {
                    //node itself
                    node: finalNode,
                    //node's parent
                    parentNode: parentNode,
                    //all children of parent
                    parentChildren: parentChildren,
                    completed: () => { this.isMoving = false; }
                });
            }, 700);
        }
    },
    watch: {
        items: {
            handler() {
                this.reload();
            },
            deep: true
        }
    }
}
</script>