<template>
  <div ref="svgDraw" id="konva-container" style="height: 100%"></div>
</template>

<script>
function base64ToBlob(base64) {
  const byteCharacters = atob(base64);
  const byteArrays = [];

  for (let offset = 0; offset < byteCharacters.length; offset += 512) {
    const slice = byteCharacters.slice(offset, offset + 512);
    const byteNumbers = new Array(slice.length);
    for (let i = 0; i < slice.length; i++) {
      byteNumbers[i] = slice.charCodeAt(i);
    }
    const byteArray = new Uint8Array(byteNumbers);
    byteArrays.push(byteArray);
  }
  return new Blob(byteArrays, { type: 'application/pdf' });
}

function createObjectURL(blob) {
  return URL.createObjectURL(blob);
}

function downloadPDF(base64, fileName) {
  const blob = base64ToBlob(base64);
  const url = createObjectURL(blob);
  const link = document.createElement('a');
  link.href = url;
  link.download = fileName || 'downloaded_pdf.pdf'; // 设置下载文件名，如果没有提供文件名，则默认为'downloaded_pdf.pdf'
  document.body.appendChild(link); // 添加到DOM树中以便触发下载
  link.click(); // 模拟点击下载
  document.body.removeChild(link); // 下载完成后移除元素
  URL.revokeObjectURL(url); // 释放URL对象
}
import Konva from 'konva';
import { Vector2 } from "three";
import DxfParser from 'dxf-parser';
import DxfParser2 from '@/utils/dxf-parser/DxfParser';
import jsPDF from 'jspdf';
import { formatEntity, handleFn, centering, _GenerateArcVertices, _GenerateBulgeVertices } from './components/entityFormatting';
export default {
  name: 'Fl2dcanvas',
  components: {},
  data() {
    return {
      bounds: null,
      bounds1: null,
    };
  },
  created() {
    this.konvaStage = null;
    this.konvaLayer = null;
    this.recordLayerConfig = {};
    this.activedSvgPan = {
      x: 0,
      y: 0,
      zoom: {
        x: 1,
        y: 1,
      },
    };
  },
  mounted() {
    //resize方法
    window.addEventListener('resize', () => {
      if (this.konvaStage) {
        const obj = document.getElementById('konva-container');
        this.konvaStage.setWidth(obj.clientWidth);
        this.konvaStage.setHeight(obj.clientHeight);
        this.konvaStage._resizeDOM();
      }
    });

    this.$nextTick(() => {
      this.konvaStage = new Konva.Stage({
        className: 'stage',
        container: 'konva-container',
        width: this.$refs.svgDraw.clientWidth,
        height: this.$refs.svgDraw.clientHeight,
        draggable: true,
      });
      //init layer
      this.konvaLayer = new Konva.Layer({
        name: 'konva-layer',
        x: 0,
        y: 0,
      });
      //设置清晰度
      this.konvaLayer.getCanvas().setPixelRatio(2);
      this.konvaStage.add(this.konvaLayer);
      this.konvaLayer.draw();
      var scaleBy = 1.1;
      //初始化缩放方法
      this.konvaStage.on('wheel', e => {
        console.log(e);
        e.evt.preventDefault();
        var oldScale = this.konvaStage.scaleX();
        var pointer = this.konvaStage.getPointerPosition();
        var mousePointTo = {
          x: (pointer.x - this.konvaStage.x()) / oldScale,
          y: (pointer.y - this.konvaStage.y()) / oldScale,
        };
        // how to scale? Zoom in? Or zoom out?
        let direction = e.evt.deltaY > 0 ? -1 : 1;
        if (e.evt.ctrlKey) {
          direction = -direction;
        }
        var newScale = direction > 0 ? oldScale * scaleBy : oldScale / scaleBy;
        this.scaleRatio = Math.ceil(newScale);
        // 判断比例尺百分比
        if (this.scaleRatio < 1 || this.scaleRatio >= 100) return;
        // 设置放大缩小多少倍
        this.konvaStage.scale({ x: newScale, y: newScale });
        var newPos = {
          x: pointer.x - mousePointTo.x * newScale,
          y: pointer.y - mousePointTo.y * newScale,
        };
        this.konvaStage.position(newPos);
      });
      //双击鼠标滚轮
      this.konvaStage.on('pointerdblclick', e => {
        /*
        this.konvaStage.scale({
          x: 1,
          y: 1,
        });
        let x=-parseFloat(this.bounds.minX);
        let y =-parseFloat(this.bounds.minY);

        this.konvaStage.setX(x);
        this.konvaStage.setY(y); // 750

        //算一下舞台中点
        let width=this.konvaStage.width();
        let height=this.konvaStage.height()
        let x1=width/2;
        let y1=height/2;

        //算包围盒宽高

        let boundsWidth=this.bounds.maxX-this.bounds.minX;
        let boundsHeight=Math.abs(Math.abs(this.bounds.maxY)-Math.abs(this.bounds.minY));

        let x2=x+x1-(boundsWidth/2);
        let y2=y+y1-(boundsHeight/2)



        this.konvaStage.setX(x2);
        this.konvaStage.setY(y2); // 750




*/

        const { scale, x, y } = this.calculateScaleAndPosition(
          this.bounds,
          this.konvaStage.width(),
          this.konvaStage.height(),
          0.05 // 5% 边距
        );

        this.konvaStage.position({ x: x, y: y });
        this.konvaStage.scale({ x: scale, y: scale });

        this.konvaStage.batchDraw();
      });
      this.konvaStage.on('click', e => {
        let target = this.konvaStage.mouseClickEndShape;
        this.$emit('leftClick', target ? target.attrs : null)
      });
    });
  },
  //销毁方法

  methods: {
    calculateScaleAndPosition(contentBounds, stageWidth, stageHeight, padding = 0.1) {
      // 1. 计算内容的实际宽高
      const contentWidth = contentBounds.maxX - contentBounds.minX;
      const contentHeight = contentBounds.maxY - contentBounds.minY;

      // 2. 处理空内容或零宽高的边界情况
      if (contentWidth <= 0 || contentHeight <= 0) {
        return {
          scale: 1,
          x: stageWidth / 2,
          y: stageHeight / 2,
        };
      }

      // 3. 计算可用区域（留出边距）
      const availableWidth = stageWidth * (1 - padding * 2);
      const availableHeight = stageHeight * (1 - padding * 2);

      // 4. 计算缩放比例（取较小的那个，确保完全显示）
      const scaleX = availableWidth / contentWidth;
      const scaleY = availableHeight / contentHeight;
      const scale = Math.min(scaleX, scaleY);

      // 5. 计算缩放后的内容尺寸
      const scaledWidth = contentWidth * scale;
      const scaledHeight = contentHeight * scale;

      // 6. 计算居中偏移量
      // 内容中心点在缩放后的位置
      const contentCenterX = (contentBounds.minX + contentBounds.maxX) / 2;
      const contentCenterY = (contentBounds.minY + contentBounds.maxY) / 2;

      // 画布中心点
      const stageCenterX = stageWidth / 2;
      const stageCenterY = stageHeight / 2;

      // 偏移量 = 画布中心 - 内容中心 * 缩放
      const x = stageCenterX - contentCenterX * scale;
      const y = stageCenterY - contentCenterY * scale;

      return { scale, x, y };
    },

    updateBounds(v) {
      if (this.bounds === null) {
        this.bounds = { minX: v.x, maxX: v.x, minY: v.y, maxY: v.y };
      } else {
        if (v.x < this.bounds.minX) {
          this.bounds.minX = v.x;
        } else if (v.x > this.bounds.maxX) {
          this.bounds.maxX = v.x;
        }
        if (v.y < this.bounds.minY) {
          this.bounds.minY = v.y;
        } else if (v.y > this.bounds.maxY) {
          this.bounds.maxY = v.y;
        }
      }
    },
    updateBounds1(v) {
      if (this.bounds1 === null) {
        this.bounds1 = { minX: v.x, maxX: v.x, minY: v.y, maxY: v.y };
      } else {
        if (v.x < this.bounds1.minX) {
          this.bounds1.minX = v.x;
        } else if (v.x > this.bounds1.maxX) {
          this.bounds1.maxX = v.x;
        }
        if (v.y < this.bounds1.minY) {
          this.bounds1.minY = v.y;
        } else if (v.y > this.bounds1.maxY) {
          this.bounds1.maxY = v.y;
        }
      }
    },
    //思路是计算居中包围盒，然后排除脏数据。
    loadDxf(data) {
      let _this = this;

      /* if(this.layer!=null){
                      let nodes=this.layer.find(".item");
                      for(let i=0;i<nodes.length;i++){
                        nodes[i].destroy();
                      }
                    }*/

      this.konvaLayer.destroyChildren();
      const parser = new DxfParser();
      let dxf = parser.parse(data);
      const parser2 = new DxfParser2();
      let dxf2 = parser2.parseSync(data);

      let hatchEntitys = dxf2.entities;
      // 处理HATCH部分
      for (let i = 0; i < hatchEntitys.length; i++) {
        let entity = hatchEntitys[i];
        let layerConfig = this.recordLayerConfig[entity.layer];
        let configParams =
          layerConfig && Object.keys(layerConfig).length > 0
            ? layerConfig
            : null;
        let color = configParams ? configParams.color : "#000";
        let visible = configParams ? configParams.visible : true;
        let name = entity.layer.replace(/\s*/g, "");
        if (entity.type == "HATCH") {
          let boundaryLoops = entity.boundaryLoops;
          for (let j = 0; j < boundaryLoops.length; j++) {
            let list = [];
            let edges = boundaryLoops[j].edges;
            if (edges != undefined) {
              for (let k = 0; k < edges.length; k++) {
                let entity = edges[k];
                if (entity.type == 3) {
                  let majorAxisEndPoint = entity.end;
                  let center = entity.start;
                  const xR = Math.sqrt(
                    majorAxisEndPoint.x * majorAxisEndPoint.x +
                      majorAxisEndPoint.y * majorAxisEndPoint.y
                  );
                  const axisRatio = entity.radius;
                  const yR = xR * axisRatio;
                  const rotation = Math.atan2(
                    majorAxisEndPoint.y,
                    majorAxisEndPoint.x
                  );

                  const arcVertices = _GenerateArcVertices({
                    vertices: [],
                    center,
                    radius: xR,
                    startAngle: parseFloat(entity.startAngle),
                    endAngle: parseFloat(entity.endAngle),
                    yRadius: yR,
                    ccwAngleDir: entity.isCcw,
                  });

                  if (rotation !== 0) {
                    //XXX should account angDir?
                    const cos = Math.cos(rotation);
                    const sin = Math.sin(rotation);
                    for (const v of arcVertices) {
                      const tx = v.x - center.x;
                      const ty = v.y - center.y;
                      /* Rotate the vertex around the ellipse center point. */
                      v.x = tx * cos - ty * sin + center.x;
                      v.y = tx * sin + ty * cos + center.y;
                    }
                  }
                  //let list = [];
                  for (let i = 0; i < arcVertices.length; i++) {
                    list.push(arcVertices[i].x);
                    list.push(-parseFloat(arcVertices[i].y));
                  }

                }
                if (entity.type == 1) {

                  list.push(entity.start.x);
                  list.push(-parseFloat(entity.start.y));
                  list.push(entity.end.x);
                  list.push(-parseFloat(entity.end.y));
                  //konvaLayer.add(line);
                }
                let line = new Konva.Line({
                  points: list,
                  name: name,
                  entityId: entity.layer,
                  strokeWidth: 0.2,
                  closed: true,
                  fill: color,
                  stroke: color,
                  customColor: color,
                  visible: visible,
                  shapeType: 'hatch'
                });
                this.konvaLayer.add(line);
              }
            } else {
              let boundaryLoops = entity.boundaryLoops;
              let loop = boundaryLoops[0];
              if (loop.type == 7) {
                let vertices = [];
                for (
                  let vtxIdx = 0;
                  vtxIdx < loop.polyline.vertices.length;
                  vtxIdx++
                ) {
                  const vtx = loop.polyline.vertices[vtxIdx];
                  if ((vtx.bulge ?? 0) == 0) {
                    vertices.push(new Vector2(vtx.x, vtx.y));
                  } else {
                    const prevVtx =
                      loop.polyline.vertices[
                        vtxIdx == 0
                          ? loop.polyline.vertices.length - 1
                          : vtxIdx - 1
                      ];
                    if ((prevVtx.bulge ?? 0) == 0) {
                      /* Start vertex is not produced by _GenerateBulgeVertices(). */
                      vertices.push(new Vector2(vtx.x, vtx.y));
                    }
                    const nextVtx =
                      loop.polyline.vertices[
                        vtxIdx == loop.polyline.vertices.length - 1
                          ? 0
                          : vtxIdx + 1
                      ];
                    vertices = _GenerateBulgeVertices(
                      [],
                      vtx,
                      nextVtx,
                      vtx.bulge
                    );
                  }
                }
                let list = [];
                for (let i = 0; i < vertices.length; i++) {
                  list.push(vertices[i].x);
                  list.push(-parseFloat(vertices[i].y));
                }

                let line = new Konva.Line({
                  points: list,
                  strokeWidth: 0.2,
                  name: name,
                  entityId: entity.layer,
                  closed: true,
                  fill: color,
                  stroke: color,
                  customColor: color,
                  visible: visible,
                  shapeType: 'hatch'
                });
                this.konvaLayer.add(line);
              }
            }
          }
        }
      }
      //加载图纸
      let entities = formatEntity(dxf.entities);
      for (let key in entities) {
        if (entities[key]) {
          let group = [];
          let l = entities[key].length;
          let layerConfig = this.recordLayerConfig[key];
          let configParams =
            layerConfig && Object.keys(layerConfig).length > 0 ? layerConfig : null;
          for (let i = 0; i < l; i++) {
            let type = entities[key][i].type;
            handleFn(
              type,
              dxf,
              entities[key][i],
              group,
              key,
              configParams,
              this.konvaLayer,
              this.recordLayerConfig
            );
          }
          if (!this.konvaLayer) return;
          for (let i = 0; i < group.length; i++) {
            let entity = group[i];
            if (entity.type === 'text') {
              if (this.konvaLayer) this.konvaLayer.add(entity.obj);
            }
          }

          if (group[0].stroke == undefined) {
            console.log(group);
            group[0].stroke = '#000';
          }
          let customShape = new Konva.Shape({
            x: 0,
            y: 0,
            strokeWidth: 0.2,
            name: key.replace(/\s*/g, ''),
            entityId: key,
            stroke: group[0].stroke,
            customColor: configParams ? configParams.color : '',
            //visible: configParams ? configParams.visible :true,
            sceneFunc(context, shape) {
              context.beginPath();
              for (let i = 0; i < group.length; i++) {
                let entity = group[i];

                if (entity.type === 'line') {
                  context.strokeStyle = entity.stroke;
                  let x1 = entity.x1;
                  let y1 = entity.y1;
                  let x2 = entity.x2;
                  let y2 = entity.y2;
                  if (x1 != undefined && y1 != undefined && x2 != undefined && y2 != undefined) {
                    context.moveTo(x1, -parseFloat(y1));
                    context.lineTo(x2, -parseFloat(y2));
                  }
                  let v1 = {};
                  v1.x = x1;
                  v1.y = y1;
                  let v2 = {};
                  v2.x = x2;
                  v2.y = y2;
                  _this.updateBounds(v1);
                  _this.updateBounds(v2);
                }
                if (entity.type === 'polyline') {
                  context.strokeStyle = entity.stroke;

                  let flag = false;

                  let points = entity.points;

                  let closed = entity.closed;

                  for (let j = 0; j < points.length; j++) {
                    let index = j % 2;
                    if (index === 0) {
                      let x = points[j];
                      let y = points[j + 1];
                      let point = {};
                      point.x = x;
                      point.y = y;
                      _this.updateBounds(point);

                      if (j === 0) {
                        context.moveTo(x, y);
                      } else {
                        context.lineTo(x, y);
                      }
                    }

                    if (closed) {
                      if (j === points.length - 1) {
                        context.lineTo(points[0], points[1]);
                      }
                    }
                  }
                }
              }
              // context.closePath();
              context.fillStrokeShape(shape);
            },
          });
          if (this.konvaLayer) this.konvaLayer.add(customShape);
        }
      }
      //缩放视口
      let scale = 1;
      const boundingRect = this.konvaLayer.getClientRect();
      let ratio = Number((this.$refs.svgDraw?.clientWidth / boundingRect.width).toFixed(1));
      if (ratio > 2) {
        scale = Number((boundingRect.width / boundingRect.height).toFixed(1));
        scale = scale < 2 ? 2 : scale;
      }
      // this.konvaStage.scale({
      //  x: scale,
      // y: scale,
      //})
      const boundingScale = this.konvaLayer.getClientRect();
      //平移视口
      const x =
        this.$refs.svgDraw?.clientWidth / 2 -
        (Math.ceil(boundingScale.width) / 2 + boundingScale.x / 2);
      const y =
        this.$refs.svgDraw.clientHeight / 2 -
        (Math.ceil(boundingScale.height) / 2 + boundingScale.y);

      this.konvaStage.scale({
        x: 2,
        y: 2,
      });
      this.konvaStage.setX(0);
      this.konvaStage.setY(750); // 750

      this.activedSvgPan.zoom.x = 0;
      this.activedSvgPan.zoom.y = 750;
      this.activedSvgPan.x = x;
      this.activedSvgPan.y = x;
      setTimeout(() => {
        this.$emit('loaded');
      }, 100);
    },
    toPdf(name) {
      //this.konvaStage.scale({
      //x: this.activedSvgPan.zoom.x,
      //y: this.activedSvgPan.zoom.y,
      //});
      //this.konvaStage.setX(this.activedSvgPan.x);
      //this.konvaStage.setY(this.activedSvgPan.y);
      // get data URL with default settings

      // open in new window
      this.konvaLayer.getCanvas().setPixelRatio(2);
      const jpegURL = this.konvaStage.toDataURL({
        mimeType: 'image/jpeg',
        quality: 1,
        pixelRatio: 2,
      });

      let pageData = this.konvaLayer.canvas.toDataURL('image/jpeg', 2);

      let img = new Image();
      img.src = jpegURL;
      const pdf = new jsPDF('l', 'px', [this.konvaStage.width(), this.konvaStage.height()]);

      pdf.addImage(
        this.konvaStage.toDataURL({ pixelRatio: 2 }),
        0,
        0,
        this.konvaStage.width(),
        this.konvaStage.height()
      );

      pdf.save(name + '.pdf');
      // var imgData = canvas.toDataURL('image/png');
      //doc.text("Hello world!", 10, 10);
      //doc.save("a4.pdf");
    },
    toPdfs(list) {
      let _this = this;
      const pdf = new jsPDF('l', 'px', [this.konvaStage.width(), this.konvaStage.height()]);
      const exportPixelRatio = 2; // 可以 2/3/4 自己调

      for (let pageIdx = 0; pageIdx < list.length; pageIdx++) {
        let data = list[pageIdx];
        //先绘制图面
        this.konvaLayer.destroyChildren();
        const parser = new DxfParser();
        let dxf = parser.parse(data);
        let entities = formatEntity(dxf.entities);
        let layers = dxf.tables.layer.layers;

        //先计算包围
        for (let key in entities) {
          if (entities[key]) {
            let group = [];
            let l = entities[key].length;
            let layerConfig = this.recordLayerConfig[key];
            let configParams =
              layerConfig && Object.keys(layerConfig).length > 0 ? layerConfig : null;
            for (let i = 0; i < l; i++) {
              let type = entities[key][i].type;
              handleFn(
                type,
                dxf,
                entities[key][i],
                group,
                key,
                configParams,
                this.konvaLayer,
                this.recordLayerConfig
              );
            }

            for (let i = 0; i < group.length; i++) {
              let entity = group[i];
              if (entity.type === 'line') {
                let x1 = entity.x1;
                let y1 = entity.y1;
                let x2 = entity.x2;
                let y2 = entity.y2;
                let v1 = {};
                v1.x = x1;
                v1.y = y1;
                let v2 = {};
                v2.x = x2;
                v2.y = y2;
                _this.updateBounds1(v1);
                _this.updateBounds1(v2);
              }
              if (entity.type === 'polyline') {
                let flag = false;
                let points = entity.points;
                let closed = entity.closed;

                for (let j = 0; j < points.length; j++) {
                  let index = j % 2;
                  if (index === 0) {
                    let x = points[j];
                    let y = points[j + 1];
                    let point = {};
                    point.x = x;
                    point.y = y;
                    _this.updateBounds1(point);
                  }
                }
              }
            }
          }
        }

        console.log('bounds1');
        console.log(_this.bounds1);

        for (let key in entities) {
          if (entities[key]) {
            let group = [];
            let l = entities[key].length;
            let layerConfig = this.recordLayerConfig[key];
            let configParams =
              layerConfig && Object.keys(layerConfig).length > 0 ? layerConfig : null;
            for (let i = 0; i < l; i++) {
              let type = entities[key][i].type;
              handleFn(
                type,
                dxf,
                entities[key][i],
                group,
                key,
                configParams,
                this.konvaLayer,
                this.recordLayerConfig
              );
            }
            if (!this.konvaLayer) return;
            for (let i = 0; i < group.length; i++) {
              let entity = group[i];
              if (entity.type === 'text') {
                if (this.konvaLayer) this.konvaLayer.add(entity.obj);
              }
            }

            if (group[0].stroke == undefined) {
              console.log(group);
              group[0].stroke = '#000';
            }

            let customShape = new Konva.Shape({
              x: 0,
              y: 0,
              strokeWidth: 0.2,
              name: key.replace(/\s*/g, ''),
              entityId: key,
              stroke: group[0].stroke,
              customColor: configParams ? configParams.color : '',
              //visible: configParams ? configParams.visible :true,
              sceneFunc(context, shape) {
                context.beginPath();
                for (let i = 0; i < group.length; i++) {
                  let entity = group[i];

                  if (entity.type === 'line') {
                    context.strokeStyle = entity.stroke;
                    let x1 = entity.x1;
                    let y1 = entity.y1;
                    let x2 = entity.x2;
                    let y2 = entity.y2;
                    if (x1 != undefined && y1 != undefined && x2 != undefined && y2 != undefined) {
                      context.moveTo(x1, -parseFloat(y1));
                      context.lineTo(x2, -parseFloat(y2));
                    }
                  }
                  if (entity.type === 'polyline') {
                    context.strokeStyle = entity.stroke;

                    let flag = false;

                    let points = entity.points;
                    let closed = entity.closed;

                    for (let j = 0; j < points.length; j++) {
                      let index = j % 2;
                      if (index === 0) {
                        let x = points[j];
                        let y = points[j + 1];

                        if (j === 0) {
                          context.moveTo(x, y);
                        } else {
                          context.lineTo(x, y);
                        }
                      }

                      if (closed) {
                        if (j === points.length - 1) {
                          context.lineTo(points[0], points[1]);
                        }
                      }
                    }
                  }
                }
                // context.closePath();
                context.fillStrokeShape(shape);
              },
            });
            if (this.konvaLayer) this.konvaLayer.add(customShape);
          }
        }

        const { scale, x, y } = this.calculateScaleAndPosition(
          _this.bounds1,
          this.konvaStage.width(),
          this.konvaStage.height(),
          0.05 // 5% 边距
        );
        console.log('test1:');
        console.log(this.bounds1);

        _this.bounds1 = null;

        this.konvaStage.position({ x: x, y: y });
        this.konvaStage.scale({ x: scale, y: scale });

        this.konvaStage.batchDraw();
        //添加成功移动视口位置

        if (pageIdx == 0) {
          pdf.addImage(
            this.konvaStage.toDataURL({ pixelRatio: 2 }),
            0,
            0,
            this.konvaStage.width(),
            this.konvaStage.height()
          );
        } else {
          pdf.addPage();
          pdf.addImage(
            this.konvaStage.toDataURL({ pixelRatio: 2 }),
            0,
            0,
            this.konvaStage.width(),
            this.konvaStage.height()
          );
        }
      }
      pdf.save('批量导出.pdf');

      // var imgData = canvas.toDataURL('image/png');
      //doc.text("Hello world!", 10, 10);
      //doc.save("a4.pdf");
    },
    clearCache() {
      this.konvaLayer && this.konvaLayer.destroyChildren();
      this.konvaLayer && this.konvaLayer.destroy();
      this.konvaLayer && this.konvaLayer.clear();
      this.konvaStage && this.konvaStage.destroy();
      this.konvaStage && this.konvaStage.clearCache();
      this.konvaLayer = null;
      this.konvaStage = null;
    },
    beforeDestroy() {
      this.clearCache();
    },
  },
};
</script>
<style lang="scss" scoped>
#konva-container {
  z-index: 3;
  width: 100%;
  height: 100%;
  cursor: pointer;
  overflow: hidden;
}
</style>
