<template>
  <div id="editorMap" ref="mapRef"></div>
</template>

<script lang="ts" setup>
  import Konva from 'konva';
  import { ref, onMounted, onUnmounted } from 'vue';
  import { GROUP_NAME, POLYGON_NAME, Points, ToolObjectItem, toolObject } from './constants';
  import { ElMessage } from 'element-plus';
  import { getDefaultScale } from './utils';
  import { Group } from 'konva/lib/Group';

  const mapRef = ref<HTMLCanvasElement | null>(null);
  let currentTool: ToolObjectItem = toolObject[1];

  let isEdit = false;

  let stage: Konva.Stage | null = null;
  let layer: Konva.Layer | null = null;
  let currentDrawingShape: Konva.Group | null = null; //现在绘画的图形
  let polygonPoints: number[] = []; //存储绘画多边形各个顶点的数组
  let stageWidth = 0; //舞台宽
  let stageHeight = 0; //舞台高
  let scale = 1; //窗口变化的缩放比例
  let drawing = false; //一开始不能绘画
  let currentDel: Konva.Node | null = null; //删除对象

  onMounted(() => {
    initKonvaStage();
    //禁止浏览器右击菜单
    document.oncontextmenu = function () {
      return false;
    };
  });

  onUnmounted(() => {
    stage?.destroy();
  });

  /**
   *初始化konva舞台
   */
  function initKonvaStage() {
    //1实例化stage层
    stageWidth = mapRef.value?.clientWidth || 0;
    stageHeight = mapRef.value?.clientHeight || 0;
    console.log('stageWidth', stageWidth);
    stage = new Konva.Stage({
      container: 'editorMap',
      width: stageWidth,
      height: stageHeight,
      ignoreStroke: true,
      background: '#00ff00',
    });
    if (import.meta.env.MODE === 'development') {
      window.stage = stage;
    }
    setStageCursor('pointer');

    //2实例化layer层
    layer = new Konva.Layer();
    //3添加layer层
    stage?.add(layer);

    //给***舞台***绑定事件
    stageBindEvent();
  }
  /**
   * 舞台绑定的事件
   * @param vc_this
   */
  function stageBindEvent() {
    //鼠标按下
    stage?.on('mousedown', (e) => {
      //鼠标左键开始
      console.log('stage mousedown', e);
      if (!isEdit) return;
      if (e.evt.button == 0) {
        if (e.target === stage) {
          stageMousedown(currentTool!);
          return;
        }
        //图形起始点只能在图片层上
        // if (e.target === shape) {
        //   //开始初始绘画
        //   stageMousedown(currentTool!);
        //   return;
        // }
        //允许后续点绘画在其他图形上
        if (drawing) {
          stageMousedown(currentTool!);
          return;
        }
      } else if (e.evt.button == 2) {
        // 如果polygonPoints为空，那么右击是没有反应的。
        console.log('mousedown 2');
      }
    });
    //鼠标移动
    stage?.on('mousemove', () => {
      if (!isEdit) return;
      if (currentTool && drawing) {
        //绘画中
        stageMousemove();
      }
    });
    //鼠标放开
    stage?.on('mouseup', (e: Konva.KonvaEventObject<any>) => {
      if (!isEdit) return;

      if (e.evt.button == 0) {
        if (drawing) {
          stageMouseup(e);
        }
      } else if (e.evt.button == 2) {
        if (polygonPoints.length != 0) {
          stageMouseup(e);
        }
      }
    });
    //   //鼠标滚轮事件取消

    //舞台快捷键
    var container = stage?.container();
    if (!container) return;
    container.tabIndex = 1;
    container?.focus();
    container?.addEventListener('keydown', (e) => {
      if (!isEdit) return;

      //删除的快捷键
      if (e.keyCode === 46) {
        removeCurrent();
      }
      if (container) {
        container.style.cursor = 'crosshair';
      }
      e.preventDefault();
      layer?.draw();
    });
  }

  const getPolygonInGroup = (g: Konva.Group | null) => {
    if (!g) return;
    return g.findOne(POLYGON_NAME) as Konva.Line;
  };

  /** 设置当前选中的group */
  function setCurrentGroup(group: Konva.Group | null) {
    currentDrawingShape = group;
    setGroupActive(group);
    currentDel = group;
  }

  /**
   * 在舞台上鼠标点下发生的事件
   * @param currentTool 当前选择的工具
   * @param e 传入的event对象
   */
  function stageMousedown(currentTool: ToolObjectItem) {
    if (!isEdit) return;

    console.log('stagemousedown');

    //如果数组长度小于2，初始化多边形和顶点，使它们成为一组,否则什么都不做
    // 小于2说明一个点也没有，一个点的长度是2，所以要先创建group
    if (polygonPoints.length < 2) {
      //最好使用konva提供的鼠标xy点坐标
      var mousePos = stage?.getPointerPosition();
      console.log('mousePos', mousePos);
      if (!mousePos) return;
      //考虑鼠标缩放
      var x = (mousePos.x / scale - layer?.getAttr('x')) / getDefaultScale(layer?.scaleX()),
        y = (mousePos.y / scale - layer?.getAttr('y')) / getDefaultScale(layer?.scaleY());
      //拖拽组
      var group = new Konva.Group({
        name: currentTool.name + 'group',
        draggable: false,
      });
      polygonPoints = [x, y];
      //添加多边形的点
      drawCircle(x, y, group, polygonPoints);

      //绘画多边形
      drawPolygon(currentTool, polygonPoints, group);
      //添加多边形的边
      // drawLine(currentTool, polygonPoints, group);
      layer?.add(group);
      setCurrentGroup(group);
      currentDel = group;

      //使所有顶点在顶层显示
      stage?.find('Circle').forEach((element) => {
        element.moveToTop();
      });
      layer?.draw();
    } //多边形增加顶点
    else {
      //最好使用konva提供的鼠标xy点坐标
      var mousePos = stage?.getPointerPosition();
      if (!mousePos) return;
      //考虑鼠标缩放
      var x = (mousePos.x / scale - layer?.getAttr('x')) / getDefaultScale(layer?.scaleX()),
        y = (mousePos.y / scale - layer?.getAttr('y')) / getDefaultScale(layer?.scaleY());
      //group继续添加多边形的点
      drawCircle(x, y, currentDrawingShape!, polygonPoints);
      polygonPoints.push(x);
      polygonPoints.push(y);

      const polygon = getPolygonInGroup(currentDrawingShape);
      currentDel = currentDrawingShape;
      //绘画多边形
      polygon?.setAttr('points', polygonPoints);
      //group继续添加多边形的边
      //使所有顶点在顶层显示
      stage?.find('Circle').forEach((element) => {
        element.moveToTop();
      });
      layer?.draw();
    }

    drawing = true;
  }
  /**
   * 鼠标在舞台上移动事件
   * @param currentTool 当前选择的工具
   * @param e 传入的event对象
   */
  function stageMousemove() {
    if (!isEdit) return;

    const container = stage?.container();
    if (!container) return;

    container.style.cursor = 'crosshair';
    //多边形初始化后，如果数组长度大于2，鼠标移动时，实时更新下一个点
    if (polygonPoints.length >= 2) {
      var mousePos = stage?.getPointerPosition();
      if (!mousePos) return;
      var x = (mousePos.x / scale - layer?.getAttr('x')) / getDefaultScale(layer?.scaleX()),
        y = (mousePos.y / scale - layer?.getAttr('y')) / getDefaultScale(layer?.scaleY());
      var tempPoints = polygonPoints.concat([]);
      tempPoints.push(x);
      tempPoints.push(y);

      const polygon = getPolygonInGroup(currentDrawingShape);
      //更新多边形
      polygon?.setAttr('points', tempPoints);
      //使所有顶点在顶层显示
      stage?.find('Circle').forEach((element) => {
        element.moveToTop();
      });
    }

    layer?.draw();
  }
  /**
   * 鼠标在舞台弹起
   * @param currentTool 当前选择的工具
   * @param e 传入的event对象
   */
  function stageMouseup(e: Konva.KonvaEventObject<any>) {
    if (!isEdit) return;

    if (e.evt.button == 2) {
      if (polygonPoints.length != 0) {
        //最好使用konva提供的鼠标xy点坐标
        var mousePos = stage?.getPointerPosition();
        if (!mousePos) return;
        //考虑鼠标缩放
        var x = (mousePos.x / scale - layer?.getAttr('x')) / getDefaultScale(layer?.scaleX()),
          y = (mousePos.y / scale - layer?.getAttr('y')) / getDefaultScale(layer?.scaleY());
        //group继续添加多边形的点
        // 右击和左击都要添加一个点
        drawCircle(x, y, currentDrawingShape!, polygonPoints);
        polygonPoints.push(x);
        polygonPoints.push(y);
        const polygon = getPolygonInGroup(currentDrawingShape);
        //绘画多边形
        polygon?.setAttr('points', polygonPoints);

        //判断是否是只有两个点的多边形，如果起点和终点相同，不允许绘画
        if (polygon?.points().length == 2 || polygon?.points().length == 4) {
          drawing = false;
          currentDrawingShape?.destroy();
          polygonPoints = [];
          ElMessage({
            message: '顶点数必须大于2个！',
            type: 'warning',
            center: true,
            duration: 1000,
          });
          return;
        }
        //右键弹起
        polygonPoints = [];
        // 停止事件冒泡
        e.cancelBubble = true;
        // 停止画多边形
        drawing = false;
      }
    }

    //使所有顶点在顶层显示
    stage?.find('Circle').forEach((element) => {
      element.moveToTop();
    });

    layer?.draw();
  }
  /**
   * 多边形圆形
   * @param //x x坐标
   * @param //y y坐标
   */
  function drawCircle(x: number, y: number, group: Konva.Group, shapePoints: number[]) {
    const circle = new Konva.Circle({
      name: currentTool.name + 'circle',
      x: x,
      y: y,
      radius: 8 / scale / getDefaultScale(layer?.scaleX()),
      visible: true, //是否显示
      fill: currentTool.anchorColor,
      stroke: currentTool.anchorColor,
      draggable: false,
      strokeWidth: 0.5,
      strokeScaleEnabled: false,
      //增加点击区域
      hitStrokeWidth: 12 / scale / getDefaultScale(layer?.scaleX()),
      //设置拖动区域，不能超过舞台大小
      dragBoundFunc: function (pos) {
        //左上角
        if (pos.x < 0 && pos.y < 0) {
          return {
            x: 0,
            y: 0,
          };
        } //左侧
        else if (pos.x <= 0 && 0 <= pos.y && pos.y <= (stage?.height() ?? 0)) {
          return {
            x: 0,
            y: pos.y,
          };
        }
        //左下角
        else if (pos.x < 0 && pos.y > (stage?.height() ?? 0)) {
          return {
            x: 0,
            y: stage?.height(),
          };
        } //下侧
        else if (0 <= pos.x && pos.x <= (stage?.width() ?? 0) && pos.y > (stage?.height() ?? 0)) {
          return {
            x: pos.x,
            y: stage?.height(),
          };
        } //右下角
        else if (pos.x > (stage?.width() ?? 0) && pos.y > (stage?.height() ?? 0)) {
          return {
            x: stage?.width(),
            y: stage?.height(),
          };
        }
        //右侧
        else if (pos.x > (stage?.width() ?? 0) && 0 <= pos.y && pos.y <= (stage?.height() ?? 0)) {
          return {
            x: stage?.width(),
            y: pos.y,
          };
        }
        //右上角
        else if (pos.x > (stage?.width() ?? 0) && pos.y < 0) {
          return {
            x: stage?.width(),
            y: 0,
          };
        } //上侧
        else if (0 <= pos.x && pos.x <= (stage?.width() ?? 0) && pos.y < 0) {
          return {
            x: pos.x,
            y: 0,
          };
        }
      },
    });
    group.add(circle);
    let xChange: number, yChange: number;
    circle.on('mouseover', () => {
      const c = stage?.container();
      if (!c) return;
      c.style.cursor = 'pointer';
    });
    circle.on('mousedown', (e) => {
      if (!isEdit) return;

      console.log('circle,');
      if (!drawing) {
        circle.draggable(true);
        //将现在绘画的对象改为group
        setCurrentGroup(circle.getParent() as Konva.Group);
      } else {
        circle.draggable(false);
      }
      e.cancelBubble = true;
    });
    circle.on('mouseleave', () => {
      if (!isEdit) return;

      const c = stage?.container();
      if (!c) return;
      c.style.cursor = 'crosshair';
    });
    circle.on('dragstart', () => {
      if (!isEdit) return;

      switch (currentTool?.type) {
        case 'poly':
          //查找拖拽了多边形的哪个点
          for (var i = 0; i < shapePoints.length; i += 2) {
            if (
              circle.getAttr('x') == shapePoints[i] &&
              circle.getAttr('y') == shapePoints[i + 1]
            ) {
              xChange = i;
              yChange = i + 1;
              break;
            }
          }
          break;
        default:
          break;
      }
    });
    circle.on('dragmove', () => {
      if (!isEdit) return;

      switch (currentTool?.type) {
        case 'poly':
          var x = circle.x();
          var y = circle.y();
          //更改拖拽点的位置
          shapePoints[xChange] = x;
          shapePoints[yChange] = y;
          break;
        default:
          break;
      }
    });
    circle.on('dragend', (e) => {
      if (!isEdit) return;

      switch (currentTool?.type) {
        case 'poly':
          //使所有顶点在顶层显示
          stage?.find('Circle').forEach((element) => {
            element.moveToTop();
          });
          circle.draggable(false);
          break;
        default:
          break;
      }
      // ElMessage({
      //   message: '修改成功！',
      //   type: 'success',
      //   center: true,
      //   duration: 1000,
      // });
      e.cancelBubble = true;
    });
    return circle;
  }

  function setStageCursor(cursor: string) {
    const c = stage?.container();
    if (c) {
      c.style.cursor = cursor;
    }
  }

  /** 设置当前的group为active */
  function setGroupActive(activeGroup: Group | null) {
    const groups = stage?.find(GROUP_NAME) as Konva.Group[];
    if (!groups) return;
    /** 将其他组的线条设为非高亮 */
    groups.forEach((g: Konva.Group) => {
      if (g === activeGroup) return;
      g.find(POLYGON_NAME).forEach((line) => {
        (line as Konva.Line).stroke(currentTool?.color!);
      });

      g.find('Circle').forEach((circle) => {
        (circle as Konva.Circle).hide();
      });
    });
    if (!activeGroup) return;
    const thisLine = activeGroup.findOne(POLYGON_NAME) as Konva.Line;
    if (thisLine) {
      thisLine.stroke(currentTool.activeColor);
    }
    const thisCircles = activeGroup.find('Circle') as Konva.Circle[];
    if (thisCircles) {
      thisCircles.forEach((circle) => {
        (circle as Konva.Circle).show();
      });
    }
    layer?.draw();
  }
  /**
  *多边形
   @param currentTool
   * @param points 多边形绘画的各个顶点，类型数组
   */
  function drawPolygon(currentTool: ToolObjectItem, points: number[], group: Konva.Group) {
    let poly = new Konva.Line({
      name: currentTool.name + 'poly',
      points: points,
      /*  fill: currentTool.color, */
      stroke: currentTool.color,
      strokeWidth: 3,
      draggable: false,
      opacity: 1,
      lineCap: 'round',
      lineJoin: 'round',
      closed: true,
      strokeScaleEnabled: false,
    });
    group.add(poly);
    setCurrentGroup(group);
    const pParent = group;
    pParent?.on('mouseleave', () => {
      if (!isEdit) return;

      const c = stage?.container();
      if (c) {
        c.style.cursor = 'crosshair';
      }
    });
    pParent?.on('mousedown', (e) => {
      if (!isEdit) return;

      console.log('group mouse down', e);
      if (e.evt.button == 0) {
        //绘画结束
        if (!drawing) {
          setStageCursor('move');
          //设置现在绘画节点的对象为该多边形和顶点的组
          // 如果要让顶点和多边形一起拖拽，必须设置，多边形不能被拖拽
          poly.setAttr('draggable', false);
          currentDrawingShape?.setAttr('draggable', true);
          //使所有顶点在顶层显示
          stage?.find('Circle').forEach((element) => {
            element.moveToTop();
          });
          pParent.moveToTop();
          //添加删除撤销对象
          currentDel = currentDrawingShape;
          setCurrentGroup(poly.getParent() as Konva.Group);

          layer?.draw();
        } else {
          setStageCursor('crosshair');
          poly.getParent()?.setAttr('draggable', false);
        }
      }
    });

    pParent?.on('dragend', () => {
      if (!isEdit) return;

      console.log('dragend');
      /** 这里可以把工具的类型用枚举值定义 */

      //使所有顶点在顶层显示
      stage?.find('Circle').forEach((element) => {
        element.moveToTop();
      });
      //添加删除撤销对象
      currentDel = currentDrawingShape;
      layer?.draw();
      /*   vc_setMaskData(); */

      // ElMessage({
      //   message: '修改成功！',
      //   type: 'success',
      //   center: true,
      //   duration: 1000,
      // });
      setStageCursor('crosshair');
      //设置组不能拖动
      currentDrawingShape?.setAttr('draggable', false);
    });
    return poly;
  }

  /** 根据json数据创建group */
  function createGroupByPoints(points: Points) {
    var group = new Konva.Group({
      name: currentTool.name + 'group',
      draggable: false,
    });
    //添加多边形的点

    //绘画多边形
    drawPolygon(currentTool, points, group);
    for (let i = 0; i < points.length; i += 2) {
      const x = points[i];
      const y = points[i + 1];
      drawCircle(x, y, group, points);
    }
    // group.setAttrs({ x: groupData.attrs.x, y: groupData.attrs.y })
    console.log('group', group);
    layer?.add(group);
    layer?.draw();
  }

  /** 画多条线 */
  function createLines(lines: Points[]) {
    lines.forEach((line) => {
      createGroupByPoints(line);
    });
  }

  const removeCurrent = () => {
    if (currentDel) {
      currentDel.destroy();
      currentDel = null;
      currentDrawingShape = null;
      // ElMessage.success({
      //   message: '删除成功！',
      //   center: true,
      //   duration: 1000,
      // });
    } else {
      ElMessage.warning({ message: '请选择要删除的电子围栏' });
    }
    layer?.draw();
  };

  const toObject = () => {
    const polyGroups = stage?.find('.polygroup');
    const gropuPoints = polyGroups?.map((item) => {
      const groupX = item.x();
      const groupY = item.y();

      const line = (item as Konva.Group).findOne((x: any) => x.className === 'Line') as Konva.Line;
      const points = line?.points();
      const newPoints: number[][] = [];
      /** 存到后端的时候，只给点的坐标信息，不会给group的位置信息，所以要将点的坐标加上group的位移，才是之后点的最终坐标 */
      for (let i = 0; i < points.length; i += 2) {
        newPoints.push([Math.floor(points[i] + groupX), Math.floor(points[i + 1] + groupY)]);
      }
      return newPoints;
    });
    return gropuPoints;
  };

  const initStageByJSON = (param: { width: number; height: number }) => {
    stage?.setAttrs({ width: param.width, height: param.height });
  };

  const toRawObject = () => {
    return stage?.toObject();
  };

  /** 退出编辑模式 */
  const exitEditMode = () => {
    setCurrentGroup(null);
    isEdit = false;
  };
  /** 进入编辑模式 */
  const setEditMode = () => {
    isEdit = true;
  };

  /** 清空所有元素 */
  const clear = () => {
    layer?.removeChildren();
  };

  const setScale = (scale: number) => {
    stage?.setAttr('scaleX', scale);
  };

  defineExpose({
    remove: removeCurrent,
    toObject,
    toRawObject,
    createLines,
    initStageByJSON,
    exitEditMode,
    setEditMode,
    clear,
    setScale,
  });
</script>

<style scoped>
  #editorMap {
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    z-index: 8;
  }
</style>
