<template>
  <!-- 注意样式的添加的对象，在style里面添加，是给dialog-wrapper添加样式。而直接给dialog添加样式直接写，比如添加width -->
  <el-dialog @close="close"
             @opened="dialogInit"
             v-dialogDrag
             v-if="routeViewDialogVisible"
             style="font-size: 10px;"
             width="1300px"
             :title= "$t('ElineRouteView.routeView')"
             :visible.sync="routeViewDialogVisible">

    <el-row :gutter="10">
      <el-col :span="10" style="padding-top: 3px ">
        <div><label style="font-size: 10px; float: left">{{$t('ElineRouteView.plannedBandWidth')}}{{bandWidth}} Mbit/s</label></div>
      </el-col>
      <el-col :span="14" style="padding-top: 3px ">
        <el-col :span="12" >
          <el-col :span="4" style="float: left"><img :src="linkStateUrl[0]"></el-col>
          <el-col :span="8" style="float: left"><label
            style="font-size: 10px; float: left; margin-top: 2px">{{$t('ElineRouteView.workPath')}}</label></el-col>
          <el-col :span="4" style="float: left"><img :src="linkStateUrl[1]"></el-col>
          <el-col :span="8" style="float: left"><label
            style="font-size: 10px; float: left; margin-top: 2px">{{$t('ElineRouteView.protectedPath')}}</label></el-col>
        </el-col>
        <el-col :span="12" >
          <el-col :span="4" style="float: left"><img :src="linkTypeUrl[0]"></el-col>
          <el-col :span="8" style="float: left"><label
            style="font-size: 10px; float: left; margin-top: 2px">{{$t('ElineRouteView.active')}}</label></el-col>
          <el-col :span="4" style="float: left"><img :src="linkTypeUrl[1]"></el-col>
          <el-col :span="8" style="float: left"><label
            style="font-size: 10px; float: left; margin-top: 2px">{{$t('ElineRouteView.inActive')}}</label></el-col>
        </el-col>
      </el-col>

    </el-row>
      <!-- 绘制路由视图部分 -->
    <svg id='svgWrapper'></svg>
    <el-row style="margin-bottom: 0px;" slot="footer" class="dialog-footer">
      <el-button style="margin-bottom: -10px;" size='mini' @click="close">{{$t("common.cancel")}}</el-button>
    </el-row>
  </el-dialog>
</template>

<script>
  import TopologyManager from '../../../domain/models/TopologyManager'
  import HelperUtil from '../../../utils/HelperUtil'
  import {TOPOLOGY_KEY,LINK_KEY} from  '../../../utils/constantCollection'

  let d3 = require('d3');
  let svg = '',viewGroup = '';
  let curThis = '';

  export default {
    name:'ElineRouteViewDialog',
    //1.声明从父组件接受的value，注意名字一致。
    props: {
      routeViewDialogVisible: null,//控制弹框的显示
      routePathObject:null,//路由视图的路径对象
      topologyCollection: null,//topology集合
      drawGraphSizeConfig:null,//画svg的基本规格参数
      endPointObject:null//当前行业务的一些基本信息
    },
    data() {
      return {
        routePathList: [],//路由视图的路径数组
        deviceListObject: null,//路由视图的设备数组对象集合
        deviceList: [],//路由视图的设备数组
        drawSvgSizeConfig:{},//画svg的基本规格参数
        bandWidth:-1,//路由视图的带宽
        linkStateUrl:['./static/routePathWork.png','./static/routePathPro.png'],
        linkTypeUrl:['./static/routePathAct.png','./static/routePathNoAct.png'],
      }
    },
    methods:{
      /**
       * @Description  ：初始化，在dialog opened生命周期调用
       * @author       : yjz
       * @param        :
       * @return       :
       * @exception    :
       * @date         :
       */
      dialogInit: function () {
        this.drawSvgSizeConfig = this.drawGraphSizeConfig;
        this.bandWidth = this.routePathObject.limitedBandwidth ;
        //1.创建SVG画布、创建视图群组g、创建标识框
        this.createSvg();
        this.handleDeviceList();
        this.handleRouteLinkList();
        //3.渲染视图
        this.renderView();
      },

      /**
       * @Description  ：初始化调用,处理设备列表
       * @author       : yjz
       * @param        :
       * @return       :
       * @exception    :
       * @date         :
       */
      handleDeviceList() {
        let TopologyMgr = TopologyManager(this.topologyCollection);
        this.deviceListObject = TopologyMgr.getRouteDeviceList(this.routePathObject.pathList,this.endPointObject);
        let list = Object.values(this.deviceListObject);  //对象转换成数组
        let listTemp = [];
        list.forEach(function (device) {
          let topologyTemp = {};  //构建新的数组，指向新的地址，避免污染collection
          topologyTemp.topologyKey = device.topologyKey;
          topologyTemp.topologyName = device.topologyName;
          topologyTemp.belongSubnet = device.belongSubnet;
          topologyTemp.nodeType = device.nodeType;
          topologyTemp.status = device.status;
          topologyTemp.xAxis = device.xAxis;
          topologyTemp.yAxis = device.yAxis;
          topologyTemp.routeType = device.routeType;

          listTemp.push(topologyTemp)
        })
        this.deviceList = HelperUtil.unique(listTemp);  //对象数组去重
        this.handleTopologyPosition();
      },

      /**
       * @Description  ：初始化调用,处理设备位置
       * @author       : yjz
       * @param        :
       * @return       :
       * @exception    :
       * @date         :
       */
      handleTopologyPosition() {
        
        //处理节点的坐标，使其居于屏幕中央，并且能显示完整。
        let minX = curThis.deviceList[0].xAxis,minY = curThis.deviceList[0].yAxis,
            maxX = curThis.deviceList[0].xAxis, maxY = curThis.deviceList[0].yAxis;
        let centerX = curThis.drawGraphSizeConfig.svgWidth/2;
        let centerY = curThis.drawGraphSizeConfig.svgHeight/2;
        //1.遍历节点，遍历的同时找到坐标的最小和最大值
        curThis.deviceList.forEach(function (node) {
          if (node.xAxis < minX) {  //search minX
            minX = node.xAxis;
          }
          if (node.xAxis > maxX) {  //search maxX
            maxX = node.xAxis;
          }
          if (node.yAxis < minY) {  //search minY
            minY = node.yAxis;
          }
          if (node.yAxis > maxY) {  //search maxY
            maxY = node.yAxis;
          }
        })
        //2.根据极值坐标，判断需要缩放的倍数,进行坐标的改变
        let maxWidth = maxX - minX;
        let maxHeight = maxY - minY;
        let kx = 1,ky = 1,k = 1;  //需要缩放的倍数
        if (maxWidth >= 1000) {
          kx = 1000/maxWidth;
        }
        if (maxHeight >= 200) {
          ky = 200/maxHeight
        }
        if (kx < ky) {
          k = kx;
        } else {
          k = ky
        }
        curThis.deviceList.forEach(function (node) {
          node.xAxis = node.xAxis * k;
          node.yAxis = node.yAxis * k;
        })
        //3.根据坐标的最小和最大值，算出中间点的值，再根据Center的坐标，求出<viewGroup>偏移量。
        let middleX = (maxX + minX)/2 * k,middleY = (maxY + minY)/2 * k;  //中点坐标
        let left = centerX - middleX , top = centerY - middleY; //偏移量
        d3.select('#view-group').attr('transform','translate(' + left + ',' + top + ')')  //开始偏移

        /**
        //遍历deviceList，count计数，看下有多少个设备
        let count = this.deviceList.length;
        let workCount = 0, protectCount = 0;
        let workList = new Array() ,protectList = new Array(),endNodeList = new Array();
        for (let i = 0; i < this.deviceList.length; i++) {
          switch (this.deviceList[i].routeType) {
            case '0':
              this.deviceList[i].xAxis = 30;
              this.deviceList[i].yAxis = 100;
              break;
            case '2':
              workCount++;
              workList.push(this.deviceList[i]);
              break;
            case '3':
              protectCount++;
              protectList.push(this.deviceList[i]);
              break;
            case '1':
              endNodeList.push(this.deviceList[i]);
              break
          }
        };
        // count = Math.max(workCount,protectCount)
        //计算出每个设备之间的间距，每两个平行的设备间距是多大，distance
        let workDistance = parseInt((1000) / (workCount + 2));
        let protectDistance = parseInt((1000) / (protectCount + 2));
        let workEndXAxis = 30 + workDistance * (workCount + 1);
        let protectEndXAxis = 30 + protectDistance * (protectCount + 1)
        let endXAxis = Math.max(workEndXAxis,protectEndXAxis)
        // let distance = Math.min(workDistance,protectDistance)
        //开始给节点赋值
        if (this.deviceList.length === 2) {//当路由路径只有起始和终点两个节点，一条路径。
          this.deviceList[0].xAxis = 300;
          this.deviceList[0].yAxis = 100;

          this.deviceList[1].xAxis = 700;
          this.deviceList[1].yAxis = 100;
        } else {//当路由路径有多个节点
          //根据type给设备的坐标赋值，先给0 1 赋值，在根据distance给 2 3 赋值。

          //处理主设备
          for (let i = 0; i < workCount; i++) {
            workList[i].xAxis = workDistance * (i + 1);
            workList[i].yAxis = 50;
          }
          //处理保护设备
          for (let i = 0; i < protectCount; i++) {
            protectList[i].xAxis = protectDistance * (i + 1);
            protectList[i].yAxis = 150;
          }
          //处理终点设备，有双归模式。
          if (endNodeList.length === 1) {//单归模式，简单
            endNodeList[0].xAxis = 30 + endXAxis;
            endNodeList[0].yAxis = 100;
          }
          if (endNodeList.length === 2) {//双归模式，判断类型。
            for (let j = 0; j < this.routePathObject.pathList.length; j++) {
              if (endNodeList[0].topologyKey === this.routePathObject.pathList[j].dstDeviceId) {
                if (this.routePathObject.pathList[j].pathType === 'work') {
                  endNodeList[0].xAxis = 30 + workDistance * workCount;
                  endNodeList[0].yAxis = 50;
                  endNodeList[1].xAxis = 30 + protectDistance * protectCount;
                  endNodeList[1].yAxis = 150;
                } else if (this.routePathObject.pathList[j].pathType === 'protect') {
                  endNodeList[1].xAxis = 30 + workDistance * workCount;
                  endNodeList[1].yAxis = 50;
                  endNodeList[0].xAxis = 30 + protectDistance * protectCount;
                  endNodeList[0].yAxis = 150;
                }
                break;
              }
            }
          }
        }
         */

      },

      /**
       * @Description  ：初始化调用,处理链路列表
       * @author       : yjz
       * @param        :
       * @return       :
       * @exception    :
       * @date         :
       */
      handleRouteLinkList() {
        let topologyListObject = HelperUtil.arrayToObject(this.deviceList,TOPOLOGY_KEY) //将处理好的拓扑集合转成对象
        let TopologyMgr = TopologyManager(topologyListObject);
        let listTemp = [];
        this.routePathObject.pathList.forEach(function (link) {
          let linkTemp = {};  //构建新的数组，指向新的地址，避免污染collection
          linkTemp.endDeviceKey = link.dstDeviceId;
          linkTemp.endPortName = link.dstDevicePortNumber;
          linkTemp.linkKey = link.pathKey;
          linkTemp.pathType = link.pathType;
          linkTemp.startDeviceKey = link.srcDeviceId;
          linkTemp.startPortName = link.srcDevicePortNumber;
          linkTemp.vpnId = link.vpnId;

          listTemp.push(linkTemp)
        })
        let linkList = HelperUtil.unique(listTemp);  //对象数组去重
        let linkListObject = HelperUtil.arrayToObject(linkList,LINK_KEY) //将处理好的拓扑集合转成对象
        this.routePathList = TopologyMgr.getNewLinkList(linkListObject);
      },

      /**
      * @Description  ：创建svg画布
      * @author       : yjz
      * @param        :
      * @return       :
      * @exception    :
      * @date         :
      */
      createSvg() {
        svg = d3.select('#svgWrapper')
          .attr('width',curThis.drawGraphSizeConfig.svgWidth)
          .attr('height',curThis.drawGraphSizeConfig.svgHeight);
        viewGroup = svg.append('g').attr('id','view-group').attr('transform','translate(0,0)')

      },

      /**
      * @Description  ：绘制路由视图
      * @author       : yjz
      * @param        :
      * @return       :
      * @exception    :
      * @date         :
      */
      renderView() {
        //绘制连接
        this.drawRouteLink();
        //绘制节点
        this.drawRouteDevice();

      },

      /**
      * @Description  ：绘制连接
      * @author       : yjz
      * @param        :
      * @return       :
      * @exception    :
      * @date         :
      */
      drawRouteLink() {
        let linkUpdate = viewGroup.selectAll(".link").data(curThis.routePathList);//返回数据和dom元素的交集
        linkUpdate.exit().remove();//返回多出的dom集合并处理
        let linkEnter = linkUpdate.enter().insert("g",".node").classed("link",true);//返回多出的数据集合并处理

        linkUpdate = linkUpdate.merge(linkEnter);//将enter 合并 到 update 里面去，使得更新包含第一次创建。

        //为二维数组中的每一个一维数组绘制连线。
        linkUpdate.each(function (d) {
          curThis.$options.methods.drawRoutePath(d, d3.select(this));
        });
      },

      /**
       * @Description  ：处理每两个节点之间的一组连线，可能包含多条
       * @author       : yjz
       * @param        :两个参数，第一个参数是二维数组中的每一项，也就是每两个节点之间的一组连线，是一个一维数组。
       *                           第二个参数是选择的当前数据代表的对象，类为link的g元素
       * @return       :
       * @exception    :
       * @date         :
       */
      drawRoutePath(d,object) {
        //新建一个数组，存放坐标重新计算好的一组连线。
        let paths = new Array();
        //处理传过来的数据，重新计算好坐标组成一个新的数组，并进行后续的绘制。
        curThis.$options.methods.handleManyRoutePath(d,paths);
        //返回每条连线的path和data的选择集
        let routePathEnter =  object.selectAll(".path").data(paths,function (d) {return d.linkKey;})
          .enter()
          .append("g")
          .classed("path",true);

        routePathEnter.append("path")
          .attr("class",'route-path')
          .attr("d",curThis.$options.methods.handlePathData)//函数处理path坐标
          .style("stroke",curThis.$options.methods.handlePathColor)//函数处理连线颜色
          .style('stroke-width','2')
          .style('fill','none')
          .style('stroke-linecap','round')
          .style("stroke-dasharray",curThis.$options.methods.handlePathType)//函数处理连线虚实;
      },

      /**
       * @Description  ：根据链路的二维数组，转化为一个一维数组，同时使得相同两个设备之间的多条链路不会重合，错开显示
       * @author       : yjz
       * @param        : 二维数组中的每一项，也就是每两个节点之间的一组连线，是一个一维数组
       * @return       :
       * @exception    :
       * @date         :
       */
      handleManyRoutePath(d,paths) {
        for (let i = 0; i < d.length; ++i) {
          let sourceAxis =[0,0],targetAxis = [0,0];
          if (i < 3) {
            //此处为大多数的情况，两个节点之间最多有三条线，分别计算坐标
            switch (i) {
              //第一条线的坐标
              case 0:
                sourceAxis = [d[i].source.xAxis , d[i].source.yAxis ];
                targetAxis = [d[i].target.xAxis , d[i].target.yAxis ];
                break;
              //第二条线的坐标
              case 1:
                sourceAxis = [d[i].source.xAxis - 10, d[i].source.yAxis - 10];
                targetAxis = [d[i].target.xAxis - 10, d[i].target.yAxis - 10];
                break;
              //第三条线的坐标
              case 2:
                sourceAxis = [d[i].source.xAxis + 10, d[i].source.yAxis + 10];
                targetAxis = [d[i].target.xAxis + 10, d[i].target.yAxis + 10];
                break;
            }
          } else {
            //根据i的奇偶来决定是在上面还是在下面
            let position = Math.pow((-1),i);
            //对2向上取整，使得宽度一上一下错落有致
            let top = Math.ceil(i/2);
            sourceAxis = [d[i].source.xAxis + top * position * 10, d[i].source.yAxis + top * position * 10];
            targetAxis = [d[i].target.xAxis + top * position * 10, d[i].target.yAxis + top * position * 10];
          }
          paths.push({"sourceAxis": sourceAxis, "targetAxis": targetAxis, "linkKey":d[i].linkKey  ,"pathType":d[i].pathType ,
            "endPortName":d[i].endPortName , "startPortName":d[i].startPortName
          });

        }
      },

      /**
      * @Description  ：处理连线数据
      * @author       : yjz
      * @param        :
      * @return       :
      * @exception    :
      * @date         :
      */
      handlePathData(p) {
        let M =  'M' + (p.sourceAxis[0] + 16) + ','+ (p.sourceAxis[1] + 16);
        let L =  'L' + (p.targetAxis[0] + 16) + ','+ (p.targetAxis[1] + 16);
        let pathData = M + '' + L + 'Z';
        return pathData;
      },

      /**
      * @Description  ：处理连线颜色，根据主备路径
      * @author       : yjz
      * @param        :
      * @return       :
      * @exception    :
      * @date         :
      */
      handlePathColor(p) {
        if (p.pathType === 'work') {
          return '#FF0000'
        } else if (p.pathType === 'protect') {
          return '#00FF00'
        }
      },

      /**
      * @Description  ：处理连线虚实，根据路径状态
      * @author       : yjz
      * @param        :
      * @return       :
      * @exception    :
      * @date         :
      */
      handlePathType(p) {
        //后台未给字段，区分active状态
      },

      /**
      * @Description  ：绘制节点
      * @author       : yjz
      * @param        :
      * @return       :
      * @exception    :
      * @date         :
      */
      drawRouteDevice() {
        let routeNodeEnter = viewGroup.selectAll(".node").data(curThis.deviceList,function (d) {return d.topologyKey;})
          .enter()
          .append("g")
          .classed("node",true)
          .attr('transform',d => {return 'translate(' + d.xAxis + ',' + d.yAxis + ')'});

        routeNodeEnter.append('image')
          .attr('class','route-image')
          .attr("width",this.drawSvgSizeConfig.imageWidth)
          .attr("height",this.drawSvgSizeConfig.imageHeight)
          .attr("xlink:href",curThis.$options.methods.handleRouteImage)

        routeNodeEnter.append('text')
          .attr('class','rout-node-text')
          .attr("dx",15)
          .attr("dy",45)
          .style('font-size','12px')
          .style('text-anchor','middle')
          .style('font-family','SimSun')
          .text(d => {return d.topologyName})

      },

      /**
      * @Description  ：处理路由图像
      * @author       : yjz
      * @param        :
      * @return       :
      * @exception    :
      * @date         :
      */
      handleRouteImage() {
        return './static/475C9D_topology.png'
      },

      /**
      * @Description  ：控制对话框的显示
      * @author       : yjz
      * @param        :
      * @return       :
      * @exception    :
      * @date         :
      */
      close() {
        this.$emit("update:routeViewDialogVisible", false);
      },
      ok() {
        this.$emit("update:routeViewDialogVisible", false);
      },

    },
    computed: {},
    watch: {},
    //无法获取 data中的数据、methods中的方法
    beforeCreate() {},
    //常用的生命周期，可以调用methods中的方法、改变data中的数据
    created() {
      //将this对象进行转换，避免出现对象未定义错误
      curThis = this;
    },
    //在挂载开始之前被调用
    beforeMount() {},
    //此时，vue实例已经挂载到页面中，可以获取到el中的DOM元素，进行DOM操作
    mounted() {},
    //运行期间
    beforeUpdate() {},
    updated() {},
    //销毁期间
    beforeDestroy() {},
    destroyed() {},
  }
</script>

<style scoped>
</style>
