import React from 'react';
import G6 from '@antv/g6';

import circle from './circle.93e1.png'
import node from './node-bg-select.171e.png'

import './Relation.css'
import { EXPAND_ICON } from './Relation1';

const colors = [
  '#BDD2FD',
  '#C2C8D5',
  '#FBE5A2',
  '#F6C3B7',
  '#B6E3F5',
  '#D3C6EA',
  '#FFD8B8',
  '#AAD8D8',
  '#FFD6E7',
];

const highlight = '#ff7800'


class Relation extends React.Component {

  componentDidMount() {
    // console.debug(this.props?.data)
  }

  componentDidUpdate(prevProps, _prevState) {
    const { childData, parentNodeId } = this.props;
    if (parentNodeId && parentNodeId !== prevProps.parentNodeId) {
      const parentData = this.graph?.findDataById(parentNodeId);
      if (!parentData.children) {
        parentData.children = [];
      }

      parentData.children = parentData.children.filter(item => item.dbType!=='More');
      parentData.children = [...parentData.children, ...childData];     
      this.graph?.changeData();
      this.graph?.fitView(); 
    } else if (this.props.data) {
      this.graph?.destroy();
      this.graph = init(this)(this.elem, this.props.data)
    }

  }

  graph = undefined
  elem = undefined

  render() {

    const { styles } = this.props;

    return (
      <React.Fragment>
        <div ref={ref => this.elem = ref} style={styles}>
        </div>
      </React.Fragment>

    );
  }
}

export default Relation;


let depthCount = {}

function countDepth(node, _depth = 0) {
  // node.text = "测试123567890"
  const depth = _depth + 1
  if (node.children) {
    let count = undefined
    for (const child of node.children) {
      if (count === undefined) {
        count = (depthCount[depth] ?? 0)
      }
      count += 1
      countDepth(child, depth)
    }
    depthCount[depth] = count
  }
}

const init = (ctx) => function (container, data) {
  depthCount = {}
  countDepth(data)
  // console.debug(depthCount)
  // const container = document.getElementById('container');
  const width = container.scrollWidth;
  const height = container.scrollHeight || 500;
  const graph = new G6.TreeGraph({
    animate: false,
    container,
    width,
    height,
    linkCenter: true,
    modes: {
      default: [
        // {
        //   type: 'collapse-expand',
        //   onChange: function onChange(item, collapsed) {
        //     const data = item.get('model');
        //     data.collapsed = collapsed;
        //     return true;
        //   },
        // },
        'drag-canvas',
        'zoom-canvas',
        'drag-node'
      ],
    },
    defaultNode: {
      type: 'real-node',
      size: 12,
    },
    nodeStateStyles: {
      hover: {}, // 清除预设hover效果
    },
    defaultEdge: {
      type: 'custom-line',
    },
    layout: {
      type: 'dendrogram',
      direction: 'V',
      nodeSep: 25,
      rankSep: 100,
      // radial: true,
    },
  });

  graph.node(function (node) {
    return {
      label: formatText(node.text ?? ''),
    };
  });

  graph.data(data);
  graph.render();
  graph.fitView();

  graph.on('node:mouseenter', (evt) => {
    const { item } = evt;
    const model = item.getModel();
    graph.setItemState(item, 'hover', true);
    item.toFront();
    item.update({
      label: model.text,
    });
  });

  graph.on('node:mouseleave', (evt) => {
    const { item } = evt;
    const model = item.getModel();
    graph.setItemState(item, 'hover', false);
    item.update({
      label: formatText(model.text),
    });
  });

  graph.on('edge:mouseenter', (evt) => {
    const { item } = evt;
    graph.setItemState(item, 'active', true);
  });

  graph.on('edge:mouseleave', (evt) => {
    const { item } = evt;
    graph.setItemState(item, 'active', false);
  });

  // click node to show the detail drawer
  graph.on('node:click', (evt) => {
    const focusNodes = graph.findAllByState('node', 'focus');
    focusNodes.forEach((fnode) => {
      graph.setItemState(fnode, 'focus', false); // false
    });
    const { item } = evt;
    graph.setItemState(item, 'focus', true);
    graph.setItemState(item, 'hover', false);

    const model = item.getModel();
    const nodeId = item.get('id');

    if (model.dbType==='Dir') {

      const children = model.children;
      if (!children) {
        ctx.props?.loadSubLeafData(model.dirId||'', nodeId);
      }

    } else if (model.dbType === 'Table') {
      
      ctx.props?.onAssetClick(model.tid);
    
    } else if (model.dbType === 'More') {
    
      ctx.props?.loadMoreLeafData(model.pDirId, model.pid, model.index);
    
    }

  });

  if (typeof window !== 'undefined')
    window.onresize = () => {
      if (!graph || graph.get('destroyed')) return;
      if (!container || !container.scrollWidth || !container.scrollHeight) return;
      graph.changeSize(container.scrollWidth, container.scrollHeight);
    };

  return graph
}


G6.registerNode(
  'real-node',
  {
    draw(cfg, group) {
      // console.debug(cfg)

      let r = 10, width1 = 0, width2 = 0, lineWidth = 0
      if (depthCount[cfg.depth] > 10) {
        r = 6
      }
      width1 = r * 2.5
      width2 = r * 4

      // if (cfg.depth === 0) {
      //   root = cfg
      // }

      // if (depthCount[cfg.depth] > 10) {
      group.addShape('image', {
        attrs: {
          x: -width1 / 2,
          y: -width1 / 2,
          width: width1,
          height: width1,
          img: circle,
        },
        name: 'halo-shape',
        visible: false,
      });
      // }

      // if (depthCount[cfg.depth] > 10) { 
      group.addShape('image', {
        attrs: {
          x: -width2 / 2,
          y: -width2 / 2,
          width: width2,
          height: width2,
          img: node,
        },
        name: 'focus-shape',
        visible: false,
      });

      lineWidth = .8
      // }

      const keyShape = group.addShape('circle', {
        attrs: {
          x: 0,
          y: 0,
          r,
          fill: colors[cfg.depth % colors.length],
          stroke: highlight,
          lineWidth,
          cursor: (cfg.dbType==='Dir'||cfg.dbType==='More')?'pointer':'default',
        },
        name: 'aggregated-node-keyShape',
      });


      if (cfg.label) {
        const text = cfg.label;
        let refY = 0;
        let offsetY = 0;
        const lineNum = cfg.labelLineNum || 1;
        const fontSize = 6
        offsetY = lineNum * (fontSize || 12);

        let [x, y] = [0, r + refY + offsetY + 5]
        // if (depthCount[cfg.depth] > 10) {
        //   [x, y] = textXY(root, cfg)
        // }

        group.addShape('text', {
          attrs: {
            text,
            x, y,
            textAlign: 'center',
            textBaseLine: 'alphabetic',
            cursor: 'pointer',
            fontSize,
            fill: '#000',
            opacity: 0.85,
            fontWeight: 400,
            // stroke: global.edge.labelCfg.style.stroke,
          },
          name: 'text-shape',
          className: 'text-shape',
        }); 
      }

      const bbox = keyShape.getBBox() 
      if (cfg.dbType === 'Dir') {
        if (!cfg.children) {
          const marker = group.addShape('marker', {
            attrs: {
              x: 0,
              y: 0,
              r: 3,
              symbol: EXPAND_ICON,
              stroke: '#73d13d',
              lineWidth: .7,
              cursor: 'pointer',
            },
            name: 'add-item',
          });
          const mbbox = marker.getBBox()
          // console.debug(bbox, mbbox)
          marker.moveTo(bbox.maxX + mbbox.width/2, bbox.minY + mbbox.height)
        } 
        // else if (cfg?.children?.length > 10) {
        //   group.addShape('marker', {
        //     attrs: {
        //       x: bbox.maxX + 8,
        //       y: bbox.height >> 1,
        //       r: 6,
        //       symbol: cfg.collapsed ? EXPAND_ICON : COLLAPSE_ICON,
        //       stroke: cfg.collapsed ? '#73d13d' : '#ff4d4f',
        //       lineWidth: 2,
        //     }
        //   });
        // } 
      }

      return keyShape;
    },
    setState: (name, value, item) => {
      const group = item.get('group');
      if (name === 'hover') {
        if (item.hasState('focus')) {
          return;
        }
        const halo = group.find((e) => e.get('name') === 'halo-shape');
        if (value) {
          // halo && halo.resumeAnimate()
          halo && halo.show();
          let scale = 1
          halo && halo.animate(
            (ratio) => {
              scale = 2 - ratio
            },
            {
              repeat: false,
              duration: 500,
              easing: 'easeCubicOut',
            },
          );
          halo && halo.animate(
            (ratio) => {
              // console.debug('scale', scale)
              const toMatrix = G6.Util.transform(
                [1, 0, 0, 0, 1, 0, 0, 0, 1],
                [['r', ratio * Math.PI * 2], ['s', scale, scale]],
              );
              return {
                matrix: toMatrix,
              };
            },
            {
              repeat: true,
              duration: 2000,
              easing: 'easeLinear',
            },
          );
        } else {
          halo && halo.stopAnimate()
          halo && halo.hide();
        }
      } else if (name === 'focus') {
        const focus = group.find((e) => e.get('name') === 'focus-shape');
        if (value) {
          focus && focus.show();
        } else {
          focus && focus.hide();
        }
        // 将相关边也高亮
        // const relatedEdges = item.getEdges();
        // relatedEdges.forEach((edge) => {
        //   edge.setState('focus', value);
        // });
      }
    },
    update: undefined,
  },
  'aggregated-node',
); // 这样可以继承 aggregated-node 的 setState

// const lineDash = [4, 2, 1, 2];
G6.registerEdge(
  'custom-line',
  {
    draw(cfg, group) {
      const startPoint = cfg.startPoint;
      const endPoint = cfg.endPoint;

      const dx = endPoint.x - startPoint.x, dy = endPoint.y - startPoint.y, dst = Math.sqrt(dx * dx + dy * dy)
      const px = dst > 30 ? 10 * dx / dst : 0, py = dst > 30 ? 10 * dy / dst : 0
      const shape = group.addShape('path', { // 自定义连线
        attrs: {
          stroke: '#aaa',
          lineWidth: .5,
          path: [
            ['M', startPoint.x + px, startPoint.y + py],
            ['L', endPoint.x - px, endPoint.y - py],
          ],
        },
        // must be assigned in G6 3.3 and later versions. it can be any value you want
        name: 'path-shape',
      });

      const midPoint = shape.getPoint(0.5);
      const rad = Math.acos(dx / dst)
      const radius = dy < 0 ? -rad : rad
      const rw = 20, rh = 6
      {
        const rect = group.addShape('rect', { // 文字的底框
          attrs: {
            width: rw,
            height: rh,
            fill: '#fff',
            radius: 2, 
            // x and y should be minus width / 2 and height / 2 respectively to translate the center of the rect to the midPoint
            // x 和 y 分别减去 width / 2 与 height / 2，使矩形中心在 midPoint 上
          },
          name: 'rect',
          visible: false
        });
        const toMatrix = G6.Util.transform(
          [1, 0, 0, 0, 1, 0, 0, 0, 1],
          [['t', -rw >> 1, -rh >> 1], ['r', dy < 0 ? -rad : rad], ['t', midPoint.x, midPoint.y]],
        );
        rect.setMatrix(toMatrix)
      }
      {
        const text = group.addShape('text', { // 文字
          attrs: {
            text: '关系',
            textAlign: 'center',
            fontSize: 4,
            fill: '#000',
            opacity: 0.85
          },
          name: 'text',
          visible: false
        })
        const bbox = text.getBBox()
        text.moveTo(midPoint.x, midPoint.y + (bbox.height >> 1))
        text.rotateAtPoint(midPoint.x, midPoint.y, rad > Math.PI * .5 ? radius + Math.PI : radius)
      }

      // {
      //   const arrow = group.addShape('marker', {
      //     attrs: {
      //       x: 0,
      //       y: 0,
      //       r: 2,
      //       fill: '#fff',
      //       symbol: 'triangle',
      //     },
      //     // must be assigned in G6 3.3 and later versions. it can be any value you want
      //     name: 'marker-shape',
      //   });
      //   const x = midPoint.x + ((rw >> 1) + 2.5) * dx / dst, y = midPoint.y + ((rw >> 1) + 2.5) * dy / dst
      //   arrow.moveTo(x, y)
      //   arrow.rotateAtPoint(x, y, radius + Math.PI * .5)
      // }

      return group;
    },
    // afterDraw: (cfg, group) => {

    // },
    setState: (name, value, item) => {
      if (name === 'active') {
        const keyShape = item.get('keyShape').get('children')[0];
        // const rect = item.get('keyShape').get('children')[1];
        // const text = item.get('keyShape').get('children')[2];
        if (value) {
          keyShape.attr('stroke', highlight);
          // rect.show()
          // text.show()
        } else {
          keyShape.attr('stroke', '#aaa');
          // rect.hide()
          // text.hide()
        }
      }
      // if (name === 'focus') {
      //   const keyShape = item.get('keyShape');
      //   if (value) {
      //     if (keyShape.cfg.animating) {
      //       return
      //     }
      //     // const length = keyShape.getTotalLength(); 
      //     // To fix stopAnimate not immediately bug
      //     window.setTimeout(() => {
      //       keyShape.animate(
      //         (ratio) => {
      //           // the operations in each frame. Ratio ranges from 0 to 1 indicating the prograss of the animation. Returns the modified configurations
      //           // const startLen = ratio * length;
      //           // Calculate the lineDash
      //           const cfg = {
      //             // lineDash: [startLen, length - startLen],
      //             lineDash,
      //             lineDashOffset: - Math.floor(ratio * 10),
      //           };
      //           return cfg;
      //         },
      //         {
      //           repeat: true, // Whether executes the animation repeatly
      //           duration: 1000, // the duration for executing once
      //         },
      //       );
      //     }, 100)

      //   } else {
      //     keyShape.stopAnimate();
      //     keyShape.attr('lineDash', null)
      //   }
      // }
    }
  },
  // 'single-edge',
)


// 截断长文本。length 为文本截断后长度，elipsis 是后缀
const formatText = (text, length = 5, elipsis = '...') => {
  if (!text) return '';
  if (text.length > length) {
    return `${text.substr(0, length)}${elipsis}`;
  }
  return text;
};
// function textXY(root, cfg) {
//   const txtWidth = cfg.label.length > 5 ? 24 : cfg.label.length * 4
//   const { x, y } = cfg
//   const _y = y - root.y, _x = x - root.x
//   const angle = Math.atan2(_y, _x)
//   const sinAngle = Math.sin(angle), cosAngle = Math.cos(angle)

//   const radX = 4 + txtWidth
//   const radY = Math.abs(Math.pow(sinAngle, Math.abs(Math.round(sinAngle * 50)))) * 10 //Math.pow(Math.E, 80 * Math.log(Math.abs(sinAngle))) * 40

//   return [
//     // 右侧文字(中间向右侧15px,上下部向左靠20px) : 左侧
//     radX * cosAngle,
//     radY * sinAngle + (sinAngle > 0 ? sinAngle * 20 : sinAngle * 10)
//   ];
// }
