    /**
     * @class Ext.chart.Legend
     *
     * Defines a legend for a chart's series.
     * The 'chart' member must be set prior to rendering.
     * The legend class displays a list of legend items each of them related with a
     * series being rendered. In order to render the legend item of the proper series
     * the series configuration object must have `showInSeries` set to true.
     *
     * The legend configuration object accepts a `position` as parameter.
     * The `position` parameter can be `left`, `right`
     * `top` or `bottom`. For example:
     *
     *     legend: {
     *         position: 'right'
     *     },
     *
     * Full example:
        <pre><code>
        var store = Ext.create('Ext.data.JsonStore', {
            fields: ['name', 'data1', 'data2', 'data3', 'data4', 'data5'],
            data: [
                {'name':'metric one', 'data1':10, 'data2':12, 'data3':14, 'data4':8, 'data5':13},
                {'name':'metric two', 'data1':7, 'data2':8, 'data3':16, 'data4':10, 'data5':3},
                {'name':'metric three', 'data1':5, 'data2':2, 'data3':14, 'data4':12, 'data5':7},
                {'name':'metric four', 'data1':2, 'data2':14, 'data3':6, 'data4':1, 'data5':23},
                {'name':'metric five', 'data1':27, 'data2':38, 'data3':36, 'data4':13, 'data5':33}
            ]
        });

        Ext.create('Ext.chart.Chart', {
            renderTo: Ext.getBody(),
            width: 500,
            height: 300,
            animate: true,
            store: store,
            shadow: true,
            theme: 'Category1',
            legend: {
                position: 'top'
            },
             axes: [{
                    type: 'Numeric',
                    grid: true,
                    position: 'left',
                    fields: ['data1', 'data2', 'data3', 'data4', 'data5'],
                    title: 'Sample Values',
                    grid: {
                        odd: {
                            opacity: 1,
                            fill: '#ddd',
                            stroke: '#bbb',
                            'stroke-width': 1
                        }
                    },
                    minimum: 0,
                    adjustMinimumByMajorUnit: 0
                }, {
                    type: 'Category',
                    position: 'bottom',
                    fields: ['name'],
                    title: 'Sample Metrics',
                    grid: true,
                    label: {
                        rotate: {
                            degrees: 315
                        }
                    }
            }],
            series: [{
                type: 'area',
                highlight: false,
                axis: 'left',
                xField: 'name',
                yField: ['data1', 'data2', 'data3', 'data4', 'data5'],
                style: {
                    opacity: 0.93
                }
            }]
        });
        </code></pre>
     *
     * @constructor
     */
    Ext.define('Ext.chart.Legend', {

        /* Begin Definitions */

        requires: ['Ext.chart.LegendItem'],

        /* End Definitions */

        /**
         * @cfg {Boolean} visible
         * Whether or not the legend should be displayed.
         */
        visible: true,

        /**
         * @cfg {String} position
         * The position of the legend in relation to the chart. One of: "top",
         * "bottom", "left", "right", or "float". If set to "float", then the legend
         * box will be positioned at the point denoted by the x and y parameters.
         */
        position: 'bottom',

        /**
         * @cfg {Number} x
         * X-position of the legend box. Used directly if position is set to "float", otherwise
         * it will be calculated dynamically.
         */
        x: 0,

        /**
         * @cfg {Number} y
         * Y-position of the legend box. Used directly if position is set to "float", otherwise
         * it will be calculated dynamically.
         */
        y: 0,

        /**
         * @cfg {String} labelFont
         * Font to be used for the legend labels, eg '12px Helvetica'
         */
        labelFont: '12px Helvetica, sans-serif',

        /**
         * @cfg {String} boxStroke
         * Style of the stroke for the legend box
         */
        boxStroke: '#000',

        /**
         * @cfg {String} boxStrokeWidth
         * Width of the stroke for the legend box
         */
        boxStrokeWidth: 1,

        /**
         * @cfg {String} boxFill
         * Fill style for the legend box
         */
        boxFill: '#FFF',

        /**
         * @cfg {Number} itemSpacing
         * Amount of space between legend items
         */
        itemSpacing: 10,

        /**
         * @cfg {Number} padding
         * Amount of padding between the legend box's border and its items
         */
        padding: 5,

        // @private
        width: 0,
        // @private
        height: 0,

        /**
         * @cfg {Number} boxZIndex
         * Sets the z-index for the legend. Defaults to 100.
         */
        boxZIndex: 100,

        constructor: function(config) {
            var me = this;
            if (config) {
                Ext.apply(me, config);
            }
            me.items = [];
            /**
             * Whether the legend box is oriented vertically, i.e. if it is on the left or right side or floating.
             * @type {Boolean}
             */
            me.isVertical = ("left|right|float".indexOf(me.position) !== -1);

            // cache these here since they may get modified later on
            me.origX = me.x;
            me.origY = me.y;
        },

        /**
         * @private Create all the sprites for the legend
         */
        create: function() {
            var me = this;
            me.createItems();
            if (!me.created && me.isDisplayed()) {
                me.createBox();
                me.created = true;

                // Listen for changes to series titles to trigger regeneration of the legend
                me.chart.series.each(function(series) {
                    series.on('titlechange', function() {
                        me.create();
                        me.updatePosition();
                    });
                });
            }
        },

        /**
         * @private Determine whether the legend should be displayed. Looks at the legend's 'visible' config,
         * and also the 'showInLegend' config for each of the series.
         */
        isDisplayed: function() {
            return this.visible && this.chart.series.findIndex('showInLegend', true) !== -1;
        },

        /**
         * @private Create the series markers and labels
         */
        createItems: function() {
            var me = this,
                chart = me.chart,
                surface = chart.surface,
                items = me.items,
                padding = me.padding,
                itemSpacing = me.itemSpacing,
                spacingOffset = 2,
                maxWidth = 0,
                maxHeight = 0,
                totalWidth = 0,
                totalHeight = 0,
                vertical = me.isVertical,
                math = Math,
                mfloor = math.floor,
                mmax = math.max,
                index = 0,
                i = 0,
                len = items ? items.length : 0,
                x, y, spacing, item, bbox, height, width;

            //remove all legend items
            if (len) {
                for (; i < len; i++) {
                    items[i].destroy();
                }
            }
            //empty array
            items.length = [];
            // Create all the item labels, collecting their dimensions and positioning each one
            // properly in relation to the previous item
            chart.series.each(function(series, i) {
                if (series.showInLegend) {
                    Ext.each([].concat(series.yField), function(field, j) {
                        item = Ext.create('Ext.chart.LegendItem', {
                            legend: this,
                            series: series,
                            surface: chart.surface,
                            yFieldIndex: j
                        });
                        bbox = item.getBBox();

                        //always measure from x=0, since not all markers go all the way to the left
                        width = bbox.width;
                        height = bbox.height;

                        if (i + j === 0) {
                            spacing = vertical ? padding + height / 2 : padding;
                        }
                        else {
                            spacing = itemSpacing / (vertical ? 2 : 1);
                        }
                        // Set the item's position relative to the legend box
                        item.x = mfloor(vertical ? padding : totalWidth + spacing);
                        item.y = mfloor(vertical ? totalHeight + spacing : padding + height / 2);

                        // Collect cumulative dimensions
                        totalWidth += width + spacing;
                        totalHeight += height + spacing;
                        maxWidth = mmax(maxWidth, width);
                        maxHeight = mmax(maxHeight, height);

                        items.push(item);
                    }, this);
                }
            }, me);

            // Store the collected dimensions for later
            me.width = mfloor((vertical ? maxWidth : totalWidth) + padding * 2);
            if (vertical && items.length === 1) {
                spacingOffset = 1;
            }
            me.height = mfloor((vertical ? totalHeight - spacingOffset * spacing : maxHeight) + (padding * 2));
            me.itemHeight = maxHeight;
        },

        /**
         * @private Get the bounds for the legend's outer box
         */
        getBBox: function() {
            var me = this;
            return {
                x: Math.round(me.x) - me.boxStrokeWidth / 2,
                y: Math.round(me.y) - me.boxStrokeWidth / 2,
                width: me.width,
                height: me.height
            };
        },

        /**
         * @private Create the box around the legend items
         */
        createBox: function() {
            var me = this,
                box = me.boxSprite = me.chart.surface.add(Ext.apply({
                    type: 'rect',
                    stroke: me.boxStroke,
                    "stroke-width": me.boxStrokeWidth,
                    fill: me.boxFill,
                    zIndex: me.boxZIndex
                }, me.getBBox()));
            box.redraw();
        },

        /**
         * @private Update the position of all the legend's sprites to match its current x/y values
         */
        updatePosition: function() {
            var me = this,
                x, y,
                legendWidth = me.width,
                legendHeight = me.height,
                padding = me.padding,
                chart = me.chart,
                chartBBox = chart.chartBBox,
                insets = chart.insetPadding,
                chartWidth = chartBBox.width - (insets * 2),
                chartHeight = chartBBox.height - (insets * 2),
                chartX = chartBBox.x + insets,
                chartY = chartBBox.y + insets,
                surface = chart.surface,
                mfloor = Math.floor;

            if (me.isDisplayed()) {
                // Find the position based on the dimensions
                switch(me.position) {
                    case "left":
                        x = insets;
                        y = mfloor(chartY + chartHeight / 2 - legendHeight / 2);
                        break;
                    case "right":
                        x = mfloor(surface.width - legendWidth) - insets;
                        y = mfloor(chartY + chartHeight / 2 - legendHeight / 2);
                        break;
                    case "top":
                        x = mfloor(chartX + chartWidth / 2 - legendWidth / 2);
                        y = insets;
                        break;
                    case "bottom":
                        x = mfloor(chartX + chartWidth / 2 - legendWidth / 2);
                        y = mfloor(surface.height - legendHeight) - insets;
                        break;
                    default:
                        x = mfloor(me.origX) + insets;
                        y = mfloor(me.origY) + insets;
                }
                me.x = x;
                me.y = y;

                // Update the position of each item
                Ext.each(me.items, function(item) {
                    item.updatePosition();
                });
                // Update the position of the outer box
                me.boxSprite.setAttributes(me.getBBox(), true);
            }
        }
    });