import G6 from '@antv/g6';
import uniqueId from 'lodash/uniqueId';

// 自定义边
export const customEdge = {
  init() {
    const lineDash = [4, 2, 1, 2];
    const interval = 9;

    G6.registerEdge(
      'customEdge',
      {
        /**
         * 绘制边，包含文本
         * @param  {Object} cfg 边的配置项
         * @param  {G.Group} group 图形分组，边中的图形对象的容器
         * @return {G.Shape} 绘制的图形，通过 node.get('keyShape') 可以获取到
         */
        draw(cfg, group) {
          // console.log('--cfg--', cfg)
          let sourceNode, targetNode, start, end
          if (typeof (cfg.source) === 'string') {
            cfg.source = cfg.sourceNode
          }
          if (!cfg.start) {
            cfg.start = {
              x: 85,
              y: 0
            }
          }
          if (!cfg.end) {
            cfg.end = {
              x: -85,
              y: 0
            }
          }
          if (!cfg.source.x) {
            sourceNode = cfg.sourceNode.getModel()
            start = { x: sourceNode.x + cfg.start.x, y: sourceNode.y + cfg.start.y }
          } else {
            start = cfg.source
          }
          if (typeof (cfg.target) === 'string') {
            cfg.target = cfg.targetNode
          }
          if (!cfg.target.x) {

            targetNode = cfg.targetNode.getModel()
            end = { x: targetNode.x + cfg.end.x, y: targetNode.y + cfg.end.y }
          } else {
            end = cfg.target
          }

          let path = []
          let hgap = Math.abs(end.x - start.x)
          if (end.x > start.x) {
            path = [
              ['M', start.x, start.y],
              [
                'C',
                start.x + hgap / (hgap / 50),
                start.y,
                end.x - hgap / (hgap / 50),
                end.y,
                end.x - 6,
                end.y
              ],
              [
                'L',
                end.x,
                end.y
              ]
            ]
          } else {
            path = [
              ['M', start.x, start.y],
              [
                'C',
                start.x,
                start.y,
                end.x,
                end.y,
                end.x - 6,
                end.y
              ],
              [
                'L',
                end.x,
                end.y
              ]
            ]
          }
          const keyShape = group.addShape('path', {
            attrs: {
              id: 'edge' + uniqueId(),
              path: path,
              stroke: '#b8c3ce',
              lineWidth: 1,
              lineAppendWidth: 8,
              endArrow: {
                fill: '#b8c3ce',
                path: G6.Arrow.vee(10, 15)
              }
            }
          });
          return keyShape
        },
        /**
         * 绘制后的附加操作，默认没有任何操作
         * @param  {Object} cfg 边的配置项
         * @param  {G.Group} group 图形分组，边中的图形对象的容器
         */
        afterDraw(cfg, group) {
          const shape = group.get('children')[0]; // 获取图形组中的第一个图形，在这里就是边的路径图形
          const length = shape.getTotalLength(); // G 增加了 totalLength 的接口
          const startPoint = shape.getPoint(0); // 获取路径图形的中点坐标
          let totalArray = [];
          for (var i = 0; i < length; i += interval) {
            totalArray = totalArray.concat(lineDash);
          }
          // 在中点增加一个圆形
          const circle = group.addShape('circle', {
            attrs: {
              x: startPoint.x,
              y: startPoint.y,
              fill: '#1890ff',
              r: 3,
            },
            name: 'circle-shape',
          });
          circle.animate(
            ratio => {
              const tmpPoint = shape.getPoint(ratio);
              return {
                x: tmpPoint.x,
                y: tmpPoint.y,
              };
            },
            {
              repeat: true,
              duration: 3000,
            },
          );
        },
        /**
         * 设置边的状态，主要是交互状态，业务状态请在 draw 方法中实现
         * 单图形的边仅考虑 selected、active 状态，有其他状态需求的用户自己复写这个方法
         * @param  {String} name 状态名称
         * @param  {Object} value 状态值
         * @param  {Edge} item 边
         */
        setState(name, value, item) {
          const group = item.getContainer();
          const shape = group.get("children")[0];
          const selectStyles = () => {
            shape.attr("stroke", "#1890ff");
            shape.attr("lineWidth", 2);
          };
          const unSelectStyles = () => {
            shape.attr("stroke", "#b8c3ce");
            shape.attr("lineWidth", 1);
          };
          switch (name) {
            case "selected":
            case "hover":
              if (value) {
                selectStyles()
              } else {
                unSelectStyles()
              }
              break;
          }
        }
      }
    );
    G6.registerEdge(
      'link-edge',
      {
        draw(cfg, group) {
          let sourceNode, targetNode, start, end
          if (!cfg.source.x) {
            sourceNode = cfg.sourceNode.getModel()
            start = { x: sourceNode.x + cfg.start.x, y: sourceNode.y + cfg.start.y }
          } else {
            start = cfg.source
          }
          if (!cfg.target.x) {
            targetNode = cfg.targetNode.getModel()
            end = { x: targetNode.x + cfg.end.x, y: targetNode.y + cfg.end.y }
          } else {
            end = cfg.target
          }
          let path = [
            ['M', start.x, start.y],
            ['L', end.x, end.y]
          ]
          const keyShape = group.addShape('path', {
            attrs: {
              id: 'edge' + uniqueId(),
              path: path,
              stroke: '#1890FF',
              strokeOpacity: 1,
            }
          });
          return keyShape
        },
      }
    );
  }
}

// 使用 DOM 自定义节点
export const customNode = {
  init() {
    G6.registerNode(
      "customNode",
      {
        /**
         * 绘制节点，包含文本
         * @param  {Object} cfg 节点的配置项
         * @param  {G.Group} group 图形分组，节点中图形对象的容器
         * @return {G.Shape} 返回一个绘制的图形作为 keyShape，通过 node.get('keyShape') 可以获取。
         * 关于 keyShape 可参考文档 核心概念-节点/边/Combo-图形 Shape 与 keyShape
         */
        draw(cfg, group) {
          // console.log('--cfg--', cfg)
          // 此处必须是number 不然bbox不正常
          const width = cfg.size && cfg.size[0] ? cfg.size[0] : 170;
          const height = cfg.size && cfg.size[1] ? cfg.size[1] : 80;
          const color = '#1890ff';
          // 此处必须有偏移 不然drag-node错位
          const offsetX = -width / 2
          const offsetY = -height / 2
          const nodeId = 'rect' + uniqueId(); // 节点id
    
          // 内置节点类型：circle圆形，rect矩形，ellipse椭圆，diamond菱形，triangle三角形，star星形，image图片，modelRect卡片，donut圆形
          const shape = group.addShape(
            "rect", // 矩形
            {
              attrs: {
                id: nodeId, // string, 节点唯一 ID
                x: offsetX, // number, 中心点 x 坐标
                y: offsetY, // number, 中心点 y 坐标
                width: width, // number, 节点宽度
                height: height, // number， 节点高度
                lineWidth: 1, // number, 描边粗细
                stroke: "rgb(240,240,240)", // string, 描边颜色
                fill: '#fff', // string, 填充色
                fillOpacity: 1, // number, 透明度
                radius: 4, // number, 圆角半径
              },
              draggable: true // 设置 draggable 以允许响应鼠标的图拽事件
            }
          );
          if(cfg.icon) {
            group.addShape(
              "image", // 图片
              {
                attrs: {
                  x: offsetX + width / 2 - 10, // number, 中心点 x 坐标
                  y: offsetY + 10, // number, 中心点 y 坐标
                  width: 20, // 图片宽度
                  height: 20, // 图片高度
                  img: cfg.icon, // string, 图片URL地址
                  parent: nodeId // 父id
                },
                draggable: true // 设置 draggable 以允许响应鼠标的图拽事件
              }
            );
          }
          // 文本
          if (cfg.label.length < 15) {
            group.addShape(
              "text", // 文本
              {
                attrs: {
                  id: 'label' + uniqueId(),
                  x: offsetX + width / 2,
                  y: offsetY + height / 2 + 8,
                  text: cfg.label, // 文本
                  parent: nodeId, // 父id
                  textAlign: 'center', // 文本内容的当前对齐方式，center / end / left / right / start
                  fontWeight: 'bold', // 字体粗细
                  fontSize: 16, // 字体大小
                  textBaseline: 'middle', // 当前文本基线
                  fontVariant: 'normal', // 设置为小型大写字母字体
                  fontFamily: '-apple-system, BlinkMacSystemFont, Segoe UI,PingFang SC, Hiragino Sans GB, Microsoft YaHei, Helvetica Neue, Helvetica,Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol', // 字体系列
                  lineHeight: 'normal', // 行高
                  fill: "#565758" // 字体颜色
                },
                draggable: true // 设置 draggable 以允许响应鼠标的图拽事件
              }
            );
          } else {
            group.addShape(
              "text",
              {
                attrs: {
                  id: 'label' + uniqueId(),
                  x: offsetX + width / 2,
                  y: offsetY + height / 2 + 10,
                  textAlign: "center",
                  textBaseline: "middle",
                  text: cfg.label,
                  text: cfg.label.substring(0, 15),
                  parent: nodeId,
                  fontWeight: 'bold',
                  fontSize: 16,
                  fill: "#565758"
                },
                draggable: true
              }
            );
            group.addShape(
              "text",
              {
                attrs: {
                  id: 'label' + uniqueId(),
                  x: offsetX + width / 2,
                  y: offsetY + height / 2 + 22,
                  textAlign: "center",
                  textBaseline: "middle",
                  text: cfg.label,
                  text: cfg.label.substring(15, cfg.label.length),
                  parent: nodeId,
                  fontWeight: 'bold',
                  fontSize: 16,
                  fill: "#565758"
                },
                draggable: true
              }
            );
          }
          // 提示图标
          if (cfg.status==='error') {
            group.addShape(
              'text',
              {
                attrs: {
                  x: offsetX + width - 19,
                  y: offsetY + height + 5,
                  text: '⚠️',
                  fill: '#000',
                  fontSize: 18,
                },
                name: 'error-text-shape',
                draggable: true
              }
            );
          }
          let inPoints = [], outPoints = [];
    
          switch (cfg.point) {
            case 'start':
              outPoints = [[0.5, 1]]
              break;
            case 'control':
              inPoints = [[0.5, 0]]
              outPoints = [[0.5, 1]]
              break;
            case 'end':
              inPoints = [[0.5, 0]]
              break;
          }
          if (inPoints.length != 0) {
            for (let i = 0; i < inPoints.length; i++) {
              let x, y = 0;
              // 0为顶 1为底
              if (inPoints[i][0] === 0) {
                y = 0;
              } else if (inPoints[i][0] === 0.5) {
                y = height / 2;
              } else {
                y = height;
              }
              x = width * inPoints[i][1];
              const id = 'circle' + uniqueId()
    
              group.addShape("rect", {
                attrs: {
                  id: nodeId,
                  parent: id,
                  isInPointOut: true,
                  x: offsetX,
                  y: offsetY,
                  width: width,
                  height: height,
                  stroke: "rgb(240,240,240)",
                  fill: '#fff',
                  fillOpacity: 0,
                  radius: 4,
                },
                draggable: true
              });
    
              group.addShape("circle", {
                attrs: {
                  id: 'circle' + uniqueId(),
                  parent: id,
                  x: x + offsetX,
                  y: y + offsetY,
                  r: 10,
                  isInPointOut: true,
                  fill: "#1890ff",
                  opacity: 0
                }
              });
              group.addShape("circle", {
                attrs: {
                  id: id,
                  x: x + offsetX,
                  y: y + offsetY,
                  r: 7,
                  isInPoint: true,
                  fill: "#fff",
                  stroke: "#1890ff",
                  opacity: 0
                }
              });
            }
          }
          
          if (outPoints.length != 0) {
            for (let i = 0; i < outPoints.length; i++) {
              let x,
                y = 0;
              //0为顶 1为底
              if (outPoints[i][0] === 0) {
                y = 0;
              } else if (outPoints[i][0] === 0.5) {
                y = height / 2;
              } else {
                y = height;
              }
              x = width * outPoints[i][1];
              const id = 'circle' + uniqueId()
              group.addShape("circle", {
                attrs: {
                  id: 'circle' + uniqueId(),
                  parent: id,
                  x: x + offsetX,
                  y: y + offsetY,
                  r: 10,
                  isOutPointOut: true,
                  fill: "#1890ff",
                  opacity: 0//默认0 需要時改成0.3
                }
              });
              group.addShape("circle", {
                attrs: {
                  id: id,
                  x: x + offsetX,
                  y: y + offsetY,
                  r: 7,
                  isOutPoint: true,
                  fill: "#fff",
                  stroke: "#1890ff",
                  opacity: 0
                }
              });
            }
          }
          return shape;
        },
        /**
         * 响应节点的状态变化。
         * 在需要使用动画来响应状态变化时需要被复写，其他样式的响应参见下文提及的 [配置状态样式] 文档
         * @param  {String} name 状态名称
         * @param  {Object} value 状态值
         * @param  {Node} item 节点
         */
        setState(name, value, item) {
          const group = item.getContainer();
          const shape = group.get("children")[0]; // 顺序根据 draw 时确定
    
          const children = group.findAll(g => {
            return g.attrs.parent === shape.attrs.id
          });
          const circles = group.findAll(circle => {
            return circle.attrs.isInPoint || circle.attrs.isOutPoint;
          });
          const selectStyles = () => {
            shape.attr("fill", "#f3f9ff");
            shape.attr("stroke", "#6ab7ff");
            shape.attr("cursor", "move");
            children.forEach(child => {
              child.attr("cursor", "move");
            });
            circles.forEach(circle => {
              circle.attr('opacity', 1)
            })
          };
          const unSelectStyles = () => {
            shape.attr("fill", "#fff");
            shape.attr("stroke", "#ced4d9");
            circles.forEach(circle => {
              circle.attr('opacity', 0)
            })
          };
          switch (name) {
            case "selected":
            case "hover":
              if (value) {
                selectStyles()
              } else {
                unSelectStyles()
              }
              break;
          }
        }
      }
    );
  }
}

// g6图绘制
var graph = null;
export function initg6(g6Data) {
  graph = null;
  const warp = document.getElementById('graph-container');
  if(!warp)return;
  const height = warp ? warp.offsetHeight - 40 || 500 : 500;
  const width = warp ? warp.clientWidth || 1000 : 1000;

  // 创建 G6 图实例
  graph = new G6.Graph({
    container: 'graph-container', // String | HTMLElement，必须，指定图画布的容器 id 或容器本身
    // 画布宽高
    height: height, // Number，必须，图的宽度
    width: width, // Number，必须，图的高度
    // fitView : true, // 图是否自适应画布
    // fitViewPadding: 0, // Number | Array；默认：0。图自适应画布时的四周留白像素值。fitView 为 true 时生效
    // fitCenter: true, // 是否平移图使其中心对齐到画布中心
    modes: {
      // 支持的 behavior
      default: [
        'brush-select',
        'drag-canvas',
        'zoom-canvas',
        'drag-node',
        'hover-node',
        'select-node',
        'hover-edge',

        // 'keyboard',
        // 'customer-events',
        'add-menu',
        {
          type: 'tooltip',
          formatText(model) {
            const cfg = model['label']
            return cfg
          },
          shouldUpdate: (e) => {
            if (e.target.cfg.type !== 'circle') return true
          },
        },
      ],
      altSelect: [
        {
          type: 'brush-select',
          trigger: 'drag',
        },
        'drag-node',
      ],
      mulitSelect: ['mulit-select'],
      addEdge: ['add-edge'],
    },
    // 默认情况下全局节点的配置项，包括样式属性和其他属性
    defaultNode: {
      type: 'customNode',
      size: [170, 80], // 节点宽高
    },
    // 默认情况下全局边的配置项，包括样式属性和其他属性
    defaultEdge: {
      type: 'customEdge',
    }
  })
  graph.data(g6Data); // 读取数据源到图上
  graph.render(); // 渲染图

  // 点击时选中，再点击时取消
  graph.on('node:click', (ev) => {
    const node = ev.item;
    graph.setItemState(node, 'selected', !node.hasState('selected')); // 切换选中
  });
  graph.on('node:mouseenter', (ev) => {
    const node = ev.item;
    graph.setItemState(node, 'selected', true);
  });
  graph.on('node:mouseleave', (ev) => {
    const node = ev.item;
    graph.setItemState(node, 'selected', false);
  });
  graph.on('edge:mouseenter', (ev) => {
    const edge = ev.item;
    graph.setItemState(edge, 'selected', true);
  });
  graph.on('edge:mouseleave', (ev) => {
    const edge = ev.item;
    graph.setItemState(edge, 'selected', false);
  });
}

