/**
 * echarts图表类：漏斗图
 *
 * @desc echarts基于Canvas，纯Javascript图表库，提供直观，生动，可交互，可个性化定制的数据统计图表。
 * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
 *
 */
module.exports = 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;
}(require);