/**
 * @class Ext.draw.modifier.Highlight
 * @extends Ext.draw.modifier.Modifier
 *
 * Highlight is a modifier that will override the attributes
 * with its `highlightStyle` attributes when `highlighted` is true.
 */
Ext.define("Ext.draw.modifier.Highlight", {
    extend: 'Ext.draw.modifier.Modifier',
    alias: 'modifier.highlight',

    config: {

        /**
         * @cfg {Boolean} enabled 'true' if the highlight is applied.
         */
        enabled: false,

        /**
         * @cfg {Object} highlightStyle The style attributes of the highlight modifier.
         */
        highlightStyle: null
    },

    preFx: true,

    applyHighlightStyle: function (style, oldStyle) {
        oldStyle = oldStyle || {};
        if (this.getSprite()) {
            Ext.apply(oldStyle, this.getSprite().self.def.normalize(style));
        } else {
            Ext.apply(oldStyle, style);
        }
        return oldStyle;
    },

    /**
     * @inheritdoc
     */
    prepareAttributes: function (attr) {
        if (!attr.hasOwnProperty('highlightOriginal')) {
            attr.highlighted = false;
            attr.highlightOriginal = Ext.Object.chain(attr);
        }
        if (this._previous) {
            this._previous.prepareAttributes(attr.highlightOriginal);
        }
    },

    updateSprite: function (sprite, oldSprite) {
        if (sprite) {
            if (this.getHighlightStyle()) {
                this._highlightStyle = sprite.self.def.normalize(this.getHighlightStyle());
            }
            this.setHighlightStyle(sprite.config.highlightCfg);
        }

        // Before attaching to a sprite, register the highlight related
        // attributes to its definition.
        //
        // TODO(zhangbei): Unfortunately this will effect all the sprites of the same type.
        // As the redundant attributes would not effect performance, it is not yet a big problem.
        var def = sprite.self.def;
        this.setSprite(sprite);
        def.setConfig({
            defaults: {
                highlighted: false
            },

            processors: {
                highlighted: 'bool'
            },

            aliases: {
                "highlight": "highlighted",
                "highlighting": "highlighted"
            },

            dirtyFlags: {
            },

            updaters: {

            }
        });
    },

    /**
     * Filter modifier changes if overriding source attributes.
     * @param {Object} attr The source attributes.
     * @param {Object} changes The modifier changes.
     * @return {*} The filtered changes.
     */
    filterChanges: function (attr, changes) {
        var me = this,
            name,
            original = attr.highlightOriginal,
            style = me.getHighlightStyle();
        if (attr.highlighted) {
            for (name in changes) {
                if (style.hasOwnProperty(name)) {
                    // If it's highlighted, then save the changes to lower level
                    // on overridden attributes.
                    original[name] = changes[name];
                    delete changes[name];
                }
            }
        }

        for (name in changes) {
            if (name !== 'highlighted' && original[name] === changes[name]) {
                // If it's highlighted, then save the changes to lower level
                // on overridden attributes.
                delete changes[name];
            }
        }

        return changes;
    },

    /**
     * @inheritdoc
     */
    pushDown: function (attr, changes) {
        var style = this.getHighlightStyle(),
            original = attr.highlightOriginal,
            oldHighlighted, name;

        if (changes.hasOwnProperty('highlighted')) {
            oldHighlighted = changes.highlighted;
            // Hide `highlighted` and `highlightStyle` to underlying modifiers.
            delete changes.highlighted;

            if (this._previous) {
                changes = this._previous.pushDown(original, changes);
            }
            changes = this.filterChanges(attr, changes);

            if (oldHighlighted !== attr.highlighted) {
                if (oldHighlighted) {
                    // switching on
                    // At this time, original should be empty.
                    for (name in style) {
                        // If changes[name] just changed the value in lower levels,
                        if (name in changes) {
                            original[name] = changes[name];
                        } else {
                            original[name] = attr[name];
                        }
                        if (original[name] !== style[name]) {
                            changes[name] = style[name];
                        }
                    }
                } else {
                    // switching off
                    for (name in style) {
                        if (!(name in changes)) {
                            changes[name] = original[name];
                        }
                        delete original[name]; // TODO: Need deletion API?
                    }
                }
                changes.highlighted = oldHighlighted;
            }
        } else {
            if (this._previous) {
                changes = this._previous.pushDown(original, changes);
            }
            changes = this.filterChanges(attr, changes);
        }

        return changes;
    },

    /**
     * @inheritdoc
     */
    popUp: function (attr, changes) {
        changes = this.filterChanges(attr, changes);
        Ext.draw.modifier.Modifier.prototype.popUp.call(this, attr, changes);
    }
});
