/**
 * @class Ext.Container
 * @extends Ext.lib.Container
 * <p>Base class for any {@link Ext.BoxComponent} that may contain other Components. Containers handle the
 * basic behavior of containing items, namely adding, inserting and removing items.</p>
 *
 * <p><u><b>Layout</b></u></p>
 * <p>Container classes delegate the rendering of child Components to a layout
 * manager class which must be configured into the Container using the
 * <code><b>{@link #layout}</b></code> configuration property.</p>
 * <p>When either specifying child <code>{@link #items}</code> of a Container,
 * or dynamically {@link #add adding} Components to a Container, remember to
 * consider how you wish the Container to arrange those child elements, and
 * whether those child elements need to be sized using one of Ext's built-in
 * <b><code>{@link #layout}</code></b> schemes. By default, Containers use the
 * {@link Ext.layout.AutoContainerLayout AutoContainerLayout} scheme which only
 * renders child components, appending them one after the other inside the
 * Container, and <b>does not apply any sizing</b> at all.</p>
 * <p>A common mistake is when a developer neglects to specify a
 * <b><code>{@link #layout}</code></b>. If a Container is left to use the default
 * {@link Ext.layout.AutoContainerLayout AutoContainerLayout} scheme, none of its
 * child components will be resized, or changed in any way when the Container
 * is resized.</p>
 * @xtype container
 */
Ext.Container = Ext.extend(Ext.lib.Container, {
    /**
     * @cfg {String/Mixed} cardSwitchAnimation
     * Animation to be used during transitions of cards. Note this only works when this container has a CardLayout.
     * Any valid value from Ext.anims can be used ('fade', 'slide', 'flip', 'cube', 'pop', 'wipe').
     * Defaults to <tt>null</tt>.
     */
    cardSwitchAnimation: null,

    initComponent: function() {
        if (this.scroll) {
            this.fields = new Ext.util.MixedCollection();

            if (!Ext.is.Blackberry) {
                this.fields.on({
                    add: this.onFieldAdd,
                    remove: this.onFieldRemove,
                    scope: this
                });
            }

            this.on({
                add: this.onItemAdd,
                remove: this.onItemRemove,
                scope: this
            });
        }

        //<deprecated since="0.99">
        if (Ext.isDefined(this.animation)) {
            console.warn("Container: animation has been removed. Please use cardSwitchAnimation.");
            this.cardSwitchAnimation = this.animation;
        }
        //</deprecated>

        Ext.Container.superclass.initComponent.apply(this, arguments);
    },

    afterRender: function() {
        Ext.Container.superclass.afterRender.apply(this, arguments);

        if (this.scroller) {
            if ((Ext.is.Android) && this.containsFormFields) {
                this.scroller.setUseCssTransform(false);
            }

            this.scroller.on('scrollstart', this.onFieldScrollStart, this);
        }
    },

    onFieldScrollStart: function() {
        var focusedField = this.focusedField;

        if (focusedField && Ext.is.iOS) {
            focusedField.blur();
//            Ext.Viewport.scrollToTop();
        }
    },

    onItemAdd: function(me, item) {
        this.fields.addAll(Ext.ComponentQuery.query('[isField]', item));
    },

    onItemRemove: function(me, item) {
        this.fields.removeAll(Ext.ComponentQuery.query('[isField]', item));
    },

    onFieldAdd: function(key, field) {
        this.handleFieldEventListener(true, field);
    },

    onFieldRemove: function(key, field) {
        this.handleFieldEventListener(false, field);
    },

    handleFieldEventListener: function(isAdding, item) {
        if (!this.fieldEventWrap)
            this.fieldEventWrap = {};

        if (['textfield', 'passwordfield', 'emailfield',
             //<deprecated since=0.99>
             'textarea',
             //</deprecated>
             'textareafield', 'searchfield', 'urlfield',
             'numberfield', 'spinnerfield'].indexOf(item.xtype) !== -1) {
            if (isAdding) {
                this.fieldEventWrap[item.id] = {
                    beforefocus: function(e) {this.onFieldBeforeFocus(item, e);},
                    focus: function(e) {this.onFieldFocus(item, e);},
                    blur: function(e) {this.onFieldBlur(item, e);},
                    keyup: function(e) {this.onFieldKeyUp(item, e);},
                    scope: this
                };

                this.containsFormFields = true;
            }

            item[isAdding ? 'on' : 'un'](this.fieldEventWrap[item.id]);

            if (!isAdding) {
                delete this.fieldEventWrap[item.id];
            }
        }
    },

    onFieldKeyUp: function(field, e) {
        if (Ext.is.iOS || Ext.is.Desktop) {
            this.resetLastWindowScroll();
        }
    },

    onFieldBeforeFocus: function(field, e) {
        this.focusingField = field;
    },

    getLastWindowScroll: function() {
        if (!this.lastWindowScroll) {
            this.resetLastWindowScroll();
        }

        return {x: this.lastWindowScroll.x, y: this.lastWindowScroll.y};
    },

    resetLastWindowScroll: function() {
        this.lastWindowScroll = {
            x: window.pageXOffset,
            y: window.pageYOffset
        };
    },

    adjustScroller: function(offset) {
        var scroller = this.getClosestScroller(),
            windowScroll = this.getLastWindowScroll();

        scroller.setOffset(offset);

        // Keep the window in the previous scroll position
        if (Ext.is.iOS) {
            window.scrollTo(windowScroll.x, windowScroll.y);
        }

        this.resetLastWindowScroll();
    },

    onFieldFocus: function(field, e) {
        if (!Ext.is.iOS && !Ext.is.Desktop) {
            var dom = field.fieldEl.dom;

            if (dom.scrollIntoViewIfNeeded) {
                dom .scrollIntoViewIfNeeded(true);
            }
        }
        else {
             var scroller = this.getClosestScroller(),
                containerRegion = Ext.util.Region.from(scroller.containerBox),
                fieldRegion = field.fieldEl.getPageBox(true);

            // Focus by mouse click or finger tap, or not iOS
            if (this.focusingField == field || !Ext.is.iOS) {
                if (Ext.is.iOS && window.pageYOffset == 0) {
                    window.scrollTo(0, 0);
                }

                var adjustment = new Ext.util.Offset();

                if (fieldRegion.left < containerRegion.left) {
                    adjustment.x = containerRegion.left - fieldRegion.left;
                }

                if (fieldRegion.top < containerRegion.top) {
                    adjustment.y = containerRegion.top - fieldRegion.top;
                }

                if (!adjustment.isZero()) {
                    var windowScroll = this.getLastWindowScroll();

                    scroller.scrollBy(adjustment);

                    if (Ext.is.iOS) {
                        window.scrollTo(windowScroll.x, windowScroll.y);
                    }

                    this.resetLastWindowScroll();
                }
            }
            // Focus by next / previous / tab
            else {
                if (this.lastFocusedField) {
                    var deltaY = fieldRegion.top - this.lastFocusedField.fieldEl.getY(),
                        offsetY = scroller.offset.y - deltaY,
                        selfHandling = false;

                    if (!containerRegion.contains(fieldRegion) &&
                        (offsetY != 0 || (offsetY == 0 && scroller.offset.y != 0))) {
                        selfHandling = true;
                    }

                    if (offsetY > 0) {
                        offsetY = 0;
                    }

                    if (selfHandling) {
                        this.adjustScroller(new Ext.util.Offset(
                            scroller.offset.x, offsetY
                        ));
                    }
                }
            }

            this.resetLastWindowScroll();
        }

        this.lastFocusedField = field;
        this.focusedField = field;
        this.focusingField = null;
    },

    getClosestScroller: function() {
        if (!this.closestScroller) {
            this.closestScroller = this.scroller || this.el.getScrollParent();
        }

        return this.closestScroller;
    },

    onFieldBlur: function(field, e) {
        if (this.focusingField == field) {
            this.focusingField = null;
        }

        if (this.focusedField == field) {
            this.focusedField = null;
        }
    },

    // @private
    afterLayout : function(layout) {
        if (this.floating && this.centered) {
            this.setCentered(true, true);
        }

        if (this.scroller) {
            this.scroller.updateBoundary();
        }
        Ext.Container.superclass.afterLayout.call(this, layout);
    },

    /**
     * Returns the current activeItem for the layout (only for a card layout)
     * @return {activeItem} activeItem Current active component
     */
    getActiveItem : function() {
        if (this.layout && this.layout.type == 'card') {
            return this.layout.activeItem;
        }
        else {
            return null;
        }
    },

    /**
     * Allows you to set the active card in this container. This
     * method is only available if the container uses a CardLayout.
     * Note that a Carousel and TabPanel both get a CardLayout
     * automatically, so both of those components are able to use this method.
     * @param {Ext.Component/Number/Object} card The card you want to be made active. A number
     * is interpreted as a card index. An object will be converted to a Component using the
     * objects xtype property, then added to the container and made active. Passing a Component
     * will make sure the component is a child of this container, and then make it active.
     * @param {String/Object} cardSwitchAnimation (optional) The cardSwitchAnimation used to switch between the cards.
     * This can be an animation type string or an animation configuration object.
     * @return {Ext.Container} this
     */
    setActiveItem : function(card, animation) {
        this.layout.setActiveItem(card, animation);
        return this;
    },

    //<deprecated since=0.99>
    setCard: function() {
        console.warn("Container: setCard has been deprecated. Please use setActiveItem.");
        this.setActiveItem.apply(this, arguments);
    },
    //</deprecated>

    /**
     * A template method that can be implemented by subclasses of
     * Container. By returning false we can cancel the card switch.
     * @param {Ext.Component} newCard The card that will be switched to
     * @param {Ext.Component} oldCard The card that will be switched from
     * @param {Number} newIndex The Container index position of the selected card
     * @param {Boolean} animated True if this cardswitch will be animated
     * @private
     */
    onBeforeCardSwitch : function(newCard, oldCard, newIndex, animated) {
        return this.fireEvent('beforecardswitch', this, newCard, oldCard, newIndex, animated);
    },

    /**
     * A template method that can be implemented by subclasses of
     * Container. If the card is switched using an animation, this method
     * will be called after the animation has finished.
     * @param {Ext.Component} newCard The card that has been switched to
     * @param {Ext.Component} oldCard The card that has been switched from
     * @param {Number} newIndex The Container index position of the selected card
     * @param {Boolean} animated True if this cardswitch was animated
     * @private
     */
    onCardSwitch : function(newCard, oldCard, newIndex, animated) {
        return this.fireEvent('cardswitch', this, newCard, oldCard, newIndex, animated);
    },

    /**
     * Disable this container by masking out
     */
    disable: function() {
        Ext.Container.superclass.disable.call(this);
        this.el.mask(null, 'x-mask-gray');
    },

    /**
     * Enable this container by removing mask
     */
    enable: function() {
        Ext.Container.superclass.enable.call(this);
        this.el.unmask();
    }
});

Ext.reg('container', Ext.Container);