<template>
    <div class="yy-tree">
        <TreeNode v-for="node in rootNode.children" 
            :level="0"
            :node="node" 
            :key="node.key">
        </TreeNode>
    </div>
</template>

<script>
import TreeNode from './tree-node';
import { Node } from './node';

const OverState = {
    PREV: 0,
    INNER: 1,
    NEXT: 2
}

const OverStateName = {
    0: 'prev',
    1: 'inner',
    2: 'next'
}

const dragPlaceholder = document.createElement('div');
dragPlaceholder.className = 'yy-tree-drag-placeholder';

export default {
    name: 'YYTree',
    provide() {
        return {
            treeRoot: this
        }
    },
    components: {
        TreeNode
    },
    props: {
        data: {
            type: Array,
            default() {
                return [];
            }
        },
        props: {
            type: Object,
            default() {
                return {
                    children: 'children',
                    label: 'name',
                    isLeaf: undefined,
                    disabled: false
                }
            }
        },
        nodeKey: {
            type: String,
            default: 'id'
        },
        renderContent: Function,
        draggable: {
            type: Boolean,
            default: false
        },
        lazy: {
            type: Boolean,
            default: false
        },
        load: Function,
        highlightCurrent: {
            type: Boolean,
            default: false
        },
        defaultExpandAll: {
            type: Boolean,
            default: false
        },
        allowDrag: Function,
        allowDrop: Function
    },
    watch: {
        data(newVal) {
            // this.nodes = this.getNodes(newVal);
            this.rootNode.setChildrenData(newVal)
        }
    },
    data() {
        return {
            rootNode: null,
            dragState: {
                overNode: null,
                state: OverState.NEXT,
                draggingNode: null,
            }
        }
    },
    methods: {
        getNodes(list) {
            const _this = this;
            const props = this.props;
            const nodeKey = this.nodeKey;

            return list.map(data => {
                const node = new Node({
                    data: data,
                    label: data[props.label],
                    key: data[nodeKey],
                    __config__: {
                        props: _this.props,
                        nodeKey
                    }
                });
                if (data[props.children]) {
                    node.setChildrenData(data[props.children])
                }

                return node;
            })
        },
        onDragStart(event, nodeIns) {
            if (typeof this.allowDrag == 'function' && !this.allowDrag(nodeIns.node)) {
                event.preventDefault();
                return false;
            }
            this.dragState.draggingNode = nodeIns;
        },
        onDragOver(event, nodeIns) {

            const overEl = nodeIns.$refs.contentWrapper;
            const offsetY = event.offsetY;
            const elHeight = overEl.offsetHeight;

            const { overNode } = this.dragState;

            if (overNode) {
                overNode.node.draggingInner = false;
            }
            this.dragState.overNode = nodeIns;

            this.dragState.state = OverState.INNER;
            if (offsetY <= elHeight * 0.2) {
                this.dragState.state = OverState.PREV;
            } else if (offsetY >= elHeight * 0.8) {
                this.dragState.state = OverState.NEXT;
            }

            if (typeof this.allowDrop == 'function' && !this.allowDrop(this.dragState.draggingNode.node, nodeIns.node, OverStateName[this.dragState.state])) {
                return false;
            }


            dragPlaceholder.style.width = overEl.offsetWidth - (nodeIns.level + 1) * 20 + 'px';
            switch (this.dragState.state) {
                case OverState.INNER:
                    dragPlaceholder.style.display = 'none';
                    nodeIns.node.draggingInner = true;
                    break;
                case OverState.PREV:
                    dragPlaceholder.style.display = 'block';
                    overEl.parentNode.insertBefore(dragPlaceholder, overEl)
                    break;
                case OverState.NEXT:
                    dragPlaceholder.style.display = 'block';
                    dragPlaceholder.nextSibling;
                    dragPlaceholder.appendChild
                    const nextSibling = overEl.nextSibling;
                    if (!nextSibling) {
                        overEl.parentNode.appendChild(dragPlaceholder);
                    } else {
                        overEl.parentNode.insertBefore(dragPlaceholder, nextSibling);
                    }
            }
            return true;
        },
        onDragEnd(event) {
            const { draggingNode, overNode, state } = this.dragState;

            const dragNode = draggingNode.node;
            const dropNode = overNode.node;

            dropNode.resetDragState();
            dragPlaceholder.remove();
            if (typeof this.allowDrop == 'function' && !this.allowDrop(draggingNode.node, overNode.node, OverStateName[state])) {
                event.preventDefault();
                return false;
            }
            

            // 搜索并移除被拖拽节点
            let searchArr = dragNode.parent.children
            for (let i in searchArr) {
                if (searchArr[i] === dragNode) {
                    searchArr.splice(i, 1);
                    break;
                }
            }

            dragNode.parent.updateLeafState();

            if (state == OverState.PREV) {
                dropNode.insertPrev(dragNode);
            } else if (state == OverState.INNER) {
                dropNode.insertInner(dragNode);
            } else if (state == OverState.NEXT) {
                dropNode.insertNext(dragNode);
            }

            this.$emit('drag-end', this.dragState.draggingNode.node, this.dragState.overNode.node, OverStateName[this.dragState.state], event)
        }
    },
    created() {
        const _this = this;
        this.rootNode = new Node({
            level: -1,
            __config__: {
                props: _this.props,
                nodeKey: _this.nodeKey,
                defaultExpandAll: this.defaultExpandAll,
                lazy: this.lazy
            }
        });
    },
    mounted() {
        this.rootNode.setChildrenData(this.data);
    }
}
</script>