/**
 * echarts图表类：chord diagram
 *
 * @author pissang (https://github.com/pissang/)
 * 
 * TODO 非Ribbon Type 支持 undirected graph ?
 */
module.exports = function (require) {
  'use strict';

  var ChartBase = require('./base'); // 图形依赖


  var TextShape = require('zrender/shape/Text');

  var LineShape = require('zrender/shape/Line');

  var SectorShape = require('zrender/shape/Sector');

  var RibbonShape = require('../util/shape/Ribbon');

  var IconShape = require('../util/shape/Icon');

  var BezierCurveShape = require('zrender/shape/BezierCurve');

  var ecConfig = require('../config'); // 和弦图默认参数


  ecConfig.chord = {
    zlevel: 0,
    // 一级层叠
    z: 2,
    // 二级层叠
    clickable: true,
    radius: ['65%', '75%'],
    center: ['50%', '50%'],
    padding: 2,
    sort: 'none',
    // can be 'none', 'ascending', 'descending'
    sortSub: 'none',
    // can be 'none', 'ascending', 'descending'
    startAngle: 90,
    clockWise: true,
    ribbonType: true,

    /***************** 下面的配置项在 ribbonType 为 false 时有效 */
    // 同force类似
    minRadius: 10,
    maxRadius: 20,
    symbol: 'circle',

    /***************** 上面的配置项在 ribbonType 为 false 时有效 */

    /***************** 下面的配置项在 ribbonType 为 true 时有效 */
    showScale: false,
    showScaleText: false,

    /***************** 上面的配置项在 ribbonType 为 true 时有效 */
    // 分类里如果有样式会覆盖节点默认样式
    // categories: [{
    // itemStyle
    // symbol
    // symbolSize
    // name
    // }],
    itemStyle: {
      normal: {
        borderWidth: 0,
        borderColor: '#000',
        label: {
          show: true,
          rotate: false,
          distance: 5 // textStyle: null      // 默认使用全局文本样式，详见TEXTSTYLE

        },
        chordStyle: {
          /** ribbonType = false 时有效 */
          width: 1,
          color: 'black',

          /** ribbonType = true 时有效 */
          borderWidth: 1,
          borderColor: '#999',
          opacity: 0.5
        }
      },
      emphasis: {
        borderWidth: 0,
        borderColor: '#000',
        chordStyle: {
          /** ribbonType = false 时有效 */
          width: 1,
          color: 'black',

          /** ribbonType = true 时有效 */
          borderWidth: 1,
          borderColor: '#999'
        }
      }
    }
    /****** 使用 Data-matrix 表示数据 */
    // data: [],
    // Source data matrix

    /**
     *         target
     *    -1--2--3--4--5-
     *  1| x  x  x  x  x
     *  2| x  x  x  x  x
     *  3| x  x  x  x  x  source
     *  4| x  x  x  x  x
     *  5| x  x  x  x  x
     *
     *  Relation ship from source to target
     *  https://github.com/mbostock/d3/wiki/Chord-Layout#wiki-chord
     *  
     *  Row based
     */
    // matrix: [],

    /****** 使用 node-links 表示数据 */
    // 参考 force
    // nodes: [],
    // links: []

  };

  var ecData = require('../util/ecData');

  var zrUtil = require('zrender/tool/util');

  var vec2 = require('zrender/tool/vector');

  var Graph = require('../data/Graph');

  var ChordLayout = require('../layout/Chord');

  function Chord(ecTheme, messageCenter, zr, option, myChart) {
    // 图表基类
    ChartBase.call(this, ecTheme, messageCenter, zr, option, myChart);
    this.scaleLineLength = 4;
    this.scaleUnitAngle = 4;
    this.refresh(option);
  }

  Chord.prototype = {
    type: ecConfig.CHART_TYPE_CHORD,

    /**
     * 绘制图形
     */
    _init: function () {
      var series = this.series;
      this.selectedMap = {};
      var chordSeriesMap = {};
      var chordSeriesGroups = {};

      for (var i = 0, l = series.length; i < l; i++) {
        if (series[i].type === this.type) {
          var _isSelected = this.isSelected(series[i].name); // Filter by selected serie


          this.selectedMap[series[i].name] = _isSelected;

          if (_isSelected) {
            this.buildMark(i);
          }

          this.reformOption(series[i]);
          chordSeriesMap[series[i].name] = series[i];
        }
      }

      for (var i = 0, l = series.length; i < l; i++) {
        if (series[i].type === this.type) {
          if (series[i].insertToSerie) {
            var referenceSerie = chordSeriesMap[series[i].insertToSerie];
            series[i]._referenceSerie = referenceSerie;
          } else {
            chordSeriesGroups[series[i].name] = [series[i]];
          }
        }
      }

      for (var i = 0, l = series.length; i < l; i++) {
        if (series[i].type === this.type) {
          if (series[i].insertToSerie) {
            // insertToSerie 可能会存在链式的使用，找到最原始的系列，分到一个 Group 里
            var mainSerie = series[i]._referenceSerie;

            while (mainSerie && mainSerie._referenceSerie) {
              mainSerie = mainSerie._referenceSerie;
            }

            if (chordSeriesGroups[mainSerie.name] && this.selectedMap[series[i].name]) {
              chordSeriesGroups[mainSerie.name].push(series[i]);
            }
          }
        }
      }

      for (var name in chordSeriesGroups) {
        this._buildChords(chordSeriesGroups[name]);
      }

      this.addShapeList();
    },
    _getNodeCategory: function (serie, group) {
      return serie.categories && serie.categories[group.category || 0];
    },
    _getNodeQueryTarget: function (serie, group) {
      var category = this._getNodeCategory(serie, group);

      return [group, category, serie];
    },
    _getEdgeQueryTarget: function (serie, edge, type) {
      type = type || 'normal';
      return [edge.itemStyle && edge.itemStyle[type], serie.itemStyle[type].chordStyle];
    },
    _buildChords: function (series) {
      var graphs = [];
      var mainSerie = series[0];

      var nodeFilter = function (n) {
        return n.layout.size > 0;
      };

      var createEdgeFilter = function (graph) {
        return function (e) {
          return graph.getEdge(e.node2, e.node1);
        };
      };

      for (var i = 0; i < series.length; i++) {
        var serie = series[i];

        if (this.selectedMap[serie.name]) {
          var graph; // matrix 表示边

          if (serie.matrix) {
            graph = this._getSerieGraphFromDataMatrix(serie, mainSerie);
          } // links 表示边
          else if (serie.links) {
            graph = this._getSerieGraphFromNodeLinks(serie, mainSerie);
          } // 过滤输出为0的节点


          graph.filterNode(nodeFilter, this);

          if (serie.ribbonType) {
            graph.filterEdge(createEdgeFilter(graph));
          }

          graphs.push(graph);
          graph.__serie = serie;
        }
      }

      if (!graphs.length) {
        return;
      }

      var mainGraph = graphs[0];

      if (!mainSerie.ribbonType) {
        var minRadius = mainSerie.minRadius;
        var maxRadius = mainSerie.maxRadius; // Map size to [minRadius, maxRadius]

        var min = Infinity,
            max = -Infinity;
        mainGraph.eachNode(function (node) {
          max = Math.max(node.layout.size, max);
          min = Math.min(node.layout.size, min);
        });
        var multiplier = (maxRadius - minRadius) / (max - min);
        mainGraph.eachNode(function (node) {
          var queryTarget = this._getNodeQueryTarget(mainSerie, node);

          var symbolSize = this.query(queryTarget, 'symbolSize');

          if (max === min) {
            node.layout.size = symbolSize || min;
          } else {
            node.layout.size = symbolSize || (node.layout.size - min) * multiplier + minRadius;
          }
        }, this);
      } // Do layout


      var layout = new ChordLayout();
      layout.clockWise = mainSerie.clockWise;
      layout.startAngle = mainSerie.startAngle * Math.PI / 180;

      if (!layout.clockWise) {
        layout.startAngle = -layout.startAngle;
      }

      layout.padding = mainSerie.padding * Math.PI / 180;
      layout.sort = mainSerie.sort;
      layout.sortSub = mainSerie.sortSub;
      layout.directed = mainSerie.ribbonType;
      layout.run(graphs);
      var showLabel = this.query(mainSerie, 'itemStyle.normal.label.show');

      if (mainSerie.ribbonType) {
        this._buildSectors(mainSerie, 0, mainGraph, mainSerie, graphs);

        if (showLabel) {
          this._buildLabels(mainSerie, 0, mainGraph, mainSerie, graphs);
        }

        for (var i = 0, j = 0; i < series.length; i++) {
          if (this.selectedMap[series[i].name]) {
            this._buildRibbons(series, i, graphs[j++], mainSerie);
          }
        }

        if (mainSerie.showScale) {
          this._buildScales(mainSerie, 0, mainGraph);
        }
      } else {
        this._buildNodeIcons(mainSerie, 0, mainGraph, mainSerie, graphs);

        if (showLabel) {
          this._buildLabels(mainSerie, 0, mainGraph, mainSerie, graphs);
        }

        for (var i = 0, j = 0; i < series.length; i++) {
          if (this.selectedMap[series[i].name]) {
            this._buildEdgeCurves(series, i, graphs[j++], mainSerie, mainGraph);
          }
        }
      }

      this._initHoverHandler(series, graphs);
    },
    _getSerieGraphFromDataMatrix: function (serie, mainSerie) {
      var nodesData = [];
      var count = 0;
      var matrix = []; // 复制一份新的matrix

      for (var i = 0; i < serie.matrix.length; i++) {
        matrix[i] = serie.matrix[i].slice();
      }

      var data = serie.data || serie.nodes;

      for (var i = 0; i < data.length; i++) {
        var node = {};
        var group = data[i];
        group.rawIndex = i;

        for (var key in group) {
          // name改为id
          if (key === 'name') {
            node['id'] = group['name'];
          } else {
            node[key] = group[key];
          }
        } // legends 选择优先级 category -> group


        var category = this._getNodeCategory(mainSerie, group);

        var name = category ? category.name : group.name;
        this.selectedMap[name] = this.isSelected(name);

        if (this.selectedMap[name]) {
          nodesData.push(node);
          count++;
        } else {
          // 过滤legend未选中的数据
          matrix.splice(count, 1);

          for (var j = 0; j < matrix.length; j++) {
            matrix[j].splice(count, 1);
          }
        }
      }

      var graph = Graph.fromMatrix(nodesData, matrix, true); // Prepare layout parameters

      graph.eachNode(function (n, idx) {
        n.layout = {
          size: n.data.outValue
        };
        n.rawIndex = n.data.rawIndex;
      });
      graph.eachEdge(function (e) {
        e.layout = {
          weight: e.data.weight
        };
      });
      return graph;
    },
    _getSerieGraphFromNodeLinks: function (serie, mainSerie) {
      var graph = new Graph(true);
      var nodes = serie.data || serie.nodes;

      for (var i = 0, len = nodes.length; i < len; i++) {
        var n = nodes[i];

        if (!n || n.ignore) {
          continue;
        } // legends 选择优先级 category -> group


        var category = this._getNodeCategory(mainSerie, n);

        var name = category ? category.name : n.name;
        this.selectedMap[name] = this.isSelected(name);

        if (this.selectedMap[name]) {
          var node = graph.addNode(n.name, n);
          node.rawIndex = i;
        }
      }

      for (var i = 0, len = serie.links.length; i < len; i++) {
        var e = serie.links[i];
        var n1 = e.source;
        var n2 = e.target;

        if (typeof n1 === 'number') {
          n1 = nodes[n1];

          if (n1) {
            n1 = n1.name;
          }
        }

        if (typeof n2 === 'number') {
          n2 = nodes[n2];

          if (n2) {
            n2 = n2.name;
          }
        }

        var edge = graph.addEdge(n1, n2, e);

        if (edge) {
          edge.rawIndex = i;
        }
      }

      graph.eachNode(function (n) {
        var value = n.data.value;

        if (value == null) {
          // value 是 null 或者 undefined
          value = 0;

          if (mainSerie.ribbonType) {
            // 默认使用所有出边值的和作为节点的大小, 不修改 data 里的数值
            for (var i = 0; i < n.outEdges.length; i++) {
              value += n.outEdges[i].data.weight || 0;
            }
          } else {
            // 默认使用所有边值的和作为节点的大小, 不修改 data 里的数值
            for (var i = 0; i < n.edges.length; i++) {
              value += n.edges[i].data.weight || 0;
            }
          }
        }

        n.layout = {
          size: value
        };
      });
      graph.eachEdge(function (e) {
        e.layout = {
          // 默认 weight 为1
          weight: e.data.weight == null ? 1 : e.data.weight
        };
      });
      return graph;
    },
    _initHoverHandler: function (series, graphs) {
      var mainSerie = series[0];
      var mainGraph = graphs[0];
      var self = this;
      mainGraph.eachNode(function (node) {
        node.shape.onmouseover = function () {
          mainGraph.eachNode(function (n) {
            n.shape.style.opacity = 0.1;

            if (n.labelShape) {
              n.labelShape.style.opacity = 0.1;
              n.labelShape.modSelf();
            }

            n.shape.modSelf();
          });

          for (var i = 0; i < graphs.length; i++) {
            for (var j = 0; j < graphs[i].edges.length; j++) {
              var e = graphs[i].edges[j];

              var queryTarget = self._getEdgeQueryTarget(graphs[i].__serie, e.data);

              e.shape.style.opacity = self.deepQuery(queryTarget, 'opacity') * 0.1;
              e.shape.modSelf();
            }
          }

          node.shape.style.opacity = 1;

          if (node.labelShape) {
            node.labelShape.style.opacity = 1;
          }

          for (var i = 0; i < graphs.length; i++) {
            var n = graphs[i].getNodeById(node.id);

            if (n) {
              //  节点有可能没数据被过滤掉了
              for (var j = 0; j < n.outEdges.length; j++) {
                var e = n.outEdges[j];

                var queryTarget = self._getEdgeQueryTarget(graphs[i].__serie, e.data);

                e.shape.style.opacity = self.deepQuery(queryTarget, 'opacity');
                var other = graphs[0].getNodeById(e.node2.id);

                if (other) {
                  if (other.shape) {
                    other.shape.style.opacity = 1;
                  }

                  if (other.labelShape) {
                    other.labelShape.style.opacity = 1;
                  }
                }
              }
            }
          }

          self.zr.refreshNextFrame();
        };

        node.shape.onmouseout = function () {
          mainGraph.eachNode(function (n) {
            n.shape.style.opacity = 1;

            if (n.labelShape) {
              n.labelShape.style.opacity = 1;
              n.labelShape.modSelf();
            }

            n.shape.modSelf();
          });

          for (var i = 0; i < graphs.length; i++) {
            for (var j = 0; j < graphs[i].edges.length; j++) {
              var e = graphs[i].edges[j];
              var queryTarget = [e.data, mainSerie];
              e.shape.style.opacity = self.deepQuery(queryTarget, 'itemStyle.normal.chordStyle.opacity');
              e.shape.modSelf();
            }
          }

          self.zr.refreshNextFrame();
        };
      });
    },
    _buildSectors: function (serie, serieIdx, graph, mainSerie) {
      var center = this.parseCenter(this.zr, mainSerie.center);
      var radius = this.parseRadius(this.zr, mainSerie.radius);
      var clockWise = mainSerie.clockWise;
      var sign = clockWise ? 1 : -1;
      graph.eachNode(function (node) {
        var category = this._getNodeCategory(mainSerie, node.data); // 默认使用 category 分类颜色


        var color = category ? this.getColor(category.name) : this.getColor(node.id);
        var startAngle = node.layout.startAngle / Math.PI * 180 * sign;
        var endAngle = node.layout.endAngle / Math.PI * 180 * sign;
        var sector = new SectorShape({
          zlevel: serie.zlevel,
          z: serie.z,
          style: {
            x: center[0],
            y: center[1],
            r0: radius[0],
            r: radius[1],
            startAngle: startAngle,
            endAngle: endAngle,
            brushType: 'fill',
            opacity: 1,
            color: color,
            clockWise: clockWise
          },
          clickable: mainSerie.clickable,
          highlightStyle: {
            brushType: 'fill'
          }
        });
        sector.style.lineWidth = this.deepQuery([node.data, mainSerie], 'itemStyle.normal.borderWidth');
        sector.highlightStyle.lineWidth = this.deepQuery([node.data, mainSerie], 'itemStyle.emphasis.borderWidth');
        sector.style.strokeColor = this.deepQuery([node.data, mainSerie], 'itemStyle.normal.borderColor');
        sector.highlightStyle.strokeColor = this.deepQuery([node.data, mainSerie], 'itemStyle.emphasis.borderColor');

        if (sector.style.lineWidth > 0) {
          sector.style.brushType = 'both';
        }

        if (sector.highlightStyle.lineWidth > 0) {
          sector.highlightStyle.brushType = 'both';
        }

        ecData.pack(sector, serie, serieIdx, node.data, node.rawIndex, node.id, // special
        node.category);
        this.shapeList.push(sector);
        node.shape = sector;
      }, this);
    },
    _buildNodeIcons: function (serie, serieIdx, graph, mainSerie) {
      var center = this.parseCenter(this.zr, mainSerie.center);
      var radius = this.parseRadius(this.zr, mainSerie.radius); // PENDING

      var r = radius[1];
      graph.eachNode(function (node) {
        var startAngle = node.layout.startAngle;
        var endAngle = node.layout.endAngle;
        var angle = (startAngle + endAngle) / 2;
        var x = r * Math.cos(angle);
        var y = r * Math.sin(angle);

        var queryTarget = this._getNodeQueryTarget(mainSerie, node.data);

        var category = this._getNodeCategory(mainSerie, node.data);

        var color = this.deepQuery(queryTarget, 'itemStyle.normal.color');

        if (!color) {
          color = category ? this.getColor(category.name) : this.getColor(node.id);
        }

        var iconShape = new IconShape({
          zlevel: serie.zlevel,
          z: serie.z + 1,
          style: {
            x: -node.layout.size,
            y: -node.layout.size,
            width: node.layout.size * 2,
            height: node.layout.size * 2,
            iconType: this.deepQuery(queryTarget, 'symbol'),
            color: color,
            brushType: 'both',
            lineWidth: this.deepQuery(queryTarget, 'itemStyle.normal.borderWidth'),
            strokeColor: this.deepQuery(queryTarget, 'itemStyle.normal.borderColor')
          },
          highlightStyle: {
            color: this.deepQuery(queryTarget, 'itemStyle.emphasis.color'),
            lineWidth: this.deepQuery(queryTarget, 'itemStyle.emphasis.borderWidth'),
            strokeColor: this.deepQuery(queryTarget, 'itemStyle.emphasis.borderColor')
          },
          clickable: mainSerie.clickable,
          position: [x + center[0], y + center[1]]
        });
        ecData.pack(iconShape, serie, serieIdx, node.data, node.rawIndex, node.id, // special
        node.category);
        this.shapeList.push(iconShape);
        node.shape = iconShape;
      }, this);
    },
    _buildLabels: function (serie, serieIdx, graph, mainSerie) {
      // var labelColor = this.query(
      // mainSerie, 'itemStyle.normal.label.color'
      // );
      var rotateLabel = this.query(mainSerie, 'itemStyle.normal.label.rotate');
      var labelDistance = this.query(mainSerie, 'itemStyle.normal.label.distance');
      var center = this.parseCenter(this.zr, mainSerie.center);
      var radius = this.parseRadius(this.zr, mainSerie.radius);
      var clockWise = mainSerie.clockWise;
      var sign = clockWise ? 1 : -1;
      graph.eachNode(function (node) {
        var startAngle = node.layout.startAngle / Math.PI * 180 * sign;
        var endAngle = node.layout.endAngle / Math.PI * 180 * sign;
        var angle = (startAngle * -sign + endAngle * -sign) / 2;
        angle %= 360;

        if (angle < 0) {
          // Constrain to [0,360]
          angle += 360;
        }

        var isRightSide = angle <= 90 || angle >= 270;
        angle = angle * Math.PI / 180;
        var v = [Math.cos(angle), -Math.sin(angle)];
        var distance = 0;

        if (mainSerie.ribbonType) {
          distance = mainSerie.showScaleText ? 35 + labelDistance : labelDistance;
        } else {
          distance = labelDistance + node.layout.size;
        }

        var start = vec2.scale([], v, radius[1] + distance);
        vec2.add(start, start, center);
        var labelShape = {
          zlevel: serie.zlevel,
          z: serie.z + 1,
          hoverable: false,
          style: {
            text: node.data.label == null ? node.id : node.data.label,
            textAlign: isRightSide ? 'left' : 'right'
          }
        };

        if (rotateLabel) {
          labelShape.rotation = isRightSide ? angle : Math.PI + angle;

          if (isRightSide) {
            labelShape.style.x = radius[1] + distance;
          } else {
            labelShape.style.x = -radius[1] - distance;
          }

          labelShape.style.y = 0;
          labelShape.position = center.slice();
        } else {
          labelShape.style.x = start[0];
          labelShape.style.y = start[1];
        } // zrender/Text并没有textColor属性，ctx fillStyle使用的是color


        labelShape.style.color = this.deepQuery([node.data, mainSerie], 'itemStyle.normal.label.textStyle.color') || '#000000';
        labelShape.style.textFont = this.getFont(this.deepQuery([node.data, mainSerie], 'itemStyle.normal.label.textStyle'));
        labelShape = new TextShape(labelShape);
        this.shapeList.push(labelShape);
        node.labelShape = labelShape;
      }, this);
    },
    _buildRibbons: function (series, serieIdx, graph, mainSerie) {
      var serie = series[serieIdx];
      var center = this.parseCenter(this.zr, mainSerie.center);
      var radius = this.parseRadius(this.zr, mainSerie.radius); // graph.edges.length = 1;

      graph.eachEdge(function (edge, idx) {
        var color; // 反向边

        var other = graph.getEdge(edge.node2, edge.node1);

        if (!other // 只有单边
        || edge.shape // 已经创建过Ribbon
        ) {
          return;
        }

        if (other.shape) {
          // 已经创建过Ribbon
          edge.shape = other.shape;
          return;
        }

        var s0 = edge.layout.startAngle / Math.PI * 180;
        var s1 = edge.layout.endAngle / Math.PI * 180;
        var t0 = other.layout.startAngle / Math.PI * 180;
        var t1 = other.layout.endAngle / Math.PI * 180;

        if (series.length === 1) {
          // 取小端的颜色
          if (edge.layout.weight <= other.layout.weight) {
            color = this.getColor(edge.node1.id);
          } else {
            color = this.getColor(edge.node2.id);
          }
        } else {
          //  使用系列颜色
          color = this.getColor(serie.name);
        }

        var queryTarget = this._getEdgeQueryTarget(serie, edge.data);

        var queryTargetEmphasis = this._getEdgeQueryTarget(serie, edge.data, 'emphasis');

        var ribbon = new RibbonShape({
          zlevel: serie.zlevel,
          z: serie.z,
          style: {
            x: center[0],
            y: center[1],
            r: radius[0],
            source0: s0,
            source1: s1,
            target0: t0,
            target1: t1,
            brushType: 'both',
            opacity: this.deepQuery(queryTarget, 'opacity'),
            color: color,
            lineWidth: this.deepQuery(queryTarget, 'borderWidth'),
            strokeColor: this.deepQuery(queryTarget, 'borderColor'),
            clockWise: mainSerie.clockWise
          },
          clickable: mainSerie.clickable,
          highlightStyle: {
            brushType: 'both',
            opacity: this.deepQuery(queryTargetEmphasis, 'opacity'),
            lineWidth: this.deepQuery(queryTargetEmphasis, 'borderWidth'),
            strokeColor: this.deepQuery(queryTargetEmphasis, 'borderColor')
          }
        });
        var node1, node2; // 从大端到小端

        if (edge.layout.weight <= other.layout.weight) {
          node1 = other.node1;
          node2 = other.node2;
        } else {
          node1 = edge.node1;
          node2 = edge.node2;
        }

        ecData.pack(ribbon, serie, serieIdx, edge.data, edge.rawIndex == null ? idx : edge.rawIndex, edge.data.name || node1.id + '-' + node2.id, // special
        node1.id, // special2
        node2.id);
        this.shapeList.push(ribbon);
        edge.shape = ribbon;
      }, this);
    },
    _buildEdgeCurves: function (series, serieIdx, graph, mainSerie, mainGraph) {
      var serie = series[serieIdx];
      var center = this.parseCenter(this.zr, mainSerie.center);
      graph.eachEdge(function (e, idx) {
        var node1 = mainGraph.getNodeById(e.node1.id);
        var node2 = mainGraph.getNodeById(e.node2.id);
        var shape1 = node1.shape;
        var shape2 = node2.shape;

        var queryTarget = this._getEdgeQueryTarget(serie, e.data);

        var queryTargetEmphasis = this._getEdgeQueryTarget(serie, e.data, 'emphasis');

        var curveShape = new BezierCurveShape({
          zlevel: serie.zlevel,
          z: serie.z,
          style: {
            xStart: shape1.position[0],
            yStart: shape1.position[1],
            xEnd: shape2.position[0],
            yEnd: shape2.position[1],
            cpX1: center[0],
            cpY1: center[1],
            lineWidth: this.deepQuery(queryTarget, 'width'),
            strokeColor: this.deepQuery(queryTarget, 'color'),
            opacity: this.deepQuery(queryTarget, 'opacity')
          },
          highlightStyle: {
            lineWidth: this.deepQuery(queryTargetEmphasis, 'width'),
            strokeColor: this.deepQuery(queryTargetEmphasis, 'color'),
            opacity: this.deepQuery(queryTargetEmphasis, 'opacity')
          }
        });
        ecData.pack(curveShape, serie, serieIdx, e.data, e.rawIndex == null ? idx : e.rawIndex, e.data.name || e.node1.id + '-' + e.node2.id, // special
        e.node1.id, // special2
        e.node2.id);
        this.shapeList.push(curveShape);
        e.shape = curveShape;
      }, this);
    },
    _buildScales: function (serie, serieIdx, graph) {
      var clockWise = serie.clockWise;
      var center = this.parseCenter(this.zr, serie.center);
      var radius = this.parseRadius(this.zr, serie.radius);
      var sign = clockWise ? 1 : -1;
      var sumValue = 0;
      var maxValue = -Infinity;
      var unitPostfix;
      var unitScale;

      if (serie.showScaleText) {
        graph.eachNode(function (node) {
          var val = node.data.value;

          if (val > maxValue) {
            maxValue = val;
          }

          sumValue += val;
        });

        if (maxValue > 1e10) {
          unitPostfix = 'b';
          unitScale = 1e-9;
        } else if (maxValue > 1e7) {
          unitPostfix = 'm';
          unitScale = 1e-6;
        } else if (maxValue > 1e4) {
          unitPostfix = 'k';
          unitScale = 1e-3;
        } else {
          unitPostfix = '';
          unitScale = 1;
        }
      }

      var unitValue = sumValue / (360 - serie.padding);
      graph.eachNode(function (node) {
        var startAngle = node.layout.startAngle / Math.PI * 180;
        var endAngle = node.layout.endAngle / Math.PI * 180;
        var scaleAngle = startAngle;

        while (true) {
          if (clockWise && scaleAngle > endAngle || !clockWise && scaleAngle < endAngle) {
            break;
          }

          var theta = scaleAngle / 180 * Math.PI;
          var v = [Math.cos(theta), Math.sin(theta)];
          var start = vec2.scale([], v, radius[1] + 1);
          vec2.add(start, start, center);
          var end = vec2.scale([], v, radius[1] + this.scaleLineLength);
          vec2.add(end, end, center);
          var scaleShape = new LineShape({
            zlevel: serie.zlevel,
            z: serie.z - 1,
            hoverable: false,
            style: {
              xStart: start[0],
              yStart: start[1],
              xEnd: end[0],
              yEnd: end[1],
              lineCap: 'round',
              brushType: 'stroke',
              strokeColor: '#666',
              lineWidth: 1
            }
          });
          this.shapeList.push(scaleShape);
          scaleAngle += sign * this.scaleUnitAngle;
        }

        if (!serie.showScaleText) {
          return;
        }

        var scaleTextAngle = startAngle;
        var step = unitValue * 5 * this.scaleUnitAngle;
        var scaleValue = 0;

        while (true) {
          if (clockWise && scaleTextAngle > endAngle || !clockWise && scaleTextAngle < endAngle) {
            break;
          }

          var theta = scaleTextAngle;
          theta = theta % 360;

          if (theta < 0) {
            theta += 360;
          }

          var isRightSide = theta <= 90 || theta >= 270;
          var textShape = new TextShape({
            zlevel: serie.zlevel,
            z: serie.z - 1,
            hoverable: false,
            style: {
              x: isRightSide ? radius[1] + this.scaleLineLength + 4 : -radius[1] - this.scaleLineLength - 4,
              y: 0,
              text: Math.round(scaleValue * 10) / 10 + unitPostfix,
              textAlign: isRightSide ? 'left' : 'right'
            },
            position: center.slice(),
            rotation: isRightSide ? [-theta / 180 * Math.PI, 0, 0] : [-(theta + 180) / 180 * Math.PI, 0, 0]
          });
          this.shapeList.push(textShape);
          scaleValue += step * unitScale;
          scaleTextAngle += sign * this.scaleUnitAngle * 5;
        }
      }, this);
    },
    refresh: function (newOption) {
      if (newOption) {
        this.option = newOption;
        this.series = newOption.series;
      }

      this.legend = this.component.legend;

      if (this.legend) {
        this.getColor = function (param) {
          return this.legend.getColor(param);
        };

        this.isSelected = function (param) {
          return this.legend.isSelected(param);
        };
      } else {
        var colorMap = {};
        var count = 0;

        this.getColor = function (key) {
          if (colorMap[key]) {
            return colorMap[key];
          }

          if (!colorMap[key]) {
            colorMap[key] = this.zr.getColor(count++);
          }

          return colorMap[key];
        };

        this.isSelected = function () {
          return true;
        };
      }

      this.backupShapeList();

      this._init();
    },
    reformOption: function (opt) {
      var _merge = zrUtil.merge;
      opt = _merge(_merge(opt || {}, this.ecTheme.chord), ecConfig.chord);
      opt.itemStyle.normal.label.textStyle = this.getTextStyle(opt.itemStyle.normal.label.textStyle);
      this.z = opt.z;
      this.zlevel = opt.zlevel;
    }
  };
  zrUtil.inherits(Chord, ChartBase); // 图表注册

  require('../chart').define('chord', Chord);

  return Chord;
}(require);