<template>
<div id="tree-container" :style="`height:${HEIGHT}px`">
  <div id="node-container" :style="`height:${HEIGHT}px; width:${WIDTH}px`">
    <template v-for="(col, index) in treeNodeColList" :key="index">
      <TreeNode
        v-for="item in col"
        v-bind="item"
        :processor="item.processorName"
        :priority="item.priority"
        :key="item.id"
        :position-x="index"
        :node-style="NODE_STYLE"
        :depth="item.deep"
        @addChild="addChild"
        @deleteNode="deleteNode"
        @nodeClick="nodeClick"
        :ref="`treenode-${item.id}`"
      />
    </template>
<!--    <TreeNode-->
<!--      v-for="requirement in data"-->
<!--      :key="requirement.id"-->
<!--      v-bind="requirement"-->
<!--      :ref="`treenode-${requirement.id}`"-->
<!--      @addChild="addChild"-->
<!--      @deleteNode="deleteNode"-->
<!--      @nodeClick="nodeClick"-->
<!--    />-->
  </div>
  <canvas id="line-drawer" :height="fitHeight" :width="WIDTH"></canvas>
</div>
</template>

<script>
import TreeNode from './TreeNode.vue';

// 容器宽高
const HEIGHT = 700;
const WIDTH = 1000;
const TREE_MAX_HEIGHT = 4;
const NODE_STYLE = {
  width: 250,
  height: 140,
  padding: 25,
};

export default {
  name: 'Tree',
  components: { TreeNode },
  props: {
    data: Array,
  },
  computed: {
    /**
     * @returns {*[][]}
     */
    treeNodeColList() {
      // 将点存入map便于调用
      const nodeMap = new Map();
      this.data.forEach((item) => {
        nodeMap.set(item.id, item);
      });
      // 将array转为树状结构(父组件中已处理无需再处理)；顺便拆成col
      let rootNode;
      const colList = [];
      for (let i = 0; i < TREE_MAX_HEIGHT; i += 1) {
        colList.push([]);
      }
      if (this.data.length === 0) {
        return colList;
      }
      this.data.forEach((item) => {
        if (item.deep > 0 && item.deep <= TREE_MAX_HEIGHT) {
          colList[item.deep - 1].push(item);
        } else {
          console.warn('需求树数据有误，树高度错误');
        }
        if (!item.parentId) rootNode = item;
      });
      // 计算一个节点包含他的后代所需要的空间
      const calculateNodeContentHeight = (node) => {
        if (!node.childs) {
          node.contentHeight = 1;
          return 1;
        }
        node.contentHeight = node.childs.reduce(
          (total, current) => total + calculateNodeContentHeight(current),
          0,
        );
        return node.contentHeight;
      };
      calculateNodeContentHeight(rootNode);
      // 计算节点基准Y轴位置
      rootNode.positionY = 0;
      for (let i = 1; i < TREE_MAX_HEIGHT; i += 1) {
        let sum = 0;
        let tempParent = colList[0][0].id;
        colList[i].forEach((item) => {
          if (item.parentId !== tempParent) {
            sum = 0;
            tempParent = item.parentId;
          }
          item.positionY = nodeMap.get(item.parentId).positionY + sum;
          sum += item.contentHeight;
        });
      }
      // 计算节点Y轴偏移位置
      for (let i = TREE_MAX_HEIGHT - 2; i >= 0; i -= 1) {
        colList[i].forEach((item) => {
          if (item.childs) {
            const { length } = item.childs;
            item.positionY = item.childs.reduce((total, current) => total + current.positionY / length, 0);
          }
        });
      }
      // 计算所需容器高度
      let maxPositionY = 0;
      this.data.forEach((item) => {
        if (item.positionY && item.positionY > maxPositionY) maxPositionY = item.positionY;
      });
      this.drawLine((maxPositionY * 140 + 100).toFixed(0));
      // 返回col
      console.warn(colList);
      return colList;
    },
  },
  data() {
    return {
      HEIGHT,
      WIDTH,
      TREE_MAX_HEIGHT,
      NODE_STYLE,
      fitHeight: 0,
      ctx: null,
    };
  },
  mounted() {
    this.initCanvasCtx();
    // this.regenerateTree();
  },
  methods: {
    initCanvasCtx() {
      this.ctx = document.getElementById('line-drawer').getContext('2d');
    },
    drawLine(height) {
      this.fitHeight = height > HEIGHT ? height : HEIGHT;
      setTimeout(() => {
        this.ctx.clearRect(0, 0, WIDTH, height > HEIGHT ? height : HEIGHT);
        this.ctx.lineWidth = 1;
        this.ctx.strokeStyle = '#bbbbbb';
        this.data.forEach((item) => {
          if (!item.childs) return;
          const parentCard = this.$refs[`treenode-${item.id}`].getTreeNodeSizeAndPosition();
          const start = {
            x: parentCard.left + parentCard.width,
            y: parentCard.top + parentCard.height / 2,
          };
          item.childs.forEach((child) => {
            const childCard = this.$refs[`treenode-${child.id}`].getTreeNodeSizeAndPosition();
            const end = {
              x: childCard.left,
              y: childCard.top + childCard.height / 2,
            };
            this.ctx.beginPath();
            this.ctx.moveTo(start.x, start.y);
            this.ctx.lineTo(end.x, end.y);
            this.ctx.stroke();
          });
        });
      });
    },
    addChild(id) {
      this.$emit('change', {
        type: 'addChildNode',
        id,
      });
    },
    deleteNode(id) {
      this.$confirm('是否确认删除该节点?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      }).then(() => {
        this.$emit('change', {
          type: 'deleteTreeNode',
          id,
        });
      }).catch(() => {
        this.$message({
          type: 'info',
          message: '已取消删除',
        });
      });
    },
    nodeClick(id) {
      this.$emit('node-click', id);
    },
  },
};
</script>

<style lang="scss">
#tree-container {
  border-radius: 4px;
  box-shadow: 0 0 3px 0px #BDC3C7;
  position: relative;
  display: flex;
  overflow-y: auto;
  justify-content:center;
  background: url("https://devcloud.cn-north-4.huaweicloud.com/projectman/plan_back.05189e7a664e6edc2f8e.png") repeat;
}
#node-container {
  position:absolute;
  z-index: 11;
}
#line-drawer {
  position: absolute;
}
.jsmind-inner {
  position:relative;
  overflow:auto;
  width:100%;
  height:100%;
  user-select:none;
}

canvas {
  position:absolute;
  z-index:1;
}

jmnodes {
  position:absolute;
  z-index:2;
  background-color:rgba(0,0,0,0);
}
jmnode {
  position:absolute;
  cursor:default;
  max-width:400px;
  white-space:nowrap;
  overflow:hidden;
  text-overflow:ellipsis;

  padding:10px;
  background-color:#fff;
  color:#333;
  border-radius:1px;
  border: 1px solid #333;
  font:16px/1.125 Verdana,Arial,Helvetica,sans-serif;

  &:hover {
    box-shadow:2px 2px 4px #333;
    background-color:#ebebeb;
    color:#333;
  }

  &.selected {
    background-color:#11f;
    color:#fff;
    box-shadow:2px 2px 8px #000;
  }

  &.root {
    font-size:24px;
  }
}
jmexpander {
  position:absolute;
  width:11px;
  height:11px;
  display:block;
  overflow:hidden;
  line-height:12px;
  font-size:12px;
  text-align:center;
  border-radius:6px;
  border-width:1px;
  border-style:solid;
  cursor:pointer;

  border-color:gray;

  &:hover {
    border-color:#000;
  }
}

@media screen and (max-device-width: 1024px) {
    jmnode{padding:5px;border-radius:3px;font-size:14px;}
    jmnode.root{font-size:21px;}
}
</style>
