import React from 'react';

import Chart from '@antv/chart-node-g6';
import G6 from '@antv/g6';

import centerImg from '../assets/center.png';
import expandImg from '../assets/expand.png';

const maxInnerRaduis = 40, imageWidth = 20, textFontSize = 8;

const centerColorGroup = ['#D8E9FF', '#A7CEFF', '#196AD2'];
const centerHighlightColorGroup = ['#99c6ff', '#4d8ddf', '#145ab3'];

const secondaryColors = ['#d1a4e4', '#92d6c9', '#7c94dd', '#6d96ff', '#6dc3ff', '#ff9366', '#ff867d', '#ff89c1', '#ff7444', '#e4a4cb'];
const secondaryHighlightColors = ['#b36cd1', '#48b5a0', '#4a6acc', '#356eff', '#37adff', '#ff7339', '#ff7065', '#ff60ab', '#ff6733', '#e56db6'];

class Relation extends React.Component {

  componentDidMount() {
    const { data, onCenterClick, onExpandClick } = this.props;

    if (data) {
      this.graph?.destroy();
      this.graph = init(this)(this.elem, data, onCenterClick, onExpandClick);
    }
  }

  componentDidUpdate(prevProps, prevState) {
    const { data, resize, expandId, onCenterClick, onExpandClick } = this.props;

    if (data) {
      if (data !== prevProps.data) {
        if ((expandId||'') === '') {
          this.graph?.destroy();
          this.graph = init(this)(this.elem, data, onCenterClick, onExpandClick);
        } else {
          if (data && (data.children||[]).length > 0) {
            this.graph?.updateChild(data, 'root');
          }
        }
      } 
    }

    if (resize !== prevProps.resize && prevProps.resize!==null) {
      if (!this.elem || !this.elem.clientWidth || !this.elem.clientHeight) return;

      this.graph?.changeSize(this.elem.clientWidth, this.elem.clientHeight);
    }
    
  }

  graph = undefined;
  elem = undefined;
  
  render() {
    return (
      <div ref={ref => this.elem = ref} style={{ position: 'relative', width: '100%', height: '100%' }}></div>
    );
  }
}

export default Relation;

const init = (ctx) => function (container, data, onCenterClick, onExpandClick) {
  const width = container.scrollWidth;
  const height = container.scrollHeight;
  // const totalLevel = data?.totalLevel;

  const tooltip = new G6.Tooltip({
    offsetX: 10,
    offsetY: 10,
    fixToNode: [1, 0.5],
    itemTypes: ['node'],
    getContent: (e) => {

      const model = e.item.getModel();
      if (e.item.getType() === 'node') {
        return `${model.text||''} (${model.count})`;
      }
      return '';
    },
  });

  const calcRaduis = (node) => {

    const outerRaduis = 20;

    if (!node?.levelId) return { innerRaduis: maxInnerRaduis, raduis: (maxInnerRaduis+outerRaduis) };

    const nodeLevel = node?.levelId.split('-').length;
    let innerRaduis = maxInnerRaduis;
    if (nodeLevel > 1) {
      innerRaduis = maxInnerRaduis - 15 - 5*(nodeLevel - 2);
    }

    if (innerRaduis < 15) {
      innerRaduis = 15;
    }

    return { innerRaduis, raduis: (innerRaduis+outerRaduis) };
  }

  const calcSecondaryNodeColor = (node) => {
    let nodeColor = '', nodeHighlightColor = '';

    const nodeLevel = node?.levelId.split('-').length;
    if (node?.levelId && nodeLevel > 1) {
      const index = node?.levelId.split('-')[1];
      nodeColor = secondaryColors[index%secondaryColors.length];
      nodeHighlightColor = secondaryHighlightColors[index%secondaryHighlightColors.length];
    }

    return { nodeColor, nodeHighlightColor }
  }

  G6.registerNode('tree-node', {
    drawShape: function drawShape(cfg, group) {

      const { innerRaduis, raduis } = calcRaduis(cfg);
      const { nodeColor, nodeHighlightColor } = calcSecondaryNodeColor(cfg);

      const nodeLevel = cfg?.levelId.split('-').length;
      let chartColor = '';

      if (nodeLevel === 1) {
        chartColor = centerHighlightColorGroup[2];
      } else {
        chartColor = nodeHighlightColor;
      }

      const keyShape = group.addShape('circle', {
        attrs: {
          x: raduis,
          y: raduis,
          r: raduis,
        },
        name: 'keyShape',
        draggable: true,
      });

      //virtual功能已去掉
      if (!cfg?.virtual && cfg?.haveChild) {
        const view = new (Chart || window.Chart)({
          group,
          width: raduis*2,
          height: raduis*2,
          x: 0,
          y: 0,
        });
  
        const data = [
          { type: 'expand', value: 49.7 },
          { type: 'interval1', value: 0.3 },
          { type: 'center', value: 49.7 },
          { type: 'interval2', value: 0.3 }
        ];
  
        view.data(data);
  
        view
          .coordinate('theta', {
            radius: 1.0,
            innerRadius: (innerRaduis/raduis) + 0.05
          })
          .rotate(Math.PI * 0.5);
        view
          .interval()
          .adjust('stack')
          .position('value')
          .color('type',[chartColor, '#fff', chartColor, '#fff'])
          .style({
            lineWidth: 0,
            cursor: 'pointer',
          })
          .state({
            active: {
              style: (element) => {
                const shape = element.shape;
                return {
                  lineWidth: 0,
                  fillOpacity: shape.attr("fillOpacity")*0.4,
                }
              }
            }
          })
    
        view.interaction('element-active');
        view.legend(false);
  
        view.on('element:click', evt => {
          const { data } = evt;
  
          if (data?.data?.type === 'center') {
            onCenterClick && onCenterClick(cfg.id);
          } else if (data?.data?.type === 'expand') {
            onExpandClick && onExpandClick(cfg.id);
          }
        })
       
        view.render();
  
        keyShape.set('intervalView', view);
      
        const imageShape1 = group.addShape('image', {
          attrs: {
            x: raduis-imageWidth/2,
            y: (raduis - innerRaduis)/2 - imageWidth/2,
            width: imageWidth,
            height: imageWidth,
            cursor: 'pointer',
            img: centerImg,
          },
          name: 'center-icon',
        });
  
        const imageShape2 = group.addShape('image', {
          attrs: {
            x: raduis-imageWidth/2,
            y: raduis + innerRaduis + (raduis - innerRaduis)/2 - imageWidth/2,
            height: imageWidth,
            width: imageWidth,
            cursor: 'pointer',
            img: expandImg,
            alt: 'test'
          },
          name: 'expand-icon',
        });
  
        imageShape1.hide();
        imageShape2.hide();
      }

      if (cfg?.levelId && nodeLevel === 1) {
        group.addShape('circle', {
          attrs: {
            x: raduis,
            y: raduis,
            r: innerRaduis,
            lineWidth: 0,
            fill: centerColorGroup[0],
            fillOpacity: 1.0,
            stroke: '#fff',
            strokeOpacity: 0,
            cursor: (cfg?.virtual)?'':'pointer',
          },
          name: 'innnerCircle1',
          draggable: true,
        });

        group.addShape('circle', {
          attrs: {
            x: raduis,
            y: raduis,
            r: innerRaduis*0.8,
            lineWidth: 0,
            fill: centerColorGroup[1],
            fillOpacity: 1.0,
            stroke: '#fff',
            strokeOpacity: 0,
            cursor: (cfg?.virtual)?'':'pointer',
          },
          name: 'innnerCircle2',
          draggable: true,
        });

        group.addShape('circle', {
          attrs: {
            x: raduis,
            y: raduis,
            r: innerRaduis*0.8*0.8,
            lineWidth: 0,
            fill: centerColorGroup[2],
            fillOpacity: 1.0,
            stroke: '#fff',
            strokeOpacity: 0,
            cursor: (cfg?.virtual)?'':'pointer',
          },
          name: 'innnerCircle3',
          draggable: true,
        });
      } else {

        group.addShape('circle', {
          attrs: {
            x: raduis,
            y: raduis,
            r: innerRaduis,
            lineWidth: 0,
            fill: nodeColor,
            fillOpacity: 1.0,
            stroke: '#fff',
            strokeOpacity: 0,
            cursor: (cfg?.virtual)?'':'pointer',
          },
          name: 'innnerCircle1',
          draggable: true,
        });
      }

      group.addShape("text", {
        attrs: {
          text: (cfg.simple)?'':(cfg.label||''),
          x: raduis,
          y: raduis - 5,
          fontSize: textFontSize,
          textAlign: 'center',
          textBaseline: "middle",
          fill: "#fff",
          cursor: (cfg?.virtual)?'':'pointer',
        },
        name: 'text',
        draggable: true
      });

      group.addShape("text", {
        attrs: {
          text: (cfg.simple)?'':`${cfg.count}`,
          x: raduis,
          y: raduis + 5,
          fontSize: textFontSize,
          textAlign: 'center',
          textBaseline: "middle",
          fill: "#fff",
          cursor: (cfg?.virtual)?'':'pointer',
        },
        name: 'text1',
        draggable: true
      });

      return keyShape;
    },
    afterDraw: (cfg, group) => {
      if (!cfg?.virtual && cfg?.haveChild) {
        const pathShape1 = group.get('children')[1];
        const pathShape2 = group.get('children')[2];
        const pathShape3 = group.get('children')[3];

        pathShape1?.hide();
        pathShape2?.hide();
        pathShape3?.hide();
      }
    },
    setState(name, value, item) {
      const model = item?.getModel();

      if (!model?.virtual && model?.haveChild) {
        const group = item.getContainer();
        console.log('group', group);

        const keyShape = group.get('children')[0];
        const pathShape1 = group.get('children')[1];
        const pathShape2 = group.get('children')[2];
        const pathShape3 = group.get('children')[3];
        const imageShape1 = group.get('children')[4];
        const imageShape2 = group.get('children')[5];
        const circleShape1 = group.get('children')[6];
        const circleShape2 = group.get('children')[7];
        const circleShape3 = group.get('children')[8];

        const { nodeColor, nodeHighlightColor } = calcSecondaryNodeColor(model);

        if (name === 'active') {
          if (value) {
            keyShape?.attr('fill', '#fff');

            pathShape1?.show();
            pathShape2?.show();
            pathShape3?.show();
            imageShape1?.show();
            imageShape2?.show();
            if (model?.levelId && model?.levelId.split('-').length===1) {
              circleShape1?.attr('fill', centerHighlightColorGroup[0]);
              circleShape2?.attr('fill', centerHighlightColorGroup[1]);
              circleShape3?.attr('fill', centerHighlightColorGroup[2]);
            } else {
              circleShape1?.attr('fill', nodeHighlightColor);
              circleShape1?.attr('shadowOffsetY', 7);
              circleShape1?.attr('shadowColor', 'rgba(0,0,0,0.23)');
              circleShape1?.attr('shadowBlur', 3);
            }

            group.toFront();
          } else {
            keyShape?.attr('fill', null);
              
            pathShape1?.hide();
            pathShape2?.hide();
            pathShape3?.hide();
            imageShape1?.hide();
            imageShape2?.hide();
            if (model?.levelId && model?.levelId.split('-').length===1) {
              circleShape1?.attr('fill', centerColorGroup[0]);
              circleShape2?.attr('fill', centerColorGroup[1]);
              circleShape3?.attr('fill', centerColorGroup[2]);
            } else {
              circleShape1?.attr('fill', nodeColor);
              circleShape1?.attr('shadowOffsetY', 0);
            }
          }
        }
      }

    },
  })

  const graph = new G6.TreeGraph({
    container,
    width,
    height,
    linkCenter: true,
    plugins: [tooltip],
    animate: false,
    modes: {
      default: [
        'drag-canvas',
        'zoom-canvas',
      ],
    },
    defaultNode: {
      type: 'tree-node',
      anchorPoints: [
        [0, 0.5],
        [1, 0.5],
      ],
    },
    defaultEdge: {
      type: 'line',
      color: '#d6ecff',
    },
    layout: {
      type: 'dendrogram',
      direction: 'LR',
      nodesepFunc: (d) => {
        const { innerRaduis } = calcRaduis(d);
        return (innerRaduis*2+30.0);
      },
      rankSep: 150,
      radial: true,
    },

    //fitView不能放在render后面, 否则调用updateChild会刷新界面
    fitView: (data && (data?.children||[]).length > 0) ? true : false,
  });

  const fittingString = (str, maxWidth, fontSize) => {
    const ellipsis = '...';
    const ellipsisLength = G6.Util.getTextSize(ellipsis, fontSize)[0];
    let currentWidth = 0;
    let res = str;
    const pattern = new RegExp('[\u4E00-\u9FA5]+'); // distinguish the Chinese charactors and letters
    str.split('').forEach((letter, i) => {
      if (currentWidth > maxWidth - ellipsisLength) return;
      if (pattern.test(letter)) {
        // Chinese charactors
        currentWidth += fontSize;
      } else {
        // get the width of single letter according to the fontSize
        currentWidth += G6.Util.getLetterWidth(letter, fontSize);
      }
      if (currentWidth > maxWidth - ellipsisLength) {
        res = `${str.substr(0, i)}${ellipsis}`;
      }
    });
    return res;
  };

  graph.node(function (node) {
    const { innerRaduis } = calcRaduis(node);

    return {
      label: fittingString(node.text||'', innerRaduis*2-4.0, textFontSize),
    };
  });

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

  if (data && (data?.children||[]).length === 0) {
    //只有一个节点的时候 居中显示
    graph.fitCenter();
  }

  graph.on('node:mouseenter', function (e) {
    const item = e.item;

    graph.setAutoPaint(false);

    graph.getNodes().forEach(function (node) {
      graph.clearItemStates(node);
    });
   
    graph.setItemState(item, 'active', true);

    graph.paint();
    graph.setAutoPaint(true);
  });

  graph.on('node:mouseleave', function (e) {
    clearAllStats();
  });

  graph.on('node:click', function(e) {
    const item = e.item;
    const model = item?.getModel();

    if (!model?.virtual) {
      if (e.target?.cfg?.name === 'center-icon') {
        onCenterClick && onCenterClick(model?.id);
      } else if (e.target?.cfg?.name==='expand-icon' || e.target?.cfg?.name.indexOf('innnerCircle')!==-1 || e.target?.cfg?.name.indexOf('text')!==-1) {
        onExpandClick && onExpandClick(model?.id);
      }
    }
  })

  function clearAllStats() {
    graph.setAutoPaint(false);
    graph.getNodes().forEach(function (node) {
      graph.clearItemStates(node);
      graph.setItemState(node, 'active', false);
    });

    graph.paint();
    graph.setAutoPaint(true);
  }

  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;
}
