<template>
  <div :id="maxContainerId" class="graphParentContainer"></div>
</template>
<script>
import * as PIXI from "pixi.js";

export default {
  props: [],
  data() {
    return {
      maxContainerId:
        new Date().getTime().toString() +
        "_" +
        Math.random().toString().substring(2),
      /*
       * 设备数组格式如下
       * {
            id: "",
            name:'',
            legendObj: {
              content: "图的地址或base64串",
              size: {
                width: 12,
                height: 12,
              },
            },
            //设备的绝对位置
            absolutePosition: {
              x: 1,
              y: 1,
            },
            anchorArr:[{
              name:'锚点名称',
              //锚点相对于设备图例的位置
              relativePosition:{
                x:1,y:1
              }
            }]
          }
      */
      nodeArr: [],
      /**
       * 线数组，格式如下：
       * lines:[{
          id: '',
          path:[{x:1,y:1}],
          style: {
                    lineWidth: 4, //线宽
                    lineColor: '#8D9399', //线的颜色
                  }
        }]
       */
      lineArr: [],
      /**
       * 文本数组，格式如下：
       * labels: [{
          id:'',
          text:'',
          style:{
            fontColor: '',
            fontSize: '',
            backColor:'',
            fontWeight:1
          },
          //文本的绝对位置
          absolutePosition: {
            x: 1,
            y: 1,
          },
        }]
       */
      labelArr: [],
      //存放图形用的一些信息点
      canvasProObj: {
        //基于canvas画布的中心点坐标，需要动态计算得到
        centerPointerPx: { x: 0, y: 0 },
        //原始缩放比例，动态计算得到的，因为图形默认显示时是要适应容器大小的，所以相比原始坐标会有比例变化
        originalScale: 1,
        //x轴差值，通过此值让x轴左右两端对称
        xJiaValue: 0,
        //y轴差值，通过此值让y轴上下两端对称
        yJiaValue: 0,
        //缓存纹理对象
        textureSource: {},
        //缩放系数
        pixiScale: 1,
        //原点X坐标
        positionPxX: 0,
        //原点Y坐标
        positionPxY: 0,
        //缩放时每次变化的递进值
        deep: 0.1,
        //鼠标状态，0 普通状态；-1 鼠标按下
        mouseState: 0,
        //上一个鼠标位置信息
        prevMousePosition: {},
      },
      //是否是点击的图形上的元素
      isClickGraphNode: false,
    };
  },
  computed: {},
  methods: {
    //绘图入口
    drawEntry: function (graphInfo) {
      if (!graphInfo) return;
      //把原始数据转为易于绘图的数据
      var graphDataObj = this.parseDataToDraw(graphInfo);
      this.nodeArr = graphDataObj.nodeArr;
      this.lineArr = graphDataObj.lineArr;
      this.labelArr = graphDataObj.labelArr;

      this.drawGraph();
    },
    //重绘
    resetDraw: function () {
      this.drawGraph();
    },
    //绘图
    drawGraph: function () {
      var _this = this;
      // let type = "WebGL";
      // if (!PIXI.utils.isWebGLSupported()) {
      //   type = "canvas";
      // }

      var pixiContainer = document.getElementById(this.maxContainerId);

      //--------------------------------------------创建舞台
      //存在舞台时，先清空舞台内的所有元素
      if (!!_this.pixiApp) {
        while (_this.pixiApp.stage.children.length > 0) {
          _this.pixiApp.stage.removeChildAt(0);
        }
      }

      var pixiApp;
      if (_this.pixiApp == null) {
        pixiApp = new PIXI.Application({
          width: pixiContainer.clientWidth, // default: 800 宽度
          height: pixiContainer.clientHeight, // default: 600 高度
          antialias: true, // default: false 反锯齿
          transparent: true, // default: false 透明度
          resolution: 1, // default: 1 分辨率
          //backgroundColor: "0xffffff",
        });
        pixiApp.renderer.autoResize = true;

        pixiApp.view.addEventListener("click", (event) => {
          //是点击的图形上元素节点冒泡上来时什么也不坐
          if (_this.isClickGraphNode == true) {
            _this.isClickGraphNode = false;
            return;
          }
          //关闭右侧详情框
          this.$emit("closeright");
        });

        pixiContainer.appendChild(pixiApp.view);
        _this.pixiApp = pixiApp;
      } else pixiApp = _this.pixiApp;

      _this.computeOriginZuobPosition();

      //图例
      _this.nodeArr.forEach((_dataObj) => {
        //2022.1.30版，图例用矩形框代替
        var graphicsRect = new PIXI.Graphics();
        graphicsRect.lineStyle({
          width: 1,
          color: "0x0091ff",
          alpha: 1,
          alignment: 0.5,
        });
        let newCordObj = _this.convertCoordToLeftOrigina({
          x: _dataObj.absolutePosition.x,
          y: _dataObj.absolutePosition.y,
        });
        var newWidth =
          _dataObj.legendObj.size.width * _this.canvasProObj.originalScale;
        var newHeight =
          _dataObj.legendObj.size.height * _this.canvasProObj.originalScale;
        graphicsRect.drawRect(newCordObj.x, newCordObj.y, newWidth, newHeight);
        //设置点击范围，不然click事件不会触发
        graphicsRect.hitArea = graphicsRect.getBounds();

        graphicsRect.name = _dataObj.id;
        //启用事件
        graphicsRect.interactive = true;
        graphicsRect.on("click", (event) => {
          _this.isClickGraphNode = true;
          _this.clickEventCall(event, 1);
        });

        pixiApp.stage.addChild(graphicsRect);
        return;

        var _legendObj = _dataObj.legendObj;
        // 创建一个纹理
        if (!_this.canvasProObj.textureSource[_legendObj.image.content]) {
          _this.canvasProObj.textureSource[_legendObj.image.content] =
            PIXI.Texture.from(_legendObj.image.content);
        }

        //加载纹理
        var spriteInstanceCoord = _this.convertCoordToLeftOrigina({
          x: _dataObj.position.x,
          y: _dataObj.position.y,
        });
        var spriteWidth =
          _legendObj.defaultSize.width * _this.canvasProObj.originalScale;
        var spriteHeight =
          _legendObj.defaultSize.height * _this.canvasProObj.originalScale;

        var legendSprite = new PIXI.Sprite(
          _this.canvasProObj.textureSource[_legendObj.image.content]
        );
        legendSprite.x = spriteInstanceCoord.x;
        legendSprite.y = spriteInstanceCoord.y;
        legendSprite.width = spriteWidth;
        legendSprite.height = spriteHeight;
        pixiApp.stage.addChild(legendSprite);
      });

      //文本
      _this.labelArr.forEach((_labelObj) => {
        var pixiTextStyle = new PIXI.TextStyle({
          fill: _labelObj.style.fontColor,
          fontSize: _labelObj.style.fontSize,
          fontWeight: _labelObj.style.fontWeight,
          // stroke: "#ffffff",
          // strokeThickness: _this.defaultStyle.text.fontStrokeWidth,
        });
        var textInstance = new PIXI.Text(_labelObj.text, pixiTextStyle);
        textInstance.name = _labelObj.id;

        let newCordObj = _this.convertCoordToLeftOrigina({
          x: _labelObj.absolutePosition.x,
          y: _labelObj.absolutePosition.y,
        });

        textInstance.position.set(newCordObj.x, newCordObj.y);
        // console.log(textInstance.width, "-", textInstance.height);
        //启用事件
        textInstance.interactive = true;
        textInstance.on("click", (event) => {
          _this.isClickGraphNode = true;
          _this.clickEventCall(event, 3);
        });
        pixiApp.stage.addChild(textInstance);
      });

      //画线
      _this.lineArr.forEach((_cLine, _index) => {
        var lineColor = "0x" + _cLine.style.lineColor.substring(1);
        var lineInstance = new PIXI.Graphics();
        lineInstance.lineStyle(_cLine.style.lineWidth, lineColor, 1, 0, false);
        lineInstance.name = _cLine.id;

        var _movePath = _cLine.path[0];
        let _newMove = _this.convertCoordToLeftOrigina({
          x: _movePath.x,
          y: _movePath.y,
        });
        lineInstance.moveTo(_newMove.x, _newMove.y);

        for (let i = 1; i < _cLine.path.length; i = i + 1) {
          let _toPath = _cLine.path[i];
          let _newTo = _this.convertCoordToLeftOrigina({
            x: _toPath.x,
            y: _toPath.y,
          });

          lineInstance.lineTo(_newTo.x, _newTo.y);
        }

        //设置点击范围，不然click事件不会触发
        lineInstance.hitArea = lineInstance.getBounds();
        //启用事件
        lineInstance.interactive = true;
        lineInstance.on("click", (event) => {
          _this.isClickGraphNode = true;
          _this.clickEventCall(event, 2);
        });
        pixiApp.stage.addChild(lineInstance);
      });
    },
    /**
     * 解析数据成易于图形使用的格式
     */
    parseDataToDraw: function (graphInfo) {
      var nodeArr = [];
      //线数组
      var lineArr = [];
      //文本数组
      var labelArr = [];

      if (graphInfo.template) {
        var rootContainerArr = graphInfo.template.frame.children;

        parseNode(rootContainerArr, graphInfo.template.frame.location);

        parseLine(graphInfo.lines || []);
        parseMainPies(graphInfo.template.mainPipes);
      }

      return {
        nodeArr,
        lineArr,
        labelArr,
      };

      /**
       * 递归解析设备(node)、文本(label)
       * dataArr 参照后台接口返回的diagram对象里的graphInfo.template.frame.children
       * absolutePosition 绝对位置{x:1,y:1}    其值是compType为"container"的每一级的location相加得出的值
       */
      function parseNode(dataArr, absolutePosition) {
        dataArr.forEach((_dataObj) => {
          var newAbsolutePosition = {
            x: _dataObj.location.x + absolutePosition.x,
            y: _dataObj.location.y + absolutePosition.y,
          };

          //文本
          if (_dataObj.label && _dataObj.label.content) {
            let _labelObj = _dataObj.label;
            _labelObj.id = _labelObj.id || Math.random().toString();

            let _style = _labelObj.style || {};
            _style.fontColor =
              _style.fontColor ||
              window.__systemConf.systemGraph.peiDian.text.color;
            _style.fontSize =
              _style.fontSize ||
              window.__systemConf.systemGraph.peiDian.text.size;
            _style.backColor =
              _style.backColor ||
              window.__systemConf.systemGraph.peiDian.text.backGround;
            _style.fontWeight =
              _style.fontWeight ||
              window.__systemConf.systemGraph.peiDian.text.weight;

            var textNewAbsolutePosition = {
              x: _labelObj.location.x + newAbsolutePosition.x,
              y: _labelObj.location.y + newAbsolutePosition.y,
            };

            labelArr.push({
              id: _labelObj.id,
              text: _labelObj.content,
              style: _style,
              //文本的绝对位置
              absolutePosition: textNewAbsolutePosition,
              //旧的绝对位置，用于文本位置调整
              oldAbsolutePosition: JSON.parse(
                JSON.stringify(textNewAbsolutePosition)
              ),
            });

            _labelObj.style = _style;
            _dataObj.label = _labelObj;
          }
          switch (_dataObj.compType) {
            //容器
            case "container":
              parseNode(_dataObj.children, newAbsolutePosition);
              break;
            //设备
            case "equipmentNode":
              let anchorArr = [];
              let anchorKeys = Object.keys(_dataObj.anchorLocations || {});
              anchorKeys.forEach((_anchorKey) => {
                anchorArr.push({
                  name: _anchorKey,
                  //锚点相对于设备图例的位置
                  relativePosition: _dataObj.anchorLocations[_anchorKey],
                });
              });

              nodeArr.push({
                id: _dataObj.id,
                name: _dataObj.dataObject.localName,
                //2022.1.30版本图例以矩形框代替
                legendObj: {
                  content: "图的地址或base64串",
                  size: {
                    width: _dataObj.size.x,
                    height: _dataObj.size.y,
                  },
                },
                //设备的绝对位置
                absolutePosition: newAbsolutePosition,
                anchorArr: anchorArr,
              });
              break;
          }
        });
      }

      /**
       * 解析线
       */
      function parseLine(lines) {
        lines.forEach((_line) => {
          if (_line.flag != "duplicate") {
            _line.id = _line.id || _line.dataObjectId;
            let lineDefaultStyle = window.__systemConf.systemGraph.peiDian.line;
            lineArr.push({
              id: _line.id,
              path: _line.locationPath,
              style: {
                lineWidth: lineDefaultStyle.width,
                lineColor: lineDefaultStyle.color,
              },
            });
          }
        });
      }

      /**
       * 解析干管，当做线处理
       */
      function parseMainPies(_dataArr) {
        _dataArr.forEach((_data) => {
          _data.id = _data.id || _data.dataObjectId;
          let lineDefaultStyle = window.__systemConf.systemGraph.peiDian.line;

          var pathArr = [];
          (_data.locationPath || []).forEach((_c1) => {
            _c1.forEach((_c2) => {
              pathArr.push({
                x: _c2.x,
                y: _c2.y,
              });
            });
          });
          lineArr.push({
            id: _data.id,
            path: pathArr,
            style: {
              lineWidth: lineDefaultStyle.width,
              lineColor: lineDefaultStyle.color,
            },
          });
          return;

          var pathArr = eachPathArr(_data.locationPath || [], []);

          lineArr.push({
            id: _data.id,
            path: pathArr,
            style: {
              lineWidth: lineDefaultStyle.width,
              lineColor: lineDefaultStyle.color,
            },
          });
        });
      }

      /**
       * 递归解析path数组
       */
      function eachPathArr(_arr, returnArr) {
        _arr.forEach((_data) => {
          if (_data instanceof Array) {
            eachPathArr(_data, returnArr);
          } else
            returnArr.push({
              x: _data.x,
              y: _data.y,
            });
        });
        return returnArr;
      }
    },
    /**
     * 1、X轴的原始坐标两端可能是不对称的；Y轴的原始坐标两端也可能是不对称的，所以计算各自的差值，以便把两端对称起来，以此把图形显示到正中间
     * 2、计算原始坐标值的缩放系数，因为原始坐标和像素坐标是不对应的，所以要根据最大原始坐标(每一个坐标的绝对值进行比较)和最大像素坐标做比率缩放
     * 3、计算中心点像素坐标
     */
    computeOriginZuobPosition: function () {
      var _this = this;

      var pixiContainer = document.getElementById(this.maxContainerId);
      var maxPxX = pixiContainer.clientWidth;
      var maxPxY = pixiContainer.clientHeight;
      //基于canvas画布的中心点坐标
      _this.canvasProObj.centerPointerPx = {
        x: maxPxX / 2,
        y: maxPxY / 2,
      };

      var originaMinX = null, //原始最小坐标X
        originaMaxX = null, //原始最大坐标X
        originaMinY = null, //原始最小坐标Y
        originaMaxY = null; //原始最大坐标Y

      /**
       * 取得X轴的原始最小坐标和最大坐标、
       * 取得Y轴的原始最小坐标和最大坐标、
       */
      compute();

      //计算X轴差值
      var xJiaValue = (maxPxX - originaMaxX - originaMinX) / 2;
      xJiaValue = originaMinX + xJiaValue < 0 ? 0 - originaMinX : xJiaValue;

      //计算Y轴差值
      var yJiaValue = (maxPxY - originaMaxY - originaMinY) / 2;
      yJiaValue = originaMinY + yJiaValue < 0 ? 0 - originaMinY : yJiaValue;

      _this.canvasProObj.xJiaValue = xJiaValue;
      _this.canvasProObj.yJiaValue = yJiaValue;

      //让每一个的X、Y坐标加上差值，改为在画图时再加差值，这样就减少了此处的循环
      setScale();

      /**
       * 取得X轴的原始最小坐标和最大坐标、
       * 取得Y轴的原始最小坐标和最大坐标、
       */
      function compute() {
        _this.nodeArr.forEach((_c) => {
          var _legendObj = _c.legendObj;
          originaMinX =
            originaMinX == null
              ? _c.absolutePosition.x
              : Math.min(originaMinX, _c.absolutePosition.x);
          originaMaxX =
            originaMaxX == null
              ? _c.absolutePosition.x + _legendObj.size.width
              : Math.max(
                  originaMaxX,
                  _c.absolutePosition.x + _legendObj.size.width
                );

          originaMinY =
            originaMinY == null ? _c.absolutePosition.y : Math.min(originaMinY);

          originaMaxY =
            originaMaxY == null
              ? _c.absolutePosition.y + _legendObj.size.height
              : Math.max(
                  originaMaxY,
                  _c.absolutePosition.y + _legendObj.size.height
                );
        });

        _this.lineArr.forEach((_cLine) => {
          _cLine.path.forEach((_c) => {
            originaMinX =
              originaMinX == null ? _c.x : Math.min(originaMinX, _c.x);
            originaMaxX =
              originaMaxX == null ? _c.x : Math.max(originaMaxX, _c.x);

            originaMinY =
              originaMinY == null ? _c.y : Math.min(originaMinY, _c.y);
            originaMaxY =
              originaMaxY == null ? _c.y : Math.max(originaMaxY, _c.y);
          });
        });
      }

      //计算缩放系数
      function setScale() {
        var xScale = maxPxX / (originaMaxX + xJiaValue);
        var yScale = maxPxY / (originaMaxY + yJiaValue);
        _this.canvasProObj.originalScale = Math.min(xScale, yScale);
        //当图形区域能显示全的时候不再进行缩放
        _this.canvasProObj.originalScale = Math.min(
          1,
          _this.canvasProObj.originalScale
        );
      }
    },
    /**
     * 把坐标转为缩放后的坐标
     * _obj:{x:1,y:1}
     */
    convertCoordToLeftOrigina: function (_obj) {
      _obj.x =
        (_obj.x + this.canvasProObj.xJiaValue) *
        this.canvasProObj.originalScale;
      _obj.y =
        (_obj.y + this.canvasProObj.yJiaValue) *
        this.canvasProObj.originalScale;

      return _obj;
    },

    /**
     * 设备、线、文本点击事件的回调
     * type 1 设备；  2 线；  3 文本
     */
    clickEventCall: function (event, type) {
      event.stopPropagation();
      var graphInstance = event.target || event.currentTarget;
      var dataId = graphInstance.name;
      var dataArr =
        type == 1
          ? this.nodeArr
          : type == 2
          ? this.lineArr
          : type == 3
          ? this.labelArr
          : [];
      var dataObj = dataArr.filter((_data) => {
        return _data.id == dataId;
      })[0];
      this.$emit("click", dataObj, type);
    },
    //图形缩放比例递进计算
    graphScaleCompute: function (deepVal) {
      return this.canvasProObj.pixiScale + deepVal;
    },
    //图形缩放
    graphScale: function (newScale) {
      newScale = Math.min(5, newScale);
      newScale = Math.max(0.25, newScale);
      //设置X、Y轴的缩放系数
      this.pixiApp.stage.scale.x = newScale;
      this.pixiApp.stage.scale.y = newScale;

      //保存缩放系数
      this.canvasProObj.pixiScale = newScale;
      //重新计算原点
      this.canvasProObj.positionPxX =
        (1 - newScale) * this.canvasProObj.centerPointerPx.x;
      this.canvasProObj.positionPxY =
        (1 - newScale) * this.canvasProObj.centerPointerPx.y;
      this.pixiApp.stage.position.set(
        this.canvasProObj.positionPxX,
        this.canvasProObj.positionPxY
      );
    },
    /**
     * 更新graphInfo中的数据
     * dataType 1 设备；  2 线；  3 文本
     * operType 1 移除设备节点；  2 更新文本字体大小；  3 更新文本颜色；  4 更新文本背景色；  5 移除文本节点  6 修改文本位置；  7 修改文本内容;   8删除线
     * exprObj 扩展数据，支持：{id:'数据id',fontSize:'字体大小',fontColor:'文本颜色',fontBackColor:'文本背景色',text:{xChange:1 新的绝对坐标减去旧的绝对坐标得出的值 ,yChange:1 新的绝对坐标减去旧的绝对坐标得出的值,content:'文本内容'}}
     */
    updateDataAndGraph: function (dataType, operType, exprObj) {
      var dataArr =
        dataType == 1
          ? this.nodeArr
          : dataType == 2
          ? this.lineArr
          : dataType == 3
          ? this.labelArr
          : [];
      for (let i = 0; i < dataArr.length; i++) {
        let _dataObj = dataArr[i];
        if (_dataObj.id == exprObj.id) {
          var stageChild = this.pixiApp.stage.getChildByName(exprObj.id);
          var _stageStyle = stageChild.style;
          switch (operType) {
            case 1:
            case 5:
            case 8:
              dataArr.splice(i, 1);
              this.pixiApp.stage.removeChild(stageChild);
              break;
            case 2:
              _dataObj.style.fontSize = exprObj.fontSize;
              _stageStyle.fontSize = exprObj.fontSize;
              stageChild.style = _stageStyle;
              break;
            case 3:
              _dataObj.style.fontColor = exprObj.fontColor;
              _dataObj.style.backColor = exprObj.fontBackColor;
              _stageStyle.fill = exprObj.fontColor;
              stageChild.style = _stageStyle;
              break;
            case 4:
              _dataObj.style.backColor = exprObj.fontBackColor;
              break;
            case 6:
              let newCordObj = this.convertCoordToLeftOrigina({
                x: _dataObj.absolutePosition.x,
                y: _dataObj.absolutePosition.y,
              });
              stageChild.position.set(newCordObj.x, newCordObj.y);
              break;
            case 7:
              stageChild.text = exprObj.text.content;
              break;
          }

          break;
        }
      }
    },
  },
  created() {},

  mounted() {},
  components: {},
};
</script>

<style scoped>
.graphParentContainer {
  width: 100%;
  height: 100%;
  background: #ffffff;
}
</style>