<template>
    <transition name="slide-up">
        <ul :class="classes" v-show="visible">
            <li>
                <div :class="listItem" @dragover.self="dragoverdiv" @drop.prevent.self="drop(data,position)" @dragleave="dragleave" @click.prevent.stop="handleSelect" >
                    <span :class="arrowClasses" @click.stop="handleExpand">
                        <!--<Icon type="arrow-right-b"></Icon>-->
                        <Icon type="ios-play"></Icon>
                    </span>
                    <Checkbox v-if="isShowCheckBox" :value="data.checked" :indeterminate="indeterminate" :disabled="data.disabled || data.disableCheckbox" @click.native.prevent.stop="handleCheck"></Checkbox>
                    <div class="node-inline">
                        <img v-if="data.icon && data.icon.indexOf('/') !== -1" :src="data.icon" width="18px" height="18px" @click.prevent.self="handleSelect" />
                    </div>
                    <div class="node-inline">
                        <Icon :type="data.icon" v-if="data.icon && data.icon.indexOf('/') === -1" width="18px" height="18px" @click.prevent.self="handleSelect" />
                    </div>
                    <div class="node-inline">
                        <!--<input type="text" v-model="data.title"  v-html="data.title" v-show="!!data.edit" />-->
                        <span :class="titleClasses"  :contenteditable="data.edit" v-html="data.title" @blur="editedAfter" :draggable="draggable" @dragstart="dragstart(data)" @dragover="dragover" @drop.prevent.self="drop(data,'inside')" @dragleave="dragleave"></span>
                        <!--:contenteditable="contentEditable"  v-show="!!!data.edit"-->
                    </div>
                    <div class="node-inline">
                        <Icon type="arrow-left-c" v-if="insert" :class="positionClasses"></Icon>
                    </div>
                </div>
                <template v-for="(item, index) in data.children">
                    <Tree-node :contentEditable="contentEditable" :onTitleChange="onTitleChange" :key="index" :data="item" :visible="data.expand" :multiple="multiple" :show-checkbox="showCheckbox" :draggable="draggable" :cascade="cascade" :withCheckAll="withCheckAll" @customDragstart="bubbleItem" @customDrop="bubbleDropItem" :lazy="lazy" :load="load" :handleNewNodeLoaded="handleNewNodeLoaded" :setParentLazyLoading="setParentLazyLoading">
                    </Tree-node>
                </template>
            </li>
        </ul>
    </transition>
</template>
<script>
import Checkbox from '../checkbox/checkbox.vue';
import Icon from '../icon/icon.vue';
import Emitter from '../../mixins/emitter';
import { findComponentsDownward } from '../../utils/assist';

const prefixCls = 'ivu-tree';

export default {
    name: 'TreeNode',
    mixins: [Emitter],
    components: { Checkbox, Icon },
    props: {
        data: {
            type: Object,
            default() {
                return {};
            }
        },
        multiple: {
            type: Boolean,
            default: false
        },
        showCheckbox: {
            type: [Boolean, Object],
            default: false
        },
        visible: {
            type: Boolean,
            default: false
        },
        cascade: {
            type: Boolean,
            default: false
        },
        draggable: {
            type: Boolean,
            default: false
        },
        withCheckAll: {
            type: Boolean,
            default: true
        },
        contentEditable: {
            type: Boolean,
            default: false
        },
        onTitleChange: {
            type: Function,
            default: () => { }
        },
        editedEnd:{
            type:Function
        },
        // 是否懒加载
        lazy: {
            type: Boolean,
            default: false
        },
        // 懒加载节点获取函数
        load: {
            type: Function,
            default: (node, resolve) => {
                // return (node, resolve) => {
                    return resolve([]);
                // };
            }
        },
        // 父组件中的 foreachMakeDataInfo 方法，处理懒加载新增的子节点
        handleNewNodeLoaded: {
            type: Function,
        },
        // 设置父节点
        setParentLazyLoading: {
            type: Function,
        },
    },
    data() {
        return {
            prefixCls: prefixCls,
            indeterminate: false,
            insert: false,
            pre: false,
            next: false,
            position: 'inside'
        };
    },
    computed: {
        classes() {
            return [
                `${prefixCls}-children`
            ];
        },
        selectedCls() {
            return [
                {
                    [`${prefixCls}-node-selected`]: this.data.selected
                }
            ];
        },
        arrowClasses() {
            return [
                `${prefixCls}-arrow`,
                {
                    //[`${prefixCls}-arrow-disabled`]: this.data.disabled,
                    [`${prefixCls}-arrow-open`]: this.data.expand,
                    [`${prefixCls}-arrow-hidden`]: !this.data.hasChild,
                    [`${prefixCls}-arrow-loading`]: this.data.loading,
                }
            ];
        },
        titleClasses() {
            return [
                `${prefixCls}-title`,
                {
                    [`${prefixCls}-title-selected`]: this.data.selected,
                    [`${prefixCls}-title-target`]: this.data.target
                }
            ];
        },
        listItem() {
            return `${prefixCls}-listItem`
        },
        positionClasses() {
            return [
                "position-arrow",
                {
                    "insert-arrow": this.insert,
                    "pre-arrow": this.pre,
                    "next-arrow": this.next
                }
            ]
        },
        isShowCheckBox () {
            if (typeof this.showCheckbox === 'object'){
                if (!this.showCheckbox.root && !this.showCheckbox.leaf) {
                    return false;
                }
                else if (!this.showCheckbox.root && this.data.children && this.data.children.length > 0) {
                    return false;
                }
                else if (!this.showCheckbox.leaf && (!this.data.children || this.data.children.length === 0)) {
                    return false;
                }
                else {
                    return true;
                }
            }
            else if (typeof this.showCheckbox === 'boolean' && this.showCheckbox) {
                return true;
            }
            else {
                return false;
            }
        }
    },
    methods: {
        editedAfter(event) {
            const span = event.target
            const title = span.innerText
            const old = Object.assign({}, this.data)
            const newData = Object.assign({}, old, { title })
            this.data.title = title
            this.onTitleChange(old, newData)
            this.$emit("editedEnd", span)
        },
        handleExpand() {
            // 懒加载并且节点未下载子节点，则下载节点的孩子节点
            if (this.lazy && !this.data.expand && (!this.data.children || this.data.children.length === 0) && !this.data.loaded) {
                this.loadNewNode();
            }

            //if (this.data.disabled) return;
            this.$set(this.data, 'expand', !this.data.expand);
            this.dispatch('Tree', 'toggle-expand', this.data);
        },
        handleSelect() {
            // 如果选项被禁止响应或checkbox与select同步时且设置了checkbox禁用时，直接返回无响应
            if (this.data.disabled || (this.cascade && this.data.disableCheckbox)) return;
            if (typeof this.data.selected === 'undefined') {
                this.$set(this.data, 'selected', false)
            }
            let selected = !this.data.selected;
            // 有checkbox时，点击item与点击checkbox行为相同
            // 单选且该项没有被选中
            if (!this.multiple && selected) {
                this.dispatch('Tree', 'oneSelected', this.data);
            } else if (this.showCheckbox) {
                let flag = false
                if (selected || this.indeterminate) { flag = true }
                if (this.withCheckAll) {
                    findComponentsDownward(this, 'TreeNode').forEach(node => {
                        if (this.cascade) {
                            node.data.checked = flag;
                        }
                        if (typeof node.data.selected === 'undefined') {
                            this.$set(node.data, 'selected', flag)
                        }
                        node.data.selected = flag
                    });
                }
                selected = flag
                if (this.cascade) {
                    this.data.checked = flag;
                }
            } else {
                if (this.withCheckAll) {
                    findComponentsDownward(this, 'TreeNode').forEach(node => {
                        if (typeof node.data.selected === 'undefined') {
                            this.$set(node.data, 'selected', selected)
                        }
                        node.data.selected = selected
                    });
                }
            }
            this.data.selected = selected;
            if (this.cascade) {
                this.dispatch('Tree', 'checked');
                this.dispatch('Tree', 'on-checked');
            }
            this.dispatch('Tree', 'selected');
            this.dispatch('Tree', 'on-selected');
        },
        handleCheck() {
            if (this.disabled) return;
            if (typeof this.data.selected === 'undefined') {
                this.$set(this.data, 'selected', false)
            }
            let checked = !this.data.checked;
            if (!this.multiple && checked) {
                // 单选且该项没有被选中，设为选中
                this.dispatch('Tree', 'oneChecked', this.data);
            } else {
                // 多选，父元素未被选中时，点击父元素变全选
                let flag = false
                if (checked || this.indeterminate) { flag = true }
                if (this.withCheckAll) {
                    findComponentsDownward(this, 'TreeNode').forEach(node => {
                        node.data.checked = flag;
                        if (this.cascade) {
                            if (typeof node.data.selected === 'undefined') {
                                this.$set(node.data, 'selected', flag)
                            }
                            node.data.selected = flag
                        }
                    });
                }
                checked = flag
                if (this.cascade) {
                    this.data.selected = flag;
                }
            }
            this.data.checked = checked;
            this.dispatch('Tree', 'checked');
            this.dispatch('Tree', 'on-checked');
            if (this.cascade) {
                this.dispatch('Tree', 'on-selected');
            }
        },
        setIndeterminate() {
            this.indeterminate = this.data.checked ? false : findComponentsDownward(this, 'TreeNode').some(node => node.data.checked);
        },
        dragstart(data) {
            if(this.draggable) {
                this.$emit("customDragstart", data)
            }
            // event.dataTransfer.setData("item", JSON.stringify(data));
        },
        dragover() {
            if(this.draggable) {
                this.insert = true;
                event.preventDefault();
            }
        },
        dragoverdiv() {
            if(this.draggable) {
                event.preventDefault();
                let mousey = event.clientY
                let eleY = event.target.getBoundingClientRect().top
                let eleHeight = event.target.clientHeight
                if ((mousey - eleY) * 3 < eleHeight) {
                    event.target.style.border = "none"
                    event.target.style.borderTop = "1px solid #4d4d4d"
                    this.position = "pre"
                } else if ((mousey - eleY) * 2 > eleHeight) {
                    event.target.style.border = "none"
                    event.target.style.borderBottom = "1px solid #4d4d4d"
                    this.position = "next"
                }
            }
        },
        drop(data, position) {
            if(this.draggable) {
                this.insert = false;
                event.target.style.border = "none"
                this.$emit("customDrop", data, position)
            }
        },
        // 逐层向上抛
        bubbleItem(item) {
            if(this.draggable) {
                this.$emit("customDragstart", item)
            }
        },
        bubbleDropItem(item, position) {
            if(this.draggable) {
                this.$emit("customDrop", item, position)
            }
        },
        dragleave() {
            if(this.draggable) {
                this.insert = false
                event.target.style.border = "none"
            }
        },
        // 下载子节点
        loadNewNode() {
            // 是否显示加载中的效果
            this.$set(this.data, 'loading', true);

            const nodeLoadPromise = new Promise((resolve, reject) => {
                this.load(this.data, resolve);
            });

            nodeLoadPromise
            .then((res) => {
                this.data.loaded = true;
                this.$set(this.data, 'loading', false);
                if (!res || res.length === 0) {
                    this.$set(this.data, 'hasChild', false);
                } else {
                    this.handleNewNodeLoaded(res)
                    this.$set(this.data, 'children', res)
                    this.setParentLazyLoading();
                }
            })
            .catch((err) => {
                this.$set(this.data, 'loading', false);
                console.log(new Error(err));
            });
        },
    },
    created() {
        // created node.vue first, mounted tree.vue second
        if (!this.data.checked) this.$set(this.data, 'checked', false);
        // 是否懒加载，懒加载根据 isLeaf 判断是否为叶子节点
        if (this.lazy) {
            this.$set(this.data, 'hasChild', this.data.isLeaf ? false : true);
        }
    },
    mounted() {
        this.$on('indeterminate', () => {
            this.broadcast('TreeNode', 'indeterminate');
            this.setIndeterminate();
        });
    },
    watch: {
        data: {
            handler(val, oldVal) {
            },
            deep: true
        }
    }
};
</script>
<style>
.position-arrow {
    position: relative;
}

.pre-arrow {
    top: -20px;
}

.next-arrow {
    top: 20px;
}

.node-inline {
    display: inline-block;
}
</style>