<template>
  <div class="baseTopo" id="baseTopo" ref="baseTopo">
    <topoTooltip
      v-show="showTooltip"
      class="topoTooltip-box"
      ref="topoTooltip"
      @closeTooltip="showTooltip = false"
      :havItem="havItem"
      :havEquipItem="havEquipItem"
      :isLock="isLock"
    ></topoTooltip>
    <canvas
      style="border: none; outline: medium"
      id="persagy_topo"
      :width="canvasWidth"
      :height="canvasHeight"
      tabindex="0"
    ></canvas>
    <zoom @sacle="changeSize" :scale="viewScale" class="zoom"></zoom>
  </div>
</template>
<script>
import {
  PTopoScene,
  PTopoParser,
  PTopoView,
} from "@/components/editClass/persagy-edit";
import topoTooltip from "./topoTooltip.vue";
import zoom from "./zoom";
import { mapState, mapMutations } from "vuex";
import base64ToFile from "@/utils/base64ToFile";
import { v1 as uuidv1 } from "uuid";
import bus from "@/bus/bus";
import axios from "axios";
import {
  saveGroup,
  readGroup,
  uploadGroup,
  getImageGroup,
  readPubGroup,
} from "@/api/editer";
import { publishGraph } from "@/api/home";
import crypto from "crypto-js/";
//////////////////////////////////////
// 常量
// 图服务路径
const imgBaseUrl = window.__systemConf.imgServeUri;
// 图上传路径
const imgServeUpload = window.__systemConf.imgServeUpload;
// 默认通用图标
const default_Eq = window.__systemConf.default_Eq;
// 联通方式图标
const uninType = window.__systemConf.uninType;
export default {
  components: { topoTooltip, zoom },
  data() {
    return {
      scene: null, //场景
      view: null, //视图
      canvasWidth: 700, //画布宽
      canvasHeight: 700, //画布高
      havItem: false, //右击是否选中item
      havEquipItem: false, //右击是否为设备
      showTooltip: false, //是否显示tooltip
      topoContent: {}, // 读图后存储图所有数据
      autoSave: null, // 自动保存定时器
      isLock: false, //右键item是否锁定
      viewScale: 0, //视图缩放比例
    };
  },
  computed: {
    ...mapState([
      "editCmd",
      "legendObj",
      "graphId",
      "id",
      "isPub",
      "categoryId",
      "projectId",
      "version",
    ]),
  },
  mounted() {
    this.canvasWidth = this.$refs.baseTopo.offsetWidth;
    this.canvasHeight = this.$refs.baseTopo.offsetHeight - 10;
    this.scene = new PTopoScene();
    this.scene.imgServeUrl = imgBaseUrl; //获取图服务路径
    this.scene.uninType = uninType; //联通方式
    this.view = new PTopoView("persagy_topo");
    this.view.scene = this.scene;
    this.view.vueOnMouseWheel = this.vueOnMouseWheel;
    this.scene.clearCmdStatus = this.clearCmdStatus;
    this.viewScale = this.view.scale;
    // 初始化bus绑定事件
    this.initBusEvent();
    // 右键事件
    this.scene.getItem = this.onContextMenu;
    this.scene.emitChoice = this.emitChoice;
    //左键事件
    this.scene.vueOnMouseDown = this.vueOnMouseDown;
    // 屏蔽浏览器右键功能（防止与编辑器右键交互重合）
    document.getElementById("baseTopo").oncontextmenu = function (e) {
      return false;
    };
    // 读取底图
    this.readtopoMsg();
    // 2分钟自动保存
    this.autoSave = setInterval(() => {
      this.autoSaveTopo();
    }, 120000);
  },
  methods: {
    ...mapMutations([
      "SETCHOICELEHEND",
      "SETLEGENDOBJ",
      "SETPROJECT",
      "SETCATEGROY",
      "SETISPUB",
      "ADDEQUIPITEM",
      "EDITEQUIPITEM",
      "SETVERSION",
      "SETVBACKGROUND",
    ]),
    // 恢复命令状态
    clearCmdStatus() {
      this.SETCHOICELEHEND("");
      this.SETLEGENDOBJ(null);
    },
    // 右键获取item
    onContextMenu(item, [event]) {
      this.showTooltip = true;
      if (item) {
        if (item.moveable) {
          this.isLock = false;
        } else {
          this.isLock = true;
        }
        if (item.legendData?.properties.type == "BaseEquipment") {
          this.havEquipItem = true;
        } else {
          this.havEquipItem = false;
        }
        this.havItem = true;
      } else {
        this.havItem = false;
        this.havEquipItem = false;
      }
      const doms = document.getElementsByClassName("topoTooltip-box")[0];
      doms.style.left = event.offsetX + "px";
      doms.style.top = event.offsetY + "px";
    },
    // 左键事键
    vueOnMouseDown(e) {
      //  关闭tooltip
      this.showTooltip = false;
    },
    // 选中后的回调函数
    emitChoice(itemList) {
      bus.$emit("emitChoice", itemList);
    },
    //初始化bus绑定事件
    initBusEvent() {
      // 改变样式
      bus.$off("updateStyle");
      bus.$on("updateStyle", (type, val) => {
        this.scene.updateStyle(type, val);
      });
      // 撤销
      bus.$off("topoUndo");
      bus.$on("topoUndo", (val) => {
        this.scene.undo();
      });
      // 重做
      bus.$off("topoRedo");
      bus.$on("topoRedo", (val) => {
        this.scene.redo();
      });
      // 删除
      bus.$off("deleteItem");
      bus.$on("deleteItem", (val) => {
        this.scene.deleteItem([val]);
        this.EDITEQUIPITEM();
      });
      // 复制
      bus.$off("copy");
      bus.$on("copy", (val) => {
        this.scene.copy();
      });
      // 粘贴
      bus.$off("paste");
      bus.$on("paste", (val) => {
        this.scene.paste();
      });
      // 保存
      bus.$off("saveTopo");
      bus.$on("saveTopo", (val) => {
        this.saveTopoDraft();
      });
      // 设置实例置顶置底
      bus.$off("setOrder");
      bus.$on("setOrder", (val) => {
        this.scene.setOrder(val);
      });
      // 设置实例status状态
      bus.$off("setItemStatus");
      bus.$on("setItemStatus", (val) => {
        this.scene.setItemStatus();
      });
      // 下载图片
      bus.$off("saveTopoImg");
      bus.$on("saveTopoImg", () => {
        // 隐藏选择器
        this.scene.selectContainer.clear();
        this.view.saveImageSize(
          `${this.topoContent.name}.png`,
          "png",
          this.canvasWidth,
          this.canvasHeight,
          this.view.backgroundColor.value
        );
      });
      // 发布图片
      bus.$off("issueTopo");
      bus.$on("issueTopo", () => {
        this.saveTopoDraft().then(() => {
          this.issueDraft();
        });
      });
      // 手动添加设备实例
      bus.$off("addEquipment");
      bus.$on("addEquipment", (val) => {
        this.addEquipmentList(val);
      });
      // 更改设备信息点
      bus.$off("changeEquipMsgPoint");
      bus.$on("changeEquipMsgPoint", (val) => {
        this.scene.changeEquipMsgPoint(val);
      });
      // 选中item
      bus.$off("chioceItem");
      bus.$on("chioceItem", (item) => {
        this.scene.toggleItem(item);
      });
      // 更改对齐方式
      bus.$off("changeAlignItem");
      bus.$on("changeAlignItem", (val) => {
        this.scene.changeAlignItem(val);
      });
      // 更改背景色
      bus.$off("changeBackgroundColor");
      bus.$on("changeBackgroundColor", (val) => {
        this.scene.changeBackgroundColor(val);
      });
    },
    // 读取拓扑图
    readtopoMsg() {
      const obj = {
        graphId: this.graphId,
        id: this.id,
      };
      if (this.isPub == 1) {
        // 已发布
        readPubGroup(obj).then((res) => {
          this.getDataSuc(res);
        });
      } else {
        readGroup(obj).then((res) => {
          this.getDataSuc(res);
        });
      }
    },
    // 读图成功回调
    getDataSuc(res) {
      let anchorList = []; //保存锚点对象
      if (res.result == "failure") return;
      this.SETCATEGROY(res.content);
      this.topoContent = res.content;
      const parse = new PTopoParser();
      if (this.scene) {
        const backgroundColor = res.content.viewBackground
          ? res.content.viewBackground
          : "#00000000";
        this.scene.changeBackgroundColor(backgroundColor);
        // 初始化保存图背景色
        this.SETVBACKGROUND(this.view.backgroundColor.toRgba());
      }
      parse.parseData(res.content.elements);
      parse.markers.forEach((item) => {
        item.selectable = true;
        item.moveable = true;
        item.connect("finishCreated", this.scene, this.scene.finishCreated);
        item.connect("onContextMenu", this, this.scene.getItem);
        // 判断如果是图//或是管道，需要拼接路径
        if (
          item.data.properties.type == "BaseImage" ||
          item.data.properties.type == "BasePipeUninTool"
        ) {
          if (item.data.style.default.url) {
            item.url = imgBaseUrl + item.data.style.default.url;
          }
        }
        this.scene.addItem(item);
      });
      parse.nodes.forEach((item) => {
        item.connect("finishCreated", this.scene, this.scene.finishCreated);
        item.connect("onContextMenu", this, this.scene.getItem);
        this.scene.addItem(item);
        // 设置url
        if (item.legendData.style.default.url) {
          item.url = imgBaseUrl + item.legendData.style.default.url;
        } else {
          item.url = imgBaseUrl + default_Eq;
        }
        // 如果为设备则存于vuex中便于联动
        if (item.legendData.properties.type == "BaseEquipment") {
          anchorList = anchorList.concat(item.anchorList);
          this.ADDEQUIPITEM(item);
        }
      });
      parse.relations.forEach((t) => {
        // 设置锚点
        if (this.scene.isAdsorb) {
          if (t.anchor1Id) {
            let startAnc = null;
            anchorList.forEach((aItem) => {
              if (aItem.id == t.anchor1Id) {
                startAnc = aItem;
              }
            });

            if (startAnc) {
              startAnc.isConnected = true;
              startAnc.parent?.connect("changePos", t, t.changePos);
              t.startAnchor = startAnc || null;
            }
          }
          if (t.anchor2Id) {
            let endAnc = null;
            anchorList.forEach((aItem) => {
              if (aItem.id == t.anchor2Id) {
                endAnc = aItem;
              }
            });
            if (endAnc) {
              endAnc.isConnected = true;
              endAnc.parent?.connect("changePos", t, t.changePos);
              t.endAnchor = endAnc || null;
            }
          }
        }

        t.connect("finishCreated", this.scene, this.scene.finishCreated);
        t.connect("onContextMenu", this, this.scene.getItem);
        this.scene.addItem(t);
      });
      // 读取完成后默认走保存一次
      this.saveTopoDraft();
    },
    // 生成快照并保存草稿
    saveTopoDraft() {
      const uuid = uuidv1();
      return Promise.all([this.generateSnap(uuid), this.saveDraft(uuid)]).then(
        (vals) => {
          // 重设版本号
          this.SETVERSION(vals[1].version);
          this.$message.success(`保存成功${vals[1].version}`);
        }
      );
    },
    // 生成快照
    generateSnap(uuid) {
      // 隐藏选择器
      this.scene.selectContainer.clear();
      setTimeout(() => {
        // base64数据
        const data = this.view.imageUrl("png");
        // 根据base64生成file
        const file = base64ToFile(data);
        const reader = new FileReader();
        const fileType = file.name.split(".");
        const imgType = fileType[fileType.length - 1];
        return new Promise((resolve, reject) => {
          reader.onloadend = function () {
            // 这个事件在读取结束后，无论成功或者失败都会触发
            if (reader.error) {
              console.log("reader error", reader.error);
              reject(reader.error);
            } else {
              // 构造 XMLHttpRequest 对象，发送文件 Binary 数据
              const xhr = new XMLHttpRequest();
              xhr.open("POST", `${imgServeUpload}${uuid}.${imgType}`);
              xhr.send(reader.result);
              xhr.onreadystatechange = function () {
                if (xhr.readyState == 4) {
                  if (xhr.status == 200) {
                    resolve(xhr);
                  }
                }
              };
            }
          };
          reader.readAsArrayBuffer(file);
        });
      }, 80);
    },
    // 保存草稿
    saveDraft(uuid) {
      const elements = this.scene.save();
      const obj = {
        elements,
        name: this.topoContent.name, // 名称
        categoryId: this.categoryId, // 图分类ID
        projectId: this.projectId, // 项目ID
        label: this.topoContent.label,
        buildingId: "1", // 建筑ID
        floorId: "1", // 楼层id
        note: "1", // 图说明
        pic: `${uuid}.png`,
        graphId: this.graphId,
        id: this.id,
        version: this.version,
        viewBackground: this.view.backgroundColor.value, //视图背景色
      };
      return new Promise((resolve, reject) => {
        saveGroup(obj).then((res) => {
          // 如果是从已发布跳转过来
          if (this.isPub == 1) {
            // 设置发布状态为 未发布
            this.SETISPUB(0);
            const gid = res.entityList[0].graphId;
            const id = res.entityList[0].id;
            // 重设图id 与 id
            this.SETPROJECT({ graphId: gid, id: id });
            // 修改url参数
            this.$router.push({
              name: "Editer",
              query: {
                graphId: gid,
                id: id,
                categoryName: encodeURI(this.categoryName),
                isPub: 0,
              },
            });
          }
          resolve(res.entityList[0]);
        });
      });
    },
    // 自动保存
    autoSaveTopo() {
      if (this.scene && this.scene.undoStack.isChange) {
        this.saveTopoDraft().then(() => {
          this.scene.undoStack.isChange = false;
        });
      }
    },
    // 发布草稿
    issueDraft() {
      const pa = {
        graphId: this.graphId,
        id: this.id,
      };
      publishGraph(pa).then((res) => {
        // 设置发布状态为 未发布
        this.SETISPUB(1);
        const gid = res.entityList[0].graphId;
        const id = res.entityList[0].id;
        // 重设图id 与 id
        this.SETPROJECT({ graphId: gid, id: id });
        // 修改url参数
        this.$router.push({
          name: "Editer",
          query: {
            graphId: gid,
            id: id,
            categoryName: encodeURI(this.categoryName),
            isPub: 1,
          },
        });
        this.$message.success("发布成功");
      });
    },
    // 手动添加设备
    addEquipmentList(list) {
      const parse = new PTopoParser();
      list.forEach((item, i) => {
        const x = (i + 1) * 100 + 300;

        const url = item.url;

        let svg2Base = "";
        let EquipHeight = this.canvasHeight - 100;
        // 拼接路径
        let data = {
          nodeId: uuidv1(),
          /** 名称 */
          name: "基础设备",
          /** 返回物理世界对象 ID 列表 */
          attachObjectIds: [item.id],
          size: { width: 50, height: 50 },
          /** 图标 (Image)，线类型 (Line) */
          type: "Image",
          /** 位置 */
          pos: { x: x, y: 100 },
          /** 由应用自己定义 */
          properties: {
            type: "BaseEquipment",
            classCode: item.classCode, // 设备类型
            localId: item.localId, // 本地编码
            localName: item.localName, //本地名称
            state: item.state,
          },
          style: {
            default: {
              strokecolor: "#c0ccda",
              url: url,
              base64Url: "",
            },
          },
        };
        parse.addNode(data);
      });
      // 添加到 scence 中
      parse.nodes.forEach((item) => {
        item.connect("finishCreated", this.scene, this.scene.finishCreated);
        item.connect("onContextMenu", this, this.scene.getItem);
        if (item.legendData.style.default.url) {
          item.url = imgBaseUrl + item.legendData.style.default.url;
          item.defaultUrl = item.legendData.style.default.url;
        } else {
          item.url = imgBaseUrl + default_Eq;
        }
        this.scene.addItem(item);
        // 如果为设备则存于vuex中便于联动
        if (item.legendData.properties.type == "BaseEquipment") {
          this.ADDEQUIPITEM(item);
        }
      });
    },
    // 图片缩小
    changeSize(isbiger) {
      if (isbiger) {
        this.view.scaleByPoint(
          this.view.scale + 0.1,
          this.canvasWidth / 2,
          this.canvasHeight / 2
        );
        this.viewScale = this.view.scale;
      } else {
        this.view.scaleByPoint(
          (this.view.scale * 0.9) / this.view.scale,
          this.canvasWidth / 2,
          this.canvasHeight / 2
        );
        this.viewScale = this.view.scale;
      }
    },
    // 滚轮滚动
    vueOnMouseWheel(e) {
      this.viewScale = this.view.scale;
    },
  },
  watch: {
    editCmd(val) {
      if (this.scene) {
        // 设置当前编辑状态
        this.scene.editCmd = val;
      }
    },
    legendObj: {
      handler: function (val, oldVal) {
        this.scene.legendObj = val;
      },
      deep: true,
    },
  },
  created() {
    this.SETPROJECT(this.$route.query);
    this.SETISPUB(this.$route.query.isPub);
    this.categoryName = decodeURI(this.$route.query.categoryName);
  },
  beforeDestroy() {
    clearInterval(this.autoSave);
  },
};
</script>
<style lang="less" scoped>
.baseTopo {
  width: 100%;
  height: 100%;
  position: relative;
  .topoTooltip-box {
    position: absolute;
    left: 0;
    top: 0;
  }
  .back-btn {
    position: absolute;
    left: 0;
    top: 20px;
  }
  .zoom {
    font-size: 14px;
    position: absolute;
    right: 24px;
    bottom: 22px;
  }
}
</style>