/**
 * @class Ext.chart.series.Gauge
 * @extends Ext.chart.series.Series
 *
 * Creates a Gauge Chart. Gauge Charts are used to show progress in a certain variable. There are two ways of using the Gauge chart.
 * One is setting a store element into the Gauge and selecting the field to be used from that store. Another one is instanciating the
 * visualization and using the `setValue` method to adjust the value you want.
 *
 * A chart/series configuration for the Gauge visualization could look like this:
 *
 *     {
 *         xtype: 'chart',
 *         store: store,
 *         axes: [{
 *             type: 'gauge',
 *             position: 'gauge',
 *             minimum: 0,
 *             maximum: 100,
 *             steps: 10,
 *             margin: -10
 *         }],
 *         series: [{
 *             type: 'gauge',
 *             field: 'data1',
 *             donut: false,
 *             colorSet: ['#F49D10', '#ddd']
 *         }]
 *     }
 *
 * In this configuration we create a special Gauge axis to be used with the gauge visualization (describing half-circle markers), and also we're
 * setting a maximum, minimum and steps configuration options into the axis. The Gauge series configuration contains the store field to be bound to
 * the visual display and the color set to be used with the visualization.
 *
 * @xtype gauge
 */

Ext.define('Ext.chart.series.Gauge', {

   
/* Begin Definitions */

    extend
: 'Ext.chart.series.Series',

   
/* End Definitions */

    type
: "gauge",
   
alias: 'series.gauge',

    rad
: Math.PI / 180,

    /**
     * @cfg {Number} highlightDuration
     * The duration for the pie slice highlight effect.
     */

    highlightDuration
: 150,

    /**
     * @cfg {String} angleField
     * The store record field name to be used for the pie angles.
     * The values bound to this field name must be positive real numbers.
     * This parameter is required.
     */

    angleField
: false,

    /**
     * @cfg {Boolean} needle
     * Use the Gauge Series as an area series or add a needle to it. Default's false.
     */

    needle
: false,
   
    /**
     * @cfg {Boolean|Number} donut
     * Use the entire disk or just a fraction of it for the gauge. Default's false.
     */

    donut
: false,

    /**
     * @cfg {Boolean} showInLegend
     * Whether to add the pie chart elements as legend items. Default's false.
     */

    showInLegend
: false,

    /**
     * @cfg {Object} style
     * An object containing styles for overriding series styles from Theming.
     */

    style
: {},
   
    constructor
: function(config) {
       
this.callParent(arguments);
       
var me = this,
            chart
= me.chart,
            surface
= chart.surface,
            store
= chart.store,
            shadow
= chart.shadow, i, l, cfg;
       
Ext.apply(me, config, {
            shadowAttributes
: [{
               
"stroke-width": 6,
               
"stroke-opacity": 1,
                stroke
: 'rgb(200, 200, 200)',
                translate
: {
                    x
: 1.2,
                    y
: 2
               
}
           
},
           
{
               
"stroke-width": 4,
               
"stroke-opacity": 1,
                stroke
: 'rgb(150, 150, 150)',
                translate
: {
                    x
: 0.9,
                    y
: 1.5
               
}
           
},
           
{
               
"stroke-width": 2,
               
"stroke-opacity": 1,
                stroke
: 'rgb(100, 100, 100)',
                translate
: {
                    x
: 0.6,
                    y
: 1
               
}
           
}]
       
});
        me
.group = surface.getGroup(me.seriesId);
       
if (shadow) {
           
for (i = 0, l = me.shadowAttributes.length; i < l; i++) {
                me
.shadowGroups.push(surface.getGroup(me.seriesId + '-shadows' + i));
           
}
       
}
        surface
.customAttributes.segment = function(opt) {
           
return me.getSegment(opt);
       
};
   
},
   
   
//@private updates some onbefore render parameters.
    initialize
: function() {
       
var me = this,
            store
= me.chart.substore || me.chart.store;
       
//Add yFields to be used in Legend.js
        me
.yField = [];
       
if (me.label.field) {
            store
.each(function(rec) {
                me
.yField.push(rec.get(me.label.field));
           
});
       
}
   
},

   
// @private returns an object with properties for a Slice
    getSegment
: function(opt) {
       
var me = this,
            rad
= me.rad,
            cos
= Math.cos,
            sin
= Math.sin,
            abs
= Math.abs,
            x
= me.centerX,
            y
= me.centerY,
            x1
= 0, x2 = 0, x3 = 0, x4 = 0,
            y1
= 0, y2 = 0, y3 = 0, y4 = 0,
            delta
= 1e-2,
            r
= opt.endRho - opt.startRho,
            startAngle
= opt.startAngle,
            endAngle
= opt.endAngle,
            midAngle
= (startAngle + endAngle) / 2 * rad,
            margin
= opt.margin || 0,
            flag
= abs(endAngle - startAngle) > 180,
            a1
= Math.min(startAngle, endAngle) * rad,
            a2
= Math.max(startAngle, endAngle) * rad,
            singleSlice
= false;

        x
+= margin * cos(midAngle);
        y
+= margin * sin(midAngle);

        x1
= x + opt.startRho * cos(a1);
        y1
= y + opt.startRho * sin(a1);

        x2
= x + opt.endRho * cos(a1);
        y2
= y + opt.endRho * sin(a1);

        x3
= x + opt.startRho * cos(a2);
        y3
= y + opt.startRho * sin(a2);

        x4
= x + opt.endRho * cos(a2);
        y4
= y + opt.endRho * sin(a2);

       
if (abs(x1 - x3) <= delta && abs(y1 - y3) <= delta) {
            singleSlice
= true;
       
}
       
//Solves mysterious clipping bug with IE
       
if (singleSlice) {
           
return {
                path
: [
               
["M", x1, y1],
               
["L", x2, y2],
               
["A", opt.endRho, opt.endRho, 0, +flag, 1, x4, y4],
               
["Z"]]
           
};
       
} else {
           
return {
                path
: [
               
["M", x1, y1],
               
["L", x2, y2],
               
["A", opt.endRho, opt.endRho, 0, +flag, 1, x4, y4],
               
["L", x3, y3],
               
["A", opt.startRho, opt.startRho, 0, +flag, 0, x1, y1],
               
["Z"]]
           
};
       
}
   
},

   
// @private utility function to calculate the middle point of a pie slice.
    calcMiddle
: function(item) {
       
var me = this,
            rad
= me.rad,
            slice
= item.slice,
            x
= me.centerX,
            y
= me.centerY,
            startAngle
= slice.startAngle,
            endAngle
= slice.endAngle,
            radius
= Math.max(('rho' in slice) ? slice.rho: me.radius, me.label.minMargin),
            donut
= +me.donut,
            a1
= Math.min(startAngle, endAngle) * rad,
            a2
= Math.max(startAngle, endAngle) * rad,
            midAngle
= -(a1 + (a2 - a1) / 2),
            xm
= x + (item.endRho + item.startRho) / 2 * Math.cos(midAngle),
            ym
= y - (item.endRho + item.startRho) / 2 * Math.sin(midAngle);

        item
.middle = {
            x
: xm,
            y
: ym
       
};
   
},

    /**
     * Draws the series for the current chart.
     */

    drawSeries
: function() {
       
var me = this,
            chart
= me.chart,
            store
= chart.substore || chart.store,
           
group = me.group,
            animate
= me.chart.animate,
            axis
= me.chart.axes.get(0),
            minimum
= axis && axis.minimum || me.minimum || 0,
            maximum
= axis && axis.maximum || me.maximum || 0,
            field
= me.angleField || me.field || me.xField,
            surface
= chart.surface,
            chartBBox
= chart.chartBBox,
            rad
= me.rad,
            donut
= +me.donut,
            values
= {},
            items
= [],
            seriesStyle
= me.seriesStyle,
            seriesLabelStyle
= me.seriesLabelStyle,
            colorArrayStyle
= me.colorArrayStyle,
            colorArrayLength
= colorArrayStyle && colorArrayStyle.length || 0,
            gutterX
= chart.maxGutter[0],
            gutterY
= chart.maxGutter[1],
            cos
= Math.cos,
            sin
= Math.sin,
            rendererAttributes
, centerX, centerY, slice, slices, sprite, value,
            item
, ln, record, i, j, startAngle, endAngle, middleAngle, sliceLength, path,
            p
, spriteOptions, bbox, splitAngle, sliceA, sliceB;
       
       
Ext.apply(seriesStyle, me.style || {});

        me
.setBBox();
        bbox
= me.bbox;

       
//override theme colors
       
if (me.colorSet) {
            colorArrayStyle
= me.colorSet;
            colorArrayLength
= colorArrayStyle.length;
       
}
       
       
//if not store or store is empty then there's nothing to draw
       
if (!store || !store.getCount()) {
           
return;
       
}
       
        centerX
= me.centerX = chartBBox.x + (chartBBox.width / 2);
        centerY
= me.centerY = chartBBox.y + chartBBox.height;
        me
.radius = Math.min(centerX - chartBBox.x, centerY - chartBBox.y);
        me
.slices = slices = [];
        me
.items = items = [];
       
       
if (!me.value) {
            record
= store.getAt(0);
            me
.value = record.get(field);
       
}
       
        value
= me.value;
       
if (me.needle) {
            sliceA
= {
                series
: me,
                value
: value,
                startAngle
: -180,
                endAngle
: 0,
                rho
: me.radius
           
};
            splitAngle
= -180 * (1 - (value - minimum) / (maximum - minimum));
            slices
.push(sliceA);
       
} else {
            splitAngle
= -180 * (1 - (value - minimum) / (maximum - minimum));
            sliceA
= {
                series
: me,
                value
: value,
                startAngle
: -180,
                endAngle
: splitAngle,
                rho
: me.radius
           
};
            sliceB
= {
                series
: me,
                value
: me.maximum - value,
                startAngle
: splitAngle,
                endAngle
: 0,
                rho
: me.radius
           
};
            slices
.push(sliceA, sliceB);
       
}
       
       
//do pie slices after.
       
for (i = 0, ln = slices.length; i < ln; i++) {
            slice
= slices[i];
            sprite
= group.getAt(i);
           
//set pie slice properties
            rendererAttributes
= Ext.apply({
                segment
: {
                    startAngle
: slice.startAngle,
                    endAngle
: slice.endAngle,
                    margin
: 0,
                    rho
: slice.rho,
                    startRho
: slice.rho * +donut / 100,
                    endRho
: slice.rho
               
}
           
}, Ext.apply(seriesStyle, colorArrayStyle && { fill: colorArrayStyle[i % colorArrayLength] } || {}));

            item
= Ext.apply({},
            rendererAttributes
.segment, {
                slice
: slice,
                series
: me,
                storeItem
: record,
                index
: i
           
});
            items
[i] = item;
           
// Create a new sprite if needed (no height)
           
if (!sprite) {
                spriteOptions
= Ext.apply({
                    type
: "path",
                   
group: group
               
}, Ext.apply(seriesStyle, colorArrayStyle && { fill: colorArrayStyle[i % colorArrayLength] } || {}));
                sprite
= surface.add(Ext.apply(spriteOptions, rendererAttributes));
           
}
            slice
.sprite = slice.sprite || [];
            item
.sprite = sprite;
            slice
.sprite.push(sprite);
           
if (animate) {
                rendererAttributes
= me.renderer(sprite, record, rendererAttributes, i, store);
                sprite
._to = rendererAttributes;
                me
.onAnimate(sprite, {
                    to
: rendererAttributes
               
});
           
} else {
                rendererAttributes
= me.renderer(sprite, record, Ext.apply(rendererAttributes, {
                    hidden
: false
               
}), i, store);
                sprite
.setAttributes(rendererAttributes, true);
           
}
       
}
       
       
if (me.needle) {
            splitAngle
= splitAngle * Math.PI / 180;
           
           
if (!me.needleSprite) {
                me
.needleSprite = me.chart.surface.add({
                    type
: 'path',
                    path
: ['M', centerX + (me.radius * +donut / 100) * cos(splitAngle),
                                centerY
+ -Math.abs((me.radius * +donut / 100) * sin(splitAngle)),
                           
'L', centerX + me.radius * cos(splitAngle),
                                centerY
+ -Math.abs(me.radius * sin(splitAngle))],
                   
'stroke-width': 4,
                   
'stroke': '#222'
               
});
           
} else {
               
if (animate) {
                    me
.onAnimate(me.needleSprite, {
                        to
: {
                        path
: ['M', centerX + (me.radius * +donut / 100) * cos(splitAngle),
                                    centerY
+ -Math.abs((me.radius * +donut / 100) * sin(splitAngle)),
                               
'L', centerX + me.radius * cos(splitAngle),
                                    centerY
+ -Math.abs(me.radius * sin(splitAngle))]
                       
}
                   
});
               
} else {
                    me
.needleSprite.setAttributes({
                        type
: 'path',
                        path
: ['M', centerX + (me.radius * +donut / 100) * cos(splitAngle),
                                    centerY
+ -Math.abs((me.radius * +donut / 100) * sin(splitAngle)),
                               
'L', centerX + me.radius * cos(splitAngle),
                                    centerY
+ -Math.abs(me.radius * sin(splitAngle))]
                   
});
               
}
           
}
            me
.needleSprite.setAttributes({
                hidden
: false    
           
}, true);
       
}
       
       
delete me.value;
   
},
   
    /**
     * Sets the Gauge chart to the current specified value.
    */

    setValue
: function (value) {
       
this.value = value;
       
this.drawSeries();
   
},

   
// @private callback for when creating a label sprite.
    onCreateLabel
: function(storeItem, item, i, display) {},

   
// @private callback for when placing a label sprite.
    onPlaceLabel
: function(label, storeItem, item, i, display, animate, index) {},

   
// @private callback for when placing a callout.
    onPlaceCallout
: function() {},

   
// @private handles sprite animation for the series.
    onAnimate
: function(sprite, attr) {
        sprite
.show();
       
return this.callParent(arguments);
   
},

    isItemInPoint
: function(x, y, item, i) {
       
return false;
   
},
   
   
// @private shows all elements in the series.
    showAll
: function() {
       
if (!isNaN(this._index)) {
           
this.__excludes[this._index] = false;
           
this.drawSeries();
       
}
   
},
   
    /**
     * Returns the color of the series (to be displayed as color for the series legend item).
     * @param item {Object} Info about the item; same format as returned by #getItemForPoint
     */

    getLegendColor
: function(index) {
       
var me = this;
       
return me.colorArrayStyle[index % me.colorArrayStyle.length];
   
}
});