/**
 * @private
 */
Ext.define('Ext.chart.legend.sprite.Item', {
    extend: 'Ext.draw.sprite.Composite',
    alias: 'sprite.legenditem',
    type: 'legenditem',
    isLegendItem: true,
 
    requires: [
        'Ext.draw.sprite.Text',
        'Ext.draw.sprite.Circle'
    ],
 
    inheritableStatics: {
        def: {
            processors: {
                enabled: 'limited01',
                markerLabelGap: 'number'
            },
            animationProcessors: {
                enabled: null,
                markerLabelGap: null
            },
            defaults: {
                enabled: true,
                markerLabelGap: 5
            },
            triggers: {
                enabled: 'enabled',
                markerLabelGap: 'layout'
            },
            updaters: {
                layout: 'layoutUpdater',
                enabled: 'enabledUpdater'
            }
        }
    },
 
    config: {
        // Sprite's attributes are processed after initConfig.
        // So we need to init below configs lazily, as otherwise
        // adding sprites (created from those configs) to composite
        // will result in an attempt to access attributes that
        // composite doesn't have yet.
        label: {
            $value: {
                type: 'text'
            },
            lazy: true
        },
        marker: {
            $value: {
                type: 'circle'
            },
            lazy: true
        },
 
        legend: null,
        store: null,
        record: null,
        series: null
    },
 
    applyLabel: function(label, oldLabel) {
        var sprite;
 
        if (label) {
            if (label.isSprite && label.type === 'text') {
                sprite = label;
            }
            else {
                if (oldLabel && label.type === oldLabel.type) {
                    oldLabel.setConfig(label);
                    sprite = oldLabel;
                    this.scheduleUpdater(this.attr, 'layout');
                }
                else {
                    sprite = new Ext.draw.sprite.Text(label);
                }
            }
        }
 
        return sprite;
    },
 
    defaultMarkerSize: 10,
 
    updateLabel: function(label, oldLabel) {
        var me = this;
 
        me.removeSprite(oldLabel);
        label.setAttributes({
            textBaseline: 'middle'
        });
        me.addSprite(label);
        me.scheduleUpdater(me.attr, 'layout');
    },
 
    applyMarker: function(config) {
        var marker;
 
        if (config) {
            if (config.isSprite) {
                marker = config;
            }
            else {
                marker = this.createMarker(config);
            }
        }
 
        marker = this.resetMarker(marker, config);
 
        return marker;
    },
 
    createMarker: function(config) {
        var marker;
 
        // If marker attributes are animated, the attributes change over
        // time from default values to the values specified in the marker
        // config. But the 'legenditem' sprite needs final values
        // to properly layout its children.
        delete config.animation;
 
        if (config.type === 'image') {
            delete config.width;
            delete config.height;
        }
 
        marker = Ext.create('sprite.' + config.type, config);
 
        return marker;
    },
 
    resetMarker: function(sprite, config) {
        var size = config.size || this.defaultMarkerSize,
            bbox, max, scale;
 
        // Layout may not work properly,
        // if the marker sprite is transformed to begin with.
        sprite.setTransform([1, 0, 0, 1, 0, 0], true);
 
        if (config.type === 'image') {
            sprite.setAttributes({
                width: size,
                height: size
            });
        }
        else {
            // This should work with any sprite, irrespective of what attribute
            // is used to control sprite's size ('size', 'r', or something else).
            // However, the 'image' sprite above is a special case.
            bbox = sprite.getBBox();
            max = Math.max(bbox.width, bbox.height);
            scale = size / max;
            sprite.setAttributes({
                scalingX: scale,
                scalingY: scale
            });
        }
 
        return sprite;
    },
 
    updateMarker: function(marker, oldMarker) {
        var me = this;
 
        me.removeSprite(oldMarker);
        me.addSprite(marker);
        me.scheduleUpdater(me.attr, 'layout');
    },
 
    updateSurface: function(surface, oldSurface) {
        var me = this;
 
        me.callParent([surface, oldSurface]);
 
        if (surface) {
            me.scheduleUpdater(me.attr, 'layout');
        }
    },
 
    enabledUpdater: function(attr) {
        var marker = this.getMarker();
 
        if (marker) {
            marker.setAttributes({
                globalAlpha: attr.enabled ? 1 : 0.3
            });
        }
    },
 
    layoutUpdater: function() {
        var me = this,
            attr = me.attr,
            label = me.getLabel(),
            marker = me.getMarker(),
            labelBBox, markerBBox,
            totalHeight;
 
        // Measuring bounding boxes of transformed marker and label
        // sprites and translating the sprites by required amount,
        // makes layout virtually bullet-proof to unaccounted for
        // changes in sprite attributes, whatever the sprite type may be.
 
        markerBBox = marker.getBBox();
        labelBBox = label.getBBox();
 
        totalHeight = Math.max(markerBBox.height, labelBBox.height);
 
        // Because we are getting an already transformed bounding box,
        // we want to add to that transformation, not replace it,
        // so setting translationX/Y attributes here would be inappropriate.
        marker.transform([1, 0, 0, 1,
                          -markerBBox.x,
                          -markerBBox.y + (totalHeight - markerBBox.height) / 2
        ], true);
        label.transform([1, 0, 0, 1,
                         -labelBBox.x + markerBBox.width + attr.markerLabelGap,
                         -labelBBox.y + (totalHeight - labelBBox.height) / 2
        ], true);
 
        me.bboxUpdater(attr);
    }
 
});