/**
 * echarts图表类：漏斗图
 *
 * @desc echarts基于Canvas，纯Javascript图表库，提供直观，生动，可交互，可个性化定制的数据统计图表。
 * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
 *
 */
define(function (require) {
    var ChartBase = require('./base');
    
    // 图形依赖
    var TextShape = require('zrender/shape/Text');
    var LineShape = require('zrender/shape/Line');
    var PolygonShape = require('zrender/shape/Polygon');

    var ecConfig = require('../config');
    // 漏斗图默认参数
    ecConfig.funnel = {
        zlevel: 0,                  // 一级层叠
        z: 2,                       // 二级层叠
        clickable: true,
        legendHoverLink: true,
        x: 80,
        y: 60,
        x2: 80,
        y2: 60,
        // width: {totalWidth} - x - x2,
        // height: {totalHeight} - y - y2,
        min: 0,
        max: 100,
        minSize: '0%',
        maxSize: '100%',
        sort: 'descending', // 'ascending', 'descending'
        gap: 0,
        funnelAlign: 'center',
        itemStyle: {
            normal: {
                // color: 各异,
                borderColor: '#fff',
                borderWidth: 1,
                label: {
                    show: true,
                    position: 'outer'
                    // formatter: 标签文本格式器，同Tooltip.formatter，不支持异步回调
                    // textStyle: null      // 默认使用全局文本样式，详见TEXTSTYLE
                },
                labelLine: {
                    show: true,
                    length: 10,
                    lineStyle: {
                        // color: 各异,
                        width: 1,
                        type: 'solid'
                    }
                }
            },
            emphasis: {
                // color: 各异,
                borderColor: 'rgba(0,0,0,0)',
                borderWidth: 1,
                label: {
                    show: true
                },
                labelLine: {
                    show: true
                }
            }
        }
    };

    var ecData = require('../util/ecData');
    var number = require('../util/number');
    var zrUtil = require('zrender/tool/util');
    var zrColor = require('zrender/tool/color');
    var zrArea = require('zrender/tool/area');
    
    /**
     * 构造函数
     * @param {Object} messageCenter echart消息中心
     * @param {ZRender} zr zrender实例
     * @param {Object} series 数据
     * @param {Object} component 组件
     */
    function Funnel(ecTheme, messageCenter, zr, option, myChart) {
        // 图表基类
        ChartBase.call(this, ecTheme, messageCenter, zr, option, myChart);
        this.refresh(option);
    }
    
    Funnel.prototype = {
        type: ecConfig.CHART_TYPE_FUNNEL,
        /**
         * 绘制图形
         */
        _buildShape: function () {
            var series = this.series;
            var legend = this.component.legend;
            // 复用参数索引
            this._paramsMap = {};
            this._selected = {};
            this.selectedMap = {};
            
            var serieName;
            for (var i = 0, l = series.length; i < l; i++) {
                if (series[i].type === ecConfig.CHART_TYPE_FUNNEL) {
                    series[i] = this.reformOption(series[i]);
                    this.legendHoverLink = series[i].legendHoverLink || this.legendHoverLink;
                    serieName = series[i].name || '';
                    // 系列图例开关
                    this.selectedMap[serieName] = legend ? legend.isSelected(serieName) : true;
                    if (!this.selectedMap[serieName]) {
                        continue;
                    }
                    this._buildSingleFunnel(i);
                    this.buildMark(i);
                }
            }

            this.addShapeList();
        },
        
        /**
         * 构建单个仪表盘
         *
         * @param {number} seriesIndex 系列索引
         */
        _buildSingleFunnel: function (seriesIndex) {
            var legend = this.component.legend;
            var serie = this.series[seriesIndex];
            var data = this._mapData(seriesIndex);
            var location = this._getLocation(seriesIndex);
            this._paramsMap[seriesIndex] = {
                location: location,
                data: data
            };
            
            var itemName;
            var total = 0;
            var selectedData = [];
            // 计算需要显示的个数和总值
            for (var i = 0, l = data.length; i < l; i++) {
                itemName = data[i].name;
                this.selectedMap[itemName] = legend ? legend.isSelected(itemName) : true;

                if (this.selectedMap[itemName] && !isNaN(data[i].value)) {
                    selectedData.push(data[i]);
                    total++;
                }
            }
            if (total === 0) {
                return;
            }
            // 可计算箱子
            var funnelCase = this._buildFunnelCase(seriesIndex);
            var align = serie.funnelAlign;
            var gap = serie.gap;
            var height = total > 1 
                         ? (location.height - (total - 1) * gap) / total : location.height;
            var width;
            var lastY = location.y;
            var lastWidth = serie.sort === 'descending'
                            ? this._getItemWidth(seriesIndex, selectedData[0].value)
                            : number.parsePercent(serie.minSize, location.width);
            var next = serie.sort === 'descending' ? 1 : 0;
            var centerX = location.centerX;
            var pointList= [];
            var x;
            var polygon;
            var lastPolygon;
            for (var i = 0, l = selectedData.length; i < l; i++) {
                itemName = selectedData[i].name;
                if (this.selectedMap[itemName] && !isNaN(selectedData[i].value)) {
                    width = i <= l - 2
                            ? this._getItemWidth(seriesIndex, selectedData[i + next].value)
                            : serie.sort === 'descending'
                              ? number.parsePercent(serie.minSize, location.width)
                              : number.parsePercent(serie.maxSize, location.width);
                    switch (align) {
                        case 'left':
                            x = location.x;
                            break;
                        case 'right':
                            x = location.x + location.width - lastWidth;
                            break;
                        default:
                            x = centerX - lastWidth / 2;
                    }
                    polygon = this._buildItem(
                        seriesIndex, selectedData[i]._index,
                        legend // color
                            ? legend.getColor(itemName) 
                            : this.zr.getColor(selectedData[i]._index),
                        x, lastY, lastWidth, width, height, align
                    );
                    lastY += height + gap;
                    lastPolygon = polygon.style.pointList;
                    
                    pointList.unshift([lastPolygon[0][0] - 10, lastPolygon[0][1]]); // 左
                    pointList.push([lastPolygon[1][0] + 10, lastPolygon[1][1]]);    // 右
                    if (i === 0) {
                        if (lastWidth === 0) {
                            lastPolygon = pointList.pop();
                            align == 'center' && (pointList[0][0] += 10);
                            align == 'right' && (pointList[0][0] = lastPolygon[0]);
                            pointList[0][1] -= align == 'center' ? 10 : 15;
                            if (l == 1) {
                                lastPolygon = polygon.style.pointList;
                            }
                        }
                        else {
                            pointList[pointList.length - 1][1] -= 5;
                            pointList[0][1] -=5;
                        }
                    }
                    lastWidth = width;
                }
            }
            
            if (funnelCase) {
                pointList.unshift([lastPolygon[3][0] - 10, lastPolygon[3][1]]); // 左
                pointList.push([lastPolygon[2][0] + 10, lastPolygon[2][1]]);    // 右
                if (lastWidth === 0) {
                    lastPolygon = pointList.pop();
                    align == 'center' && (pointList[0][0] += 10);
                    align == 'right' && (pointList[0][0] = lastPolygon[0]);
                    pointList[0][1] += align == 'center' ? 10 : 15;
                }
                else {
                    pointList[pointList.length - 1][1] += 5;
                    pointList[0][1] +=5;
                }
                funnelCase.style.pointList = pointList;
            }
        },
        
        _buildFunnelCase: function(seriesIndex) {
            var serie = this.series[seriesIndex];
            if (this.deepQuery([serie, this.option], 'calculable')) {
                var location = this._paramsMap[seriesIndex].location;
                var gap = 10;
                var funnelCase = {
                    hoverable: false,
                    style: {
                        pointListd: [
                            [location.x - gap, location.y - gap],
                            [location.x + location.width + gap, location.y - gap],
                            [location.x + location.width + gap, location.y + location.height + gap],
                            [location.x - gap, location.y + location.height + gap]
                        ],
                        brushType: 'stroke',
                        lineWidth: 1,
                        strokeColor: serie.calculableHolderColor
                                     || this.ecTheme.calculableHolderColor
                                     || ecConfig.calculableHolderColor
                    }
                };
                ecData.pack(funnelCase, serie, seriesIndex, undefined, -1);
                this.setCalculable(funnelCase);
                funnelCase = new PolygonShape(funnelCase);
                this.shapeList.push(funnelCase);
                return funnelCase;
            }
        },
        
        _getLocation: function (seriesIndex) {
            var gridOption = this.series[seriesIndex];
            var zrWidth = this.zr.getWidth();
            var zrHeight = this.zr.getHeight();
            var x = this.parsePercent(gridOption.x, zrWidth);
            var y = this.parsePercent(gridOption.y, zrHeight);

            var width = gridOption.width == null
                        ? (zrWidth - x - this.parsePercent(gridOption.x2, zrWidth))
                        : this.parsePercent(gridOption.width, zrWidth);

            return {
                x: x,
                y: y,
                width: width,
                height: gridOption.height == null
                        ? (zrHeight - y - this.parsePercent(gridOption.y2, zrHeight))
                        : this.parsePercent(gridOption.height, zrHeight),
                centerX: x + width / 2
            };
        },
        
        _mapData: function(seriesIndex) {
            var serie = this.series[seriesIndex];
            var funnelData = zrUtil.clone(serie.data);
            for (var i = 0, l = funnelData.length; i < l; i++) {
                funnelData[i]._index = i;
            }
            function numDescending (a, b) {
                if (a.value === '-') {
                    return 1;
                }
                else if (b.value === '-') {
                    return -1;
                }
                return b.value - a.value;
            }
            function numAscending (a, b) {
                return -numDescending(a, b);
            }
            if (serie.sort != 'none') {
                funnelData.sort(serie.sort === 'descending' ? numDescending : numAscending);
            }
            
            return funnelData;
        },
        
        /**
         * 构建单个扇形及指标
         */
        _buildItem: function (
            seriesIndex, dataIndex, defaultColor,
            x, y, topWidth, bottomWidth, height, align
        ) {
            var series = this.series;
            var serie = series[seriesIndex];
            var data = serie.data[dataIndex];
            
            // 漏斗
            var polygon = this.getPolygon(
                    seriesIndex, dataIndex, defaultColor,
                    x, y, topWidth, bottomWidth, height, align
                );
            ecData.pack(
                polygon,
                series[seriesIndex], seriesIndex,
                series[seriesIndex].data[dataIndex], dataIndex,
                series[seriesIndex].data[dataIndex].name
            );
            this.shapeList.push(polygon);

            // 文本标签
            var label = this.getLabel(
                    seriesIndex, dataIndex, defaultColor,
                    x, y, topWidth, bottomWidth, height, align
                );
            ecData.pack(
                label,
                series[seriesIndex], seriesIndex,
                series[seriesIndex].data[dataIndex], dataIndex,
                series[seriesIndex].data[dataIndex].name
            );
            this.shapeList.push(label);
            // 特定状态下是否需要显示文本标签
            if (!this._needLabel(serie, data,false)) {
                label.invisible = true;
            }

            // 文本标签视觉引导线
            var labelLine = this.getLabelLine(
                    seriesIndex, dataIndex, defaultColor,
                    x, y, topWidth, bottomWidth, height, align
                );
            this.shapeList.push(labelLine);
            // 特定状态下是否需要显示文本标签引导线
            if (!this._needLabelLine(serie, data,false)) {
                labelLine.invisible = true;
            }
            
            var polygonHoverConnect = [];
            var labelHoverConnect = [];
            if (this._needLabelLine(serie, data, true)) {
                polygonHoverConnect.push(labelLine.id);
                labelHoverConnect.push(labelLine.id);
            }
            if (this._needLabel(serie, data, true)) {
                polygonHoverConnect.push(label.id);
                labelHoverConnect.push(polygon.id);
            }
            polygon.hoverConnect = polygonHoverConnect;
            label.hoverConnect = labelHoverConnect;
            
            return polygon;
        },

        /**
         * 根据值计算宽度 
         */
        _getItemWidth: function (seriesIndex, value) {
            var serie = this.series[seriesIndex];
            var location = this._paramsMap[seriesIndex].location;
            var min = serie.min;
            var max = serie.max;
            var minSize = number.parsePercent(serie.minSize, location.width);
            var maxSize = number.parsePercent(serie.maxSize, location.width);
            return (value - min) * (maxSize - minSize) / (max - min) + minSize;
        },
        
        /**
         * 构建扇形
         */
        getPolygon: function (
            seriesIndex, dataIndex, defaultColor,
            xLT, y, topWidth, bottomWidth, height, align
        ) {
            var serie = this.series[seriesIndex];
            var data = serie.data[dataIndex];
            var queryTarget = [data, serie];

            // 多级控制
            var normal = this.deepMerge(queryTarget, 'itemStyle.normal') || {};
            var emphasis = this.deepMerge(queryTarget,'itemStyle.emphasis') || {};

            var normalColor = this.getItemStyleColor(normal.color, seriesIndex, dataIndex, data)
                              || defaultColor;
            
            var emphasisColor = this.getItemStyleColor(emphasis.color, seriesIndex, dataIndex, data)
                || (typeof normalColor === 'string'
                    ? zrColor.lift(normalColor, -0.2)
                    : normalColor
                );
            
            var  xLB;
            switch (align) {
                case 'left':
                    xLB = xLT;
                    break;
                case 'right':
                    xLB = xLT + (topWidth - bottomWidth);
                    break;
                default:
                    xLB = xLT + (topWidth - bottomWidth) / 2;
                    break;
            }
            var polygon = {
                zlevel: serie.zlevel,
                z: serie.z,
                clickable: this.deepQuery(queryTarget, 'clickable'),
                style: {
                    pointList: [
                        [xLT, y],
                        [xLT + topWidth, y],
                        [xLB + bottomWidth, y + height],
                        [xLB, y + height]
                    ],
                    brushType: 'both',
                    color: normalColor,
                    lineWidth: normal.borderWidth,
                    strokeColor: normal.borderColor
                },
                highlightStyle: {
                    color: emphasisColor,
                    lineWidth: emphasis.borderWidth,
                    strokeColor: emphasis.borderColor
                }
            };
            
            if (this.deepQuery([data, serie, this.option], 'calculable')) {
                this.setCalculable(polygon);
                polygon.draggable = true;
            }

            return new PolygonShape(polygon);
        },

        /**
         * 需要显示则会有返回构建好的shape，否则返回undefined
         */
        getLabel: function (
            seriesIndex, dataIndex, defaultColor,
            x, y, topWidth, bottomWidth, height, align
        ) {
            var serie = this.series[seriesIndex];
            var data = serie.data[dataIndex];
            var location = this._paramsMap[seriesIndex].location;
            // serie里有默认配置，放心大胆的用！
            var itemStyle = zrUtil.merge(
                    zrUtil.clone(data.itemStyle) || {},
                    serie.itemStyle
                );
            var status = 'normal';
            // label配置
            var labelControl = itemStyle[status].label;
            var textStyle = labelControl.textStyle || {};
            var lineLength = itemStyle[status].labelLine.length;

            var text = this.getLabelText(seriesIndex, dataIndex, status);
            var textFont = this.getFont(textStyle);
            var textAlign;
            var textColor = defaultColor;
            labelControl.position = labelControl.position 
                                    || itemStyle.normal.label.position;
            if (labelControl.position === 'inner'
                || labelControl.position === 'inside'
                || labelControl.position === 'center'
            ) {
                // 内部
                textAlign = align;
                textColor = 
                    Math.max(topWidth, bottomWidth) / 2 > zrArea.getTextWidth(text, textFont)
                    ? '#fff' : zrColor.reverse(defaultColor);
            }
            else if (labelControl.position === 'left'){
                // 左侧显示
                textAlign = 'right';
            }
            else {
                // 右侧显示，默认 labelControl.position === 'outer' || 'right)
                textAlign = 'left';
            }
            
            var textShape = {
                zlevel: serie.zlevel,
                z: serie.z + 1,
                style: {
                    x: this._getLabelPoint(
                           labelControl.position, x, location,
                           topWidth, bottomWidth,lineLength, align
                       ),
                    y: y + height / 2,
                    color: textStyle.color || textColor,
                    text: text,
                    textAlign: textStyle.align || textAlign,
                    textBaseline: textStyle.baseline || 'middle',
                    textFont: textFont
                }
            };
            
            //----------高亮
            status = 'emphasis';
            // label配置
            labelControl = itemStyle[status].label || labelControl;
            textStyle = labelControl.textStyle || textStyle;
            lineLength = itemStyle[status].labelLine.length || lineLength;
            labelControl.position = labelControl.position || itemStyle.normal.label.position;
            text = this.getLabelText(seriesIndex, dataIndex, status);
            textFont = this.getFont(textStyle);
            textColor = defaultColor;
            if (labelControl.position === 'inner' 
                || labelControl.position === 'inside'
                || labelControl.position === 'center'
            ) {
                // 内部
                textAlign = align;
                textColor = 
                    Math.max(topWidth, bottomWidth) / 2 > zrArea.getTextWidth(text, textFont)
                    ? '#fff' : zrColor.reverse(defaultColor);
            }
            else if (labelControl.position === 'left'){
                // 左侧显示
                textAlign = 'right';
            }
            else {
                // 右侧显示，默认 labelControl.position === 'outer' || 'right)
                textAlign = 'left';
            }
            
            textShape.highlightStyle = {
                x: this._getLabelPoint(
                       labelControl.position, x, location,
                       topWidth, bottomWidth,lineLength, align
                   ),
                color: textStyle.color || textColor,
                text: text,
                textAlign: textStyle.align || textAlign,
                textFont: textFont,
                brushType: 'fill'
            };
            
            return new TextShape(textShape);
        },

        /**
         * 根据lable.format计算label text
         */
        getLabelText: function (seriesIndex, dataIndex, status) {
            var series = this.series;
            var serie = series[seriesIndex];
            var data = serie.data[dataIndex];
            var formatter = this.deepQuery(
                [data, serie],
                'itemStyle.' + status + '.label.formatter'
            );
            
            if (formatter) {
                if (typeof formatter === 'function') {
                    return formatter.call(
                        this.myChart,
                        {
                            seriesIndex: seriesIndex,
                            seriesName: serie.name || '',
                            series: serie,
                            dataIndex: dataIndex,
                            data: data,
                            name: data.name,
                            value: data.value
                        }
                    );
                }
                else if (typeof formatter === 'string') {
                    formatter = formatter.replace('{a}','{a0}')
                                         .replace('{b}','{b0}')
                                         .replace('{c}','{c0}')
                                         .replace('{a0}', serie.name)
                                         .replace('{b0}', data.name)
                                         .replace('{c0}', data.value);
    
                    return formatter;
                }
            }
            else {
                return data.name;
            }
        },
        
        /**
         * 需要显示则会有返回构建好的shape，否则返回undefined
         */
        getLabelLine: function (
            seriesIndex, dataIndex, defaultColor,
            x, y, topWidth, bottomWidth, height, align
        ) {
            var serie = this.series[seriesIndex];
            var data = serie.data[dataIndex];
            var location = this._paramsMap[seriesIndex].location;

            // serie里有默认配置，放心大胆的用！
            var itemStyle = zrUtil.merge(
                    zrUtil.clone(data.itemStyle) || {},
                    serie.itemStyle
                );
            var status = 'normal';
            // labelLine配置
            var labelLineControl = itemStyle[status].labelLine;
            var lineLength = itemStyle[status].labelLine.length;
            var lineStyle = labelLineControl.lineStyle || {};
            
            var labelControl = itemStyle[status].label;
            labelControl.position = labelControl.position 
                                    || itemStyle.normal.label.position;

            var lineShape = {
                zlevel: serie.zlevel,
                z: serie.z + 1,
                hoverable: false,
                style: {
                    xStart: this._getLabelLineStartPoint(x, location, topWidth, bottomWidth, align),
                    yStart: y + height / 2,
                    xEnd: this._getLabelPoint(
                              labelControl.position, x, location,
                              topWidth, bottomWidth,lineLength, align
                          ),
                    yEnd: y + height / 2,
                    strokeColor: lineStyle.color || defaultColor,
                    lineType: lineStyle.type,
                    lineWidth: lineStyle.width
                }
            };
            
            status = 'emphasis';
            // labelLine配置
            labelLineControl = itemStyle[status].labelLine || labelLineControl;
            lineLength = itemStyle[status].labelLine.length || lineLength;
            lineStyle = labelLineControl.lineStyle || lineStyle;

            labelControl = itemStyle[status].label || labelControl;
            labelControl.position = labelControl.position;
            
            lineShape.highlightStyle = {
                xEnd: this._getLabelPoint(
                          labelControl.position, x, location,
                          topWidth, bottomWidth,lineLength, align
                      ),
                strokeColor: lineStyle.color || defaultColor,
                lineType: lineStyle.type,
                lineWidth: lineStyle.width
            };
            
            return new LineShape(lineShape);
        },
        
        _getLabelPoint: function(position, x, location, topWidth, bottomWidth, lineLength, align) {
            position = (position === 'inner' || position === 'inside') ? 'center' : position;
            switch (position) {
                case 'center':
                    return align == 'center'
                            ? (x + topWidth / 2)
                            : align == 'left' ? (x + 10) : (x + topWidth - 10);
                case 'left':
                    // 左侧文本
                    if (lineLength === 'auto') {
                        return location.x - 10;
                    }
                    else {
                        return align == 'center'
                            // 居中布局
                            ? (location.centerX - Math.max(topWidth, bottomWidth) / 2 - lineLength)
                            : align == 'right'
                                // 右对齐布局
                                ? (x 
                                    - (topWidth < bottomWidth ? (bottomWidth - topWidth) : 0)
                                    - lineLength
                                )
                                // 左对齐布局
                                : (location.x - lineLength);
                    }
                    break;
                default:
                    // 右侧文本
                    if (lineLength === 'auto') {
                        return location.x + location.width + 10;
                    }
                    else {
                        return align == 'center'
                            // 居中布局
                            ? (location.centerX + Math.max(topWidth, bottomWidth) / 2 + lineLength)
                            : align == 'right'
                                // 右对齐布局
                                ? (location.x + location.width + lineLength)
                                // 左对齐布局
                                : (x + Math.max(topWidth, bottomWidth) + lineLength);
                    }
            }
        },
        
        _getLabelLineStartPoint: function(x, location, topWidth, bottomWidth, align) {
            return align == 'center'
                   ? location.centerX 
                   : topWidth < bottomWidth
                     ? (x + Math.min(topWidth, bottomWidth) / 2)
                     : (x + Math.max(topWidth, bottomWidth) / 2);
        },

        /**
         * 返回特定状态（normal or emphasis）下是否需要显示label标签文本
         * @param {Object} serie
         * @param {Object} data
         * @param {boolean} isEmphasis true is 'emphasis' and false is 'normal'
         */
        _needLabel: function (serie, data, isEmphasis) {
            return this.deepQuery(
                [data, serie],
                'itemStyle.'
                + (isEmphasis ? 'emphasis' : 'normal')
                + '.label.show'
            );
        },

        /**
         * 返回特定状态（normal or emphasis）下是否需要显示labelLine标签视觉引导线
         * @param {Object} serie
         * @param {Object} data
         * @param {boolean} isEmphasis true is 'emphasis' and false is 'normal'
         */
        _needLabelLine: function (serie, data, isEmphasis) {
            return this.deepQuery(
                [data, serie],
                'itemStyle.'
                + (isEmphasis ? 'emphasis' : 'normal')
                +'.labelLine.show'
            );
        },
        
        /**
         * 刷新
         */
        refresh: function (newOption) {
            if (newOption) {
                this.option = newOption;
                this.series = newOption.series;
            }
            
            this.backupShapeList();
            this._buildShape();
        }
    };
    
    zrUtil.inherits(Funnel, ChartBase);
    
    // 图表注册
    require('../chart').define('funnel', Funnel);
    
    return Funnel;
});