<!--
Program: Tree
Author: SDL
Description: Tree Common Component
-->
<template>
  <div :class="classes" role="tree">
    <ul :class="containerClasses" role="group" style="padding-bottom: 6px;">
      <tree-item
        v-for="(child, index) in data"
        ref="children"
        :key="index"
        :data="child"
        :text-field-name="textFieldName"
        :before-item-click="beforeItemClick"
        :value-field-name="valueFieldName"
        :children-field-name="childrenFieldName"
        :item-events="itemEvents"
        :whole-row="wholeRow"
        :show-checkbox="showCheckbox"
        :allow-transition="allowTransition"
        :height="sizeHeight"
        :parent-item="data"
        :draggable="draggable"
        :drag-over-background-color="dragOverBackgroundColor"
        :on-item-click="onItemClick"
        :on-item-toggle="onItemToggle"
        :on-item-drag-start="onItemDragStart"
        :on-item-drag-end="onItemDragEnd"
        :on-item-drop="onItemDrop"
        :klass="index === data.length - 1 ? 'tree-last' : ''"
      >
        <template v-slot="_">
          <slot :vm="_.vm" :model="_.model">
            <i :class="_.vm.themeIconClasses" role="presentation" v-if="!_.model.loading"></i>
            <span v-text="_.model[textFieldName]"></span>
          </slot>
        </template>
      </tree-item>
    </ul>
  </div>
</template>
<script>
import TreeItem from './TreeItem.vue';

let ITEM_ID = 0;
const ITEM_HEIGHT_SMALL = 18;
const ITEM_HEIGHT_DEFAULT = 24;
const ITEM_HEIGHT_LARGE = 32;

export default {
  name: 'Tree',
  props: {
    data: { type: Array },
    size: { type: String, validator: value => ['large', 'small'].indexOf(value) > -1 },
    showCheckbox: { type: Boolean, default: false },
    wholeRow: { type: Boolean, default: false },
    noDots: { type: Boolean, default: false },
    collapse: { type: Boolean, default: false },
    multiple: { type: Boolean, default: false },
    allowBatch: { type: Boolean, default: false },
    allowTransition: { type: Boolean, default: true },
    textFieldName: { type: String, default: 'text' },
    valueFieldName: { type: String, default: 'value' },
    childrenFieldName: { type: String, default: 'children' },
    itemEvents: {
      type: Object,
      default() {
        return {};
      },
    },
    async: { type: Function },
    beforeItemClick: { type: Function },
    loadingText: { type: String, default: 'Loading...' },
    draggable: { type: Boolean, default: false },
    dragOverBackgroundColor: { type: String, default: '#C9FDC9' },
    klass: String,
  },
  data() {
    return {
      draggedItem: undefined,
      draggedElm: undefined,
    };
  },
  computed: {
    classes() {
      return [{ tree: true }, { 'tree-default': !this.size }, { [`tree-default-${this.size}`]: !!this.size }, { 'tree-checkbox-selection': !!this.showCheckbox }, { [this.klass]: !!this.klass }];
    },
    containerClasses() {
      return [{ 'tree-container-ul': true }, { 'tree-children': true }, { 'tree-wholerow-ul': !!this.wholeRow }, { 'tree-no-dots': !!this.noDots }];
    },
    sizeHeight() {
      switch (this.size) {
        case 'large':
          return ITEM_HEIGHT_LARGE;
        case 'small':
          return ITEM_HEIGHT_SMALL;
        default:
          return ITEM_HEIGHT_DEFAULT;
      }
    },
  },
  methods: {
    initializeData(items) {
      if (items && items.length > 0) {
        for (let i of Object.keys(items)) {
          items[i] = this.initializeDataItem(items[i]);
          this.initializeData(items[i][this.childrenFieldName]);
        }
      }
    },
    initializeDataItem(item) {
      function Model(itemObj, textFieldName, valueFieldName, childrenFieldName, collapse) {
        ITEM_ID += 1;
        this.id = itemObj.id || ITEM_ID;
        this[textFieldName] = itemObj[textFieldName] || '';
        this[valueFieldName] = itemObj[valueFieldName] || itemObj[textFieldName];
        this.icon = itemObj.icon || '';
        this.opened = itemObj.opened || collapse;
        this.selected = itemObj.selected || false;
        this.disabled = itemObj.disabled || false;
        this.loading = itemObj.loading || false;
        this.isFolder = itemObj.isFolder || false;
        this[childrenFieldName] = itemObj[childrenFieldName] || [];
      }

      const node = Object.assign(new Model(item, this.textFieldName, this.valueFieldName, this.childrenFieldName, this.collapse), item);
      const self = this;
      node.addBefore = (data, selectedNode) => {
        const newItem = self.initializeDataItem(data);
        const index = selectedNode.parentItem.findIndex(t => t.id === node.id);
        selectedNode.parentItem.splice(index, 0, newItem);
      };
      node.addAfter = (data, selectedNode) => {
        const newItem = self.initializeDataItem(data);
        const index = selectedNode.parentItem.findIndex(t => t.id === node.id) + 1;
        selectedNode.parentItem.splice(index, 0, newItem);
      };
      node.addChild = data => {
        const newItem = self.initializeDataItem(data);
        node.opened = true;
        node[self.childrenFieldName].push(newItem);
      };
      node.openChildren = () => {
        node.opened = true;
        self.handleRecursionNodeChildren(node, nodeObj => {
          nodeObj.opened = true;
        });
      };
      node.closeChildren = () => {
        node.opened = false;
        self.handleRecursionNodeChildren(node, nodeObj => {
          nodeObj.opened = false;
        });
      };
      return node;
    },
    initializeLoading() {
      const item = {};
      item[this.textFieldName] = this.loadingText;
      item.disabled = true;
      item.loading = true;
      return this.initializeDataItem(item);
    },
    handleRecursionNodeChilds(node, func) {
      if (func(node) !== false) {
        if (node.$refs.children && node.$refs.children.length > 0) {
          Object.keys(node.$refs.children).forEach(i => {
            if (!node.$refs.children[i].disabled) {
              this.handleRecursionNodeChilds(node.$refs.children[i], func);
            }
          });
        }
      }
    },
    handleRecursionNodeChildren(node, func) {
      if (func(node) !== false) {
        if (node[this.childrenFieldName] && node[this.childrenFieldName].length > 0) {
          const nodeList = node[this.childrenFieldName];
          Object.keys(nodeList).forEach(i => {
            this.handleRecursionNodeChildren(nodeList[i], func);
          });
        }
      }
    },
    onItemClick(oriNode, oriItem, e) {
      if (this.multiple) {
        if (this.allowBatch) {
          this.handleBatchSelectItems(oriNode, oriItem);
        }
      } else {
        this.handleSingleSelectItems(oriNode, oriItem);
      }
      this.$emit('item-click', oriNode, oriItem, e);
    },
    handleSingleSelectItems(oriNode) {
      this.handleRecursionNodeChilds(this, node => {
        if (node.model) node.model.selected = false;
      });
      oriNode.model.selected = true;
    },
    handleBatchSelectItems(oriNode) {
      this.handleRecursionNodeChilds(oriNode, node => {
        if (node.model.disabled) return;
        node.model.selected = oriNode.model.selected;
      });
    },
    onItemToggle(oriNode, oriItem, e) {
      if (oriNode.model.opened) {
        this.handleAsyncLoad(oriNode.model[this.childrenFieldName], oriNode, oriItem);
      }
      this.$emit('item-toggle', oriNode, oriItem, e);
    },
    handleAsyncLoad(oriParent, oriNode) {
      const self = this;
      if (this.async) {
        if (oriParent[0].loading) {
          this.async(oriNode, data => {
            if (data.length > 0) {
              Object.keys(data).forEach(i => {
                if (!data[i].isLeaf) {
                  if (typeof data[i][self.childrenFieldName] !== 'object') {
                    data[i][self.childrenFieldName] = [self.initializeLoading()];
                  }
                }
                const dataItem = self.initializeDataItem(data[i]);
                oriParent.splice(i, 1, dataItem);
              });
            } else {
              oriNode.model[self.childrenFieldName] = [];
            }
          });
        }
      }
    },
    onItemDragStart(e, oriNode, oriItem) {
      if (!this.draggable || oriItem.dragDisabled) return false;
      e.dataTransfer.effectAllowed = 'move';
      const agent = window.navigator.userAgent.toLowerCase();
      if (!(window.navigator.appName === 'Netscape' && agent.indexOf('trident') !== -1) || agent.indexOf('msie') !== -1) {
        e.dataTransfer.setData('text', null);
      }

      this.draggedElm = e.target;
      this.draggedItem = {
        item: oriItem,
        parentItem: oriNode.parentItem,
        index: oriNode.parentItem.findIndex(t => t.id === oriItem.id),
      };
      this.$emit('item-drag-start', oriNode, oriItem, e);
      return true;
    },
    onItemDragEnd(e, oriNode, oriItem) {
      this.draggedItem = undefined;
      this.draggedElm = undefined;
      this.$emit('item-drag-end', oriNode, oriItem, e);
    },
    onItemDrop(e, oriNode, oriItem, parentItem, parent) {
      if (!this.draggable || !!oriItem.dropDisabled) return false;
      this.$emit('item-drop-before', oriNode, oriItem, !this.draggedItem ? undefined : this.draggedItem.item, e);
      if (!this.draggedElm || this.draggedElm === e.target || this.draggedElm.contains(e.target)) {
        return true;
      }
      if (this.draggedItem) {
        if (
          this.draggedItem.parentItem === oriItem[this.childrenFieldName] ||
          this.draggedItem.item === oriItem ||
          (oriItem[this.childrenFieldName] && oriItem[this.childrenFieldName].findIndex(t => t.id === this.draggedItem.item.id) !== -1)
        ) {
          return true;
        }
        let delFlag = false;
        if (oriItem[this.childrenFieldName]) {
          // console.log('e.target:', e.target.nodeName);
          if (e.target.nodeName === 'SPAN') {
            oriItem[this.childrenFieldName].push(this.draggedItem.item);
          } else {
            // console.log('11:', this.draggedItem);
            if (parentItem === this.draggedItem.parentItem) {
              parentItem.splice(
                parentItem.findIndex(t => t.id === this.draggedItem.item.id),
                1,
              );
              delFlag = true;
            }
            // console.log('parentItem:', e.target.name);
            // if (parentItem.findIndex(t => t.id === oriItem.id) + 1 === parentItem.length) {
            //   parentItem.push(this.draggedItem.item);
            // } else {
            parentItem.splice(parentItem.findIndex(t => t.id === oriItem.id) + 1, 0, this.draggedItem.item);
            // }
          }
        } else {
          oriItem[this.childrenFieldName] = [this.draggedItem.item];
        }
        if (e.target.nodeName === 'SPAN') {
          oriItem.opened = true;
        }
        const { draggedItem } = this;
        this.$nextTick(() => {
          if (!delFlag) {
            draggedItem.parentItem.splice(draggedItem.index, 1);
          }
        });
        // console.log('111:', oriNode);
        // console.log('111:', oriItem, parentItem);
        // console.log('111:', parent);
        if (e.target.nodeName === 'SPAN') {
          this.$emit('item-drop', oriNode, oriItem, this.draggedItem.item, e);
        } else {
          this.$emit('item-drop', oriNode, parent, this.draggedItem.item, e);
        }
      }
      return true;
    },
  },
  created() {
    this.initializeData(this.data);
  },
  mounted() {
    if (this.async) {
      this.data.splice(0, 1, this.initializeLoading());
      this.handleAsyncLoad(this.data, this);
    }
  },
  components: {
    TreeItem,
  },
};
</script>
<style lang="less">
@import './less/style';
</style>
