/**

 * @license 

 * Highcharts funnel module

 *

 * (c) 2010-2014 Torstein Honsi

 *

 * 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,

	pick = Highcharts.pick;



// set default options

defaultPlotOptions.funnel = merge(defaultPlotOptions.pie, {

	animation: false,

	center: ['50%', '50%'],

	width: '90%',

	neckWidth: '30%',

	height: '100%',

	neckHeight: '25%',

	reversed: false,

	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,

			options = series.options,

			reversed = options.reversed,

			ignoreHiddenPoint = options.ignoreHiddenPoint,

			plotWidth = chart.plotWidth,

			plotHeight = chart.plotHeight,

			cumulative = 0, // start at top

			center = options.center,

			centerX = getLength(center[0], plotWidth),

			centerY = getLength(center[1], 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(reversed ? plotHeight - y : 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) {

			if (!ignoreHiddenPoint || point.visible !== false) {

				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;

			}



			if (reversed) {

				y1 = height - y1;

				y3 = height - y3;

				y5 = (y5 ? height - y5 : null);

			}

			// 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;



			if (!ignoreHiddenPoint || point.visible !== false) {

				cumulative += fraction;

			}

		});		

	},

	/**

	 * 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 pointOptions = point.options,

				graphic = point.graphic,

				shapeArgs = point.shapeArgs;



			if (!graphic) { // Create the shapes				

				point.graphic = renderer.path(shapeArgs).

					attr({

						fill: point.color,

						stroke: pick(pointOptions.borderColor, options.borderColor),

						'stroke-width': pick(pointOptions.borderWidth, options.borderWidth)

					}).

					add(series.group);

					

			} else { // Update the shapes

				graphic.animate(shapeArgs);

			}

		});

	},



	/**

	 * Funnel items don't have angles (#2289)

	 */

	sortByAngle: function (points) {

		points.sort(function (a, b) {

			return a.plotY - b.plotY;

		});

	},

	

	/**

	 * 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);

	}



});



/** 

 * Pyramid series type.

 * A pyramid series is a special type of funnel, without neck and reversed by default.

 */

defaultOptions.plotOptions.pyramid = Highcharts.merge(defaultOptions.plotOptions.funnel, {        

	neckWidth: '0%',

	neckHeight: '0%',

	reversed: true

});

Highcharts.seriesTypes.pyramid = Highcharts.extendClass(Highcharts.seriesTypes.funnel, {

	type: 'pyramid'

});



}(Highcharts));

