/**
 * @license
 * Highcharts funnel module, Beta
 *
 * (c) 2010-2012 Torstein Hønsi
 *
 * License: www.highcharts.com/license
 */

/*global Highcharts */
(function (Highcharts) {
  'use strict';

  // create shortcuts
  var defaultOptions = Highcharts.getOptions(),
    defaultPlotOptions = defaultOptions.plotOptions,
    seriesTypes = Highcharts.seriesTypes,
    merge = Highcharts.merge,
    noop = function () {},
    each = Highcharts.each;

  // set default options
  defaultPlotOptions.funnel = merge(defaultPlotOptions.pie, {
    center: ['50%', '50%'],
    width: '90%',
    neckWidth: '30%',
    height: '100%',
    neckHeight: '25%',

    dataLabels: {
      //position: 'right',
      connectorWidth: 1,
      connectorColor: '#606060',
    },
    size: true, // to avoid adapting to data label size in Pie.drawDataLabels
    states: {
      select: {
        color: '#C0C0C0',
        borderColor: '#000000',
        shadow: false,
      },
    },
  });

  seriesTypes.funnel = Highcharts.extendClass(seriesTypes.pie, {
    type: 'funnel',
    animate: noop,

    /**
     * Overrides the pie translate method
     */
    translate: function () {
      var // Get positions - either an integer or a percentage string must be given
        getLength = function (length, relativeTo) {
          return /%$/.test(length)
            ? (relativeTo * parseInt(length, 10)) / 100
            : parseInt(length, 10);
        },
        sum = 0,
        series = this,
        chart = series.chart,
        plotWidth = chart.plotWidth,
        plotHeight = chart.plotHeight,
        cumulative = 0, // start at top
        options = series.options,
        center = options.center,
        centerX = getLength(center[0], plotWidth),
        centerY = getLength(center[0], plotHeight),
        width = getLength(options.width, plotWidth),
        tempWidth,
        getWidthAt,
        height = getLength(options.height, plotHeight),
        neckWidth = getLength(options.neckWidth, plotWidth),
        neckHeight = getLength(options.neckHeight, plotHeight),
        neckY = height - neckHeight,
        data = series.data,
        path,
        fraction,
        half = options.dataLabels.position === 'left' ? 1 : 0,
        x1,
        y1,
        x2,
        x3,
        y3,
        x4,
        y5;

      // Return the width at a specific y coordinate
      series.getWidthAt = getWidthAt = function (y) {
        return y > height - neckHeight || height === neckHeight
          ? neckWidth
          : neckWidth + (width - neckWidth) * ((height - neckHeight - y) / (height - neckHeight));
      };
      series.getX = function (y, half) {
        return centerX + (half ? -1 : 1) * (getWidthAt(y) / 2 + options.dataLabels.distance);
      };

      // Expose
      series.center = [centerX, centerY, height];
      series.centerX = centerX;

      /*
       * Individual point coordinate naming:
       *
       * x1,y1 _________________ x2,y1
       *  \                         /
       *   \                       /
       *    \                     /
       *     \                   /
       *      \                 /
       *     x3,y3 _________ x4,y3
       *
       * Additional for the base of the neck:
       *
       *       |               |
       *       |               |
       *       |               |
       *     x3,y5 _________ x4,y5
       */

      // get the total sum
      each(data, function (point) {
        sum += point.y;
      });

      each(data, function (point) {
        // set start and end positions
        y5 = null;
        fraction = sum ? point.y / sum : 0;
        y1 = centerY - height / 2 + cumulative * height;
        y3 = y1 + fraction * height;
        //tempWidth = neckWidth + (width - neckWidth) * ((height - neckHeight - y1) / (height - neckHeight));
        tempWidth = getWidthAt(y1);
        x1 = centerX - tempWidth / 2;
        x2 = x1 + tempWidth;
        tempWidth = getWidthAt(y3);
        x3 = centerX - tempWidth / 2;
        x4 = x3 + tempWidth;

        // the entire point is within the neck
        if (y1 > neckY) {
          x1 = x3 = centerX - neckWidth / 2;
          x2 = x4 = centerX + neckWidth / 2;

          // the base of the neck
        } else if (y3 > neckY) {
          y5 = y3;

          tempWidth = getWidthAt(neckY);
          x3 = centerX - tempWidth / 2;
          x4 = x3 + tempWidth;

          y3 = neckY;
        }

        // save the path
        path = ['M', x1, y1, 'L', x2, y1, x4, y3];
        if (y5) {
          path.push(x4, y5, x3, y5);
        }
        path.push(x3, y3, 'Z');

        // prepare for using shared dr
        point.shapeType = 'path';
        point.shapeArgs = { d: path };

        // for tooltips and data labels
        point.percentage = fraction * 100;
        point.plotX = centerX;
        point.plotY = (y1 + (y5 || y3)) / 2;

        // Placement of tooltips and data labels
        point.tooltipPos = [centerX, point.plotY];

        // Slice is a noop on funnel points
        point.slice = noop;

        // Mimicking pie data label placement logic
        point.half = half;

        cumulative += fraction;
      });

      series.setTooltipPoints();
    },
    /**
     * Draw a single point (wedge)
     * @param {Object} point The point object
     * @param {Object} color The color of the point
     * @param {Number} brightness The brightness relative to the color
     */
    drawPoints: function () {
      var series = this,
        options = series.options,
        chart = series.chart,
        renderer = chart.renderer;

      each(series.data, function (point) {
        var graphic = point.graphic,
          shapeArgs = point.shapeArgs;

        if (!graphic) {
          // Create the shapes
          point.graphic = renderer
            .path(shapeArgs)
            .attr({
              fill: point.color,
              stroke: options.borderColor,
              'stroke-width': options.borderWidth,
            })
            .add(series.group);
        } else {
          // Update the shapes
          graphic.animate(shapeArgs);
        }
      });
    },

    /**
     * Funnel items don't have angles (#2289)
     */
    sortByAngle: noop,

    /**
     * Extend the pie data label method
     */
    drawDataLabels: function () {
      var data = this.data,
        labelDistance = this.options.dataLabels.distance,
        leftSide,
        sign,
        point,
        i = data.length,
        x,
        y;

      // In the original pie label anticollision logic, the slots are distributed
      // from one labelDistance above to one labelDistance below the pie. In funnels
      // we don't want this.
      this.center[2] -= 2 * labelDistance;

      // Set the label position array for each point.
      while (i--) {
        point = data[i];
        leftSide = point.half;
        sign = leftSide ? 1 : -1;
        y = point.plotY;
        x = this.getX(y, leftSide);

        // set the anchor point for data labels
        point.labelPos = [
          0, // first break of connector
          y, // a/a
          x + (labelDistance - 5) * sign, // second break, right outside point shape
          y, // a/a
          x + labelDistance * sign, // landing point for connector
          y, // a/a
          leftSide ? 'right' : 'left', // alignment
          0, // center angle
        ];
      }

      seriesTypes.pie.prototype.drawDataLabels.call(this);
    },
  });
})(Highcharts);