/**
 * @class Ext.chart.series.sprite.Pie3DPart
 * @extends Ext.draw.sprite.Path
 * 
 * Pie3D series sprite.
 */
Ext.define('Ext.chart.series.sprite.Pie3DPart', {
    extend: 'Ext.draw.sprite.Path',
    mixins: {
        markerHolder: 'Ext.chart.MarkerHolder'
    },
    alias: 'sprite.pie3dPart',
    type: 'pie3dPart',
    inheritableStatics: {
        def: {
            processors: {
                /**
                 * @cfg {Number} [centerX=0] The central point of the series on the x-axis.
                 */
                centerX: 'number',

                /**
                 * @cfg {Number} [centerY=0] The central point of the series on the x-axis.
                 */
                centerY: 'number',

                /**
                 * @cfg {Number} [startAngle=0] The starting angle of the polar series.
                 */
                startAngle: 'number',

                /**
                 * @cfg {Number} [endAngle=Math.PI] The ending angle of the polar series.
                 */
                endAngle: 'number',

                /**
                 * @cfg {Number} [startRho=0] The starting radius of the polar series.
                 */
                startRho: 'number',

                /**
                 * @cfg {Number} [endRho=150] The ending radius of the polar series.
                 */
                endRho: 'number',

                /**
                 * @cfg {Number} [margin=0] Margin from the center of the pie. Used for donut.
                 */
                margin: 'number',

                /**
                 * @cfg {Number} [thickness=0] The thickness of the 3D pie part.
                 */
                thickness: 'number',

                /**
                 * @cfg {Number} [distortion=0] The distortion of the 3D pie part.
                 */
                distortion: 'number',

                /**
                 * @cfg {Object} [baseColor='white'] The color of the 3D pie part before adding the 3D effect.
                 */
                baseColor: 'color',

                /**
                 * @cfg {Number} [baseRotation=0] The starting rotation of the polar series.
                 */
                baseRotation: 'number',

                /**
                 * @cfg {String} [part='top'] The part of the 3D Pie represented by the sprite.
                 */
                part: 'enums(top,start,end,inner,outer)'
            },
            aliases: {
                rho: 'endRho'
            },
            triggers: {
                centerX: 'path,bbox',
                centerY: 'path,bbox',
                startAngle: 'path,partZIndex',
                endAngle: 'path,partZIndex',
                startRho: 'path',
                endRho: 'path,bbox',
                margin: 'path,bbox',
                thickness: 'path',
                baseRotation: 'path,partZIndex',
                baseColor: 'partZIndex,partColor',
                part: 'path,partZIndex'
            },
            defaults: {
                centerX: 0,
                centerY: 0,
                startAngle: 0,
                endAngle: 0,
                startRho: 0,
                endRho: 150,
                margin: 0,
                distortion: 1,
                baseRotation: 0,
                baseColor: 'white',
                miterLimit: 1,
                part: 'top'
            },
            updaters: {
                partColor: function (attr) {
                    var color = Ext.draw.Color.fly(attr.baseColor),
                        fillStyle;

                    switch (attr.part) {
                        case 'top':
                            fillStyle = color.toString();
                            break;
                        case 'outer':
                            fillStyle = Ext.create('Ext.draw.gradient.Linear', {
                                type: 'linear',
                                stops: [
                                    {
                                        offset: 0,
                                        color: color.createDarker(0.3).toString()
                                    },
                                    {
                                        offset: 0.3,
                                        color: color.toString()
                                    },
                                    {
                                        offset: 0.8,
                                        color: color.createLighter(0.2).toString()
                                    },
                                    {
                                        offset: 1,
                                        color: color.createDarker(0.4).toString()
                                    }
                                ]
                            });
                            break;
                        case 'start':
                            fillStyle = color.createDarker(0.3).toString();
                            break;
                        case 'end':
                            fillStyle = color.createDarker(0.3).toString();
                            break;
                        case 'inner':
                            fillStyle = Ext.create('Ext.draw.gradient.Linear', {
                                type: 'linear',
                                stops: [
                                    {
                                        offset: 0,
                                        color: color.createDarker(0.4).toString()
                                    },
                                    {
                                        offset: 0.2,
                                        color: color.createLighter(0.2).toString()
                                    },
                                    {
                                        offset: 0.7,
                                        color: color.toString()
                                    },
                                    {
                                        offset: 1,
                                        color: color.createDarker(0.3).toString()
                                    }
                                ]
                            });
                            break;
                    }

                    attr.fillStyle = fillStyle;
                    attr.canvasAttributes.fillStyle = fillStyle;
                },
                partZIndex: function (attr) {
                    var rotation = attr.baseRotation,
                        midAngle = (attr.startAngle + attr.endAngle) * 0.5,
                        depth = Math.sin(midAngle + rotation);

                    switch (attr.part) {
                        case 'top':
                            attr.zIndex = 5;
                            break;
                        case 'outer':
                            attr.zIndex = 4 + depth;
                            break;
                        case 'start':
                            attr.zIndex = 1 + Math.sin(attr.startAngle + rotation);
                            break;
                        case 'end':
                            attr.zIndex = 1 + Math.sin(attr.endAngle + rotation);
                            break;
                        case 'inner':
                            attr.zIndex = 1 + depth;
                            break;
                    }
                    attr.dirtyZIndex = true;
                }
            }
        }
    },

    updatePlainBBox: function (plain) {
        var attr = this.attr,
            rho = attr.part === 'inner' ? attr.startRho : attr.endRho;
        plain.width = rho * 2;
        plain.height = rho * attr.distortion * 2 + attr.thickness;
        plain.x = attr.centerX - rho;
        plain.y = attr.centerY - rho * attr.distortion;
    },
    
    updateTransformedBBox: function (transform) {
        return this.updatePlainBBox(transform);
    },
    
    updatePath: function (path) {
        if (this.attr.endAngle < this.attr.startAngle) {
            return;
        }
        this[this.attr.part + 'Renderer'](path);
    },

    topRenderer: function (path) {
        var attr = this.attr,
            margin = attr.margin,
            distortion = attr.distortion,
            centerX = attr.centerX,
            centerY = attr.centerY,
            baseRotation = attr.baseRotation,
            startAngle = attr.startAngle + baseRotation,
            endAngle = attr.endAngle + baseRotation,
            midAngle = (startAngle + endAngle) * 0.5,
            startRho = attr.startRho,
            endRho = attr.endRho,
            sinEnd = Math.sin(endAngle),
            cosEnd = Math.cos(endAngle);

        centerX += Math.cos(midAngle) * margin;
        centerY += Math.sin(midAngle) * margin * distortion;
        path.ellipse(centerX, centerY, startRho, startRho * distortion, 0, startAngle, endAngle, false);
        path.lineTo(centerX + cosEnd * endRho, centerY + sinEnd * endRho * distortion);
        path.ellipse(centerX, centerY, endRho, endRho * distortion, 0, endAngle, startAngle, true);
        path.closePath();
    },

    startRenderer: function (path) {
        var attr = this.attr,
            margin = attr.margin,
            centerX = attr.centerX,
            centerY = attr.centerY,
            distortion = attr.distortion,
            baseRotation = attr.baseRotation,
            startAngle = attr.startAngle + baseRotation ,
            endAngle = attr.endAngle + baseRotation,
            thickness = attr.thickness,
            startRho = attr.startRho,
            endRho = attr.endRho,
            sinStart = Math.sin(startAngle),
            cosStart = Math.cos(startAngle),
            midAngle;

        if (cosStart < 0) {
            midAngle = (startAngle + endAngle) * 0.5;
            centerX += Math.cos(midAngle) * margin;
            centerY += Math.sin(midAngle) * margin * distortion;
            path.moveTo(centerX + cosStart * startRho, centerY + sinStart * startRho * distortion);
            path.lineTo(centerX + cosStart * endRho, centerY + sinStart * endRho * distortion);
            path.lineTo(centerX + cosStart * endRho, centerY + sinStart * endRho * distortion + thickness);
            path.lineTo(centerX + cosStart * startRho, centerY + sinStart * startRho * distortion + thickness);
            path.closePath();
        }
    },

    endRenderer: function (path) {
        var attr = this.attr,
            margin = attr.margin,
            centerX = attr.centerX,
            centerY = attr.centerY,
            distortion = attr.distortion,
            baseRotation = attr.baseRotation,
            startAngle = attr.startAngle + baseRotation ,
            endAngle = attr.endAngle + baseRotation,
            thickness = attr.thickness,
            startRho = attr.startRho,
            endRho = attr.endRho,
            sin = Math.sin(endAngle),
            cos = Math.cos(endAngle),
            midAngle;

        if (cos > 0) {
            midAngle = (startAngle + endAngle) * 0.5;
            centerX += Math.cos(midAngle) * margin;
            centerY += Math.sin(midAngle) * margin * distortion;
            path.moveTo(centerX + cos * startRho, centerY + sin * startRho * distortion);
            path.lineTo(centerX + cos * endRho, centerY + sin * endRho * distortion);
            path.lineTo(centerX + cos * endRho, centerY + sin * endRho * distortion + thickness);
            path.lineTo(centerX + cos * startRho, centerY + sin * startRho * distortion + thickness);
            path.closePath();
        }
    },

    innerRenderer: function (path) {
        var attr = this.attr,
            margin = attr.margin,
            centerX = attr.centerX,
            centerY = attr.centerY,
            distortion = attr.distortion,
            baseRotation = attr.baseRotation,
            startAngle = attr.startAngle + baseRotation ,
            endAngle = attr.endAngle + baseRotation,
            midAngle = (startAngle + endAngle) * 0.5,
            thickness = attr.thickness,
            startRho = attr.startRho,
            isTranslucent = attr.globalAlpha < 1,
            sinEnd, cosEnd,
            tempStart, tempEnd;

        centerX += Math.cos(midAngle) * margin;
        centerY += Math.sin(midAngle) * margin * distortion;
        if (startAngle >= Math.PI * 2 || isTranslucent) {
            startAngle -= Math.PI * 2;
            endAngle -= Math.PI * 2;
        }
        if (endAngle > Math.PI && endAngle < Math.PI * 3 || isTranslucent) {
            tempStart = startAngle;
            tempEnd = Math.min(endAngle, Math.PI * 2);
            sinEnd = Math.sin(tempEnd);
            cosEnd = Math.cos(tempEnd);
            path.ellipse(centerX, centerY, startRho, startRho * distortion, 0, tempStart, tempEnd, false);
            path.lineTo(centerX + cosEnd * startRho, centerY + sinEnd * startRho * distortion + thickness);
            path.ellipse(centerX, centerY + thickness, startRho, startRho * distortion, 0, tempEnd, tempStart, true);
            path.closePath();
        }
        if (endAngle > Math.PI * 3) {
            tempStart = Math.PI;
            tempEnd = endAngle;
            sinEnd = Math.sin(tempEnd);
            cosEnd = Math.cos(tempEnd);
            path.ellipse(centerX, centerY, startRho, startRho * distortion, 0, tempStart, tempEnd, false);
            path.lineTo(centerX + cosEnd * startRho, centerY + sinEnd * startRho * distortion + thickness);
            path.ellipse(centerX, centerY + thickness, startRho, startRho * distortion, 0, tempEnd, tempStart, true);
            path.closePath();
        }
    },

    outerRenderer: function (path) {
        var attr = this.attr,
            margin = attr.margin,
            centerX = attr.centerX,
            centerY = attr.centerY,
            distortion = attr.distortion,
            baseRotation = attr.baseRotation,
            startAngle = attr.startAngle + baseRotation ,
            endAngle = attr.endAngle + baseRotation,
            midAngle = (startAngle + endAngle) * 0.5,
            thickness = attr.thickness,
            endRho = attr.endRho,
            isTranslucent = attr.globalAlpha < 1,
            sinEnd, cosEnd,
            tempStart, tempEnd;

        centerX += Math.cos(midAngle) * margin;
        centerY += Math.sin(midAngle) * margin * distortion;

        if (startAngle >= Math.PI * 2 || isTranslucent) {
            startAngle -= Math.PI * 4;
            endAngle -= Math.PI * 4;
        }

        if (startAngle < Math.PI || isTranslucent) {
            tempStart = startAngle;
            tempEnd = Math.min(endAngle, Math.PI);
            sinEnd = Math.sin(tempEnd);
            cosEnd = Math.cos(tempEnd);
            path.ellipse(centerX, centerY, endRho, endRho * distortion, 0, tempStart, tempEnd, false);
            path.lineTo(centerX + cosEnd * endRho, centerY + sinEnd * endRho * distortion + thickness);
            path.ellipse(centerX, centerY + thickness, endRho, endRho * distortion, 0, tempEnd, tempStart, true);
            path.closePath();
        }
        if (endAngle > Math.PI * 2) {
            tempStart = Math.max(startAngle, Math.PI * 2);
            tempEnd = endAngle;
            sinEnd = Math.sin(tempEnd);
            cosEnd = Math.cos(tempEnd);
            path.ellipse(centerX, centerY, endRho, endRho * distortion, 0, tempStart, tempEnd, false);
            path.lineTo(centerX + cosEnd * endRho, centerY + sinEnd * endRho * distortion + thickness);
            path.ellipse(centerX, centerY + thickness, endRho, endRho * distortion, 0, tempEnd, tempStart, true);
            path.closePath();
        }
    }
});