/**
 * echarts图表类：chord diagram
 *
 * @author pissang (https://github.com/pissang/)
 * 
 * TODO 非Ribbon Type 支持 undirected graph ?
 */

define(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;
                    if (serie.data && serie.matrix) {
                        graph = this._getSerieGraphFromDataMatrix(
                            serie, mainSerie
                        );
                    } else if (serie.nodes && 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: this.getZlevelBase(),
                    z : this.getZBase(),
                    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: this.getZlevelBase(),
                    z: this.getZBase() + 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: this.getZlevelBase(),
                    z: this.getZBase() + 1,
                    hoverable: false,
                    style: {
                        text: node.data.label == null ? node.id : node.data.label,
                        textAlign: isRightSide ? 'left' : 'right',
                        color: labelColor || '#000000'
                    }
                };
                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];
                }
                labelShape.style.textColor = this.deepQuery(
                    [node.data, mainSerie],
                    'itemStyle.normal.label.textStyle.color'
                ) || '#fff';
                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: this.getZlevelBase(),
                    z: this.getZBase(),
                    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: this.getZlevelBase(),
                    z: this.getZBase(),
                    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: this.getZlevelBase(),
                        z: this.getZBase() - 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: this.getZlevelBase(),
                        z: this.getZBase() - 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;
});