<template>
  <collapse-transition>
    <ul :class="classes">
      <li>
        <div :class="headClasses">
          <span :class="arrowClasses" @click="handleExpand">
            <Icon v-if="showArrow" type="md-arrow-dropright" />
            <Icon
              v-if="showLoading"
              type="ios-loading"
              class="ivu-load-loop"
            ></Icon>
          </span>
          <Checkbox
            v-if="showCheckbox"
            :value="data.checked"
            :indeterminate="data.indeterminate"
            :disabled="data.disabled || data.disableCheckbox"
            @click.native.prevent="handleCheck"
          ></Checkbox>
          <Render
            v-if="data.render"
            :render="data.render"
            :data="data"
            :node="node"
          ></Render>
          <Render
            v-else-if="isParentRender"
            :render="parentRender"
            :data="data"
            :node="node"
          ></Render>
          <span v-else :class="titleClasses" @click="handleSelect">{{
            data.title
          }}</span>
          <div :class="editClasses" v-if="editable">
            <Button size="small" icon="md-add" @click="nodeAppend"></Button>
            <Button size="small" icon="md-create" @click="nodeEdit"></Button>
            <Button size="small" icon="ios-trash" @click="nodeRemove"></Button>
          </div>
        </div>
        <template v-if="data.expand">
          <Tree-node
            v-for="(item, i) in children"
            :key="i"
            :data="item"
            :multiple="multiple"
            :show-checkbox="showCheckbox"
            :editable="editable"
            :children-key="childrenKey"
          >
          </Tree-node>
        </template>
      </li>
    </ul>
  </collapse-transition>
</template>
<script>
import Render from "./render";
import Emitter from "./emitter";
import CollapseTransition from "./collapse-transition";

const prefixCls = "ivu-tree";

// Find components upward
function findComponentUpward(context, componentName, componentNames) {
  if (typeof componentName === "string") {
    componentNames = [componentName];
  } else {
    componentNames = componentName;
  }

  let parent = context.$parent;
  let name = parent.$options.name;
  while (parent && (!name || componentNames.indexOf(name) < 0)) {
    parent = parent.$parent;
    if (parent) name = parent.$options.name;
  }
  return parent;
}

export default {
  name: "TreeNode",
  mixins: [Emitter],
  components: { CollapseTransition, Render },
  props: {
    data: {
      type: Object,
      default() {
        return {};
      }
    },
    multiple: {
      type: Boolean,
      default: false
    },
    childrenKey: {
      type: String,
      default: "children"
    },
    showCheckbox: {
      type: Boolean,
      default: false
    },
    editable: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      prefixCls: prefixCls
    };
  },
  computed: {
    classes() {
      return [`${prefixCls}-children`];
    },
    selectedCls() {
      return [
        {
          [`${prefixCls}-node-selected`]: this.data.selected
        }
      ];
    },
    headClasses() {
      return [
        `${prefixCls}-li-head`,
        {
          [`${this.prefixCls}-li-head-edit`]: this.editable
        }
      ];
    },
    arrowClasses() {
      return [
        `${prefixCls}-arrow`,
        {
          [`${prefixCls}-arrow-disabled`]: this.data.disabled,
          [`${prefixCls}-arrow-open`]: this.data.expand
        }
      ];
    },
    titleClasses() {
      return [
        `${prefixCls}-title`,
        {
          [`${prefixCls}-title-selected`]: this.data.selected
        }
      ];
    },
    editClasses() {
      return [`${prefixCls}-edit-btns`];
    },
    showArrow() {
      return (
        (this.data[this.childrenKey] && this.data[this.childrenKey].length) ||
        ("loading" in this.data && !this.data.loading)
      );
    },
    showLoading() {
      return "loading" in this.data && this.data.loading;
    },
    isParentRender() {
      const Tree = findComponentUpward(this, "TreeList");
      return Tree && Tree.render;
    },
    parentRender() {
      const Tree = findComponentUpward(this, "TreeList");
      if (Tree && Tree.render) {
        return Tree.render;
      } else {
        return null;
      }
    },
    node() {
      const Tree = findComponentUpward(this, "TreeList");
      if (Tree) {
        // 将所有的 node（即flatState）和当前 node 都传递
        return [
          Tree.flatState,
          Tree.flatState.find(item => item.nodeKey === this.data.nodeKey)
        ];
      } else {
        return [];
      }
    },
    children() {
      return this.data[this.childrenKey];
    }
  },
  methods: {
    handleExpand() {
      const item = this.data;
      if (item.disabled) return;

      // async loading
      if (item[this.childrenKey].length === 0) {
        const tree = findComponentUpward(this, "TreeList");
        if (tree && tree.loadData) {
          this.$set(this.data, "loading", true);
          tree.loadData(item, children => {
            this.$set(this.data, "loading", false);
            if (children.length) {
              this.$set(this.data, this.childrenKey, children);
              this.$nextTick(() => this.handleExpand());
            }
          });
          return;
        }
      }

      if (item[this.childrenKey] && item[this.childrenKey].length) {
        this.$set(this.data, "expand", !this.data.expand);
        this.dispatch("TreeList", "toggle-expand", this.data);
      }
    },
    handleSelect() {
      if (this.data.disabled) return;
      this.dispatch("TreeList", "on-selected", this.data.nodeKey);
    },
    handleCheck() {
      if (this.data.disabled) return;
      const changes = {
        checked: !this.data.checked && !this.data.indeterminate,
        nodeKey: this.data.nodeKey
      };
      this.dispatch("TreeList", "on-check", changes);
    },
    nodeAppend() {
      this.dispatch("TreeList", "node-append", this.data.nodeKey);
    },
    nodeEdit() {
      this.dispatch("TreeList", "node-edit", this.data.nodeKey);
    },
    nodeRemove() {
      this.dispatch("TreeList", "node-remove", this.data.nodeKey);
      // todo
    }
  }
};
</script>

<style>
.ivu-tree-edit-btns {
  float: right;
  margin-right: -100px;
}
.ivu-tree-edit-btns > button {
  margin-left: 5px;
}
.ivu-tree-li-head {
  height: 26px;
  line-height: 26px;
}
.ivu-tree-li-head-edit:hover {
  background: #f0faff;
}
.ivu-tree-li-head-edit {
  margin-right: 100px;
}
</style>
