/**
 * @author Robert Dougan <rob@sencha.com>
 *
 * NavigationView is basically a {@link Ext.Container} with a {@link Ext.layout.Card card} layout, so only one view
 * can be visible at a time. However, NavigationView also adds extra functionality on top of this to allow
 * you to `push` and `pop` views at any time. When you do this, your NavigationView will automatically animate
 * between your current active view, and the new view you want to `push`, or the previous view you want to `pop`.
 *
 * Using the NavigationView is very simple. Here is a basic example of it in action:
 *
 *     var view = Ext.create('Ext.NavigationView', {
 *         fullscreen: true,
 *
 *         items: [{
 *             title: 'First',
 *             items: [{
 *                 xtype: 'button',
 *                 text: 'Push a new view!',
 *                 handler: function() {
 *                     //use the push() method to push another view. It works much like
 *                     //add() or setActiveItem(). it accepts a view instance, or you can give it
 *                     //a view config.
 *                     view.push({
 *                         title: 'Second',
 *                         html: 'Second view!'
 *                     });
 *                 }
 *             }]
 *         }]
 *     });
 *
 * Now, here comes the fun part: you can push any view/item into the NavigationView, at any time, and it will
 * automatically handle the animations between the two views, including adding a back button (if necessary)
 * and showing the new title.
 *
 *     view.push({
 *         title: 'A new view',
 *         html: 'Some new content'
 *     });
 *
 * As you can see, it is as simple as calling the {@link #method-push} method, with a new view (instance or object). Done.
 *
 * You can also `pop` a view at any time. This will remove the top-most view from the NavigationView, and amimate back
 * to the previous view. You can do this using the {@link #method-pop} method (which requires no arguments).
 *
 *     view.pop();
 *
 */
Ext.define('Ext.navigation.View', {
    extend: 'Ext.Container',
    alternateClassName: 'Ext.NavigationView',
    xtype: 'navigationview',
    requires: ['Ext.navigation.Bar'],

    config: {
        // @inherit
        baseCls: Ext.baseCSSPrefix + 'navigationview',

        /**
         * @cfg {Boolean/Object} navigationBar
         * The NavigationBar used in this navigation view. It defaults to be docked to the top.
         *
         * You can just pass in a normal object if you want to customize the NavigationBar. For example:
         *
         *     navigationBar: {
         *         ui: 'dark',
         *         docked: 'bottom'
         *     }
         *
         * You **cannot** specify a *title* property in this configuration. The title of the navigationBar is taken
         * from the configuration of this views children:
         *
         *     view.push({
         *         title: 'This views title which will be shown in the navigation bar',
         *         html: 'Some HTML'
         *     });
         *
         * @accessor
         */
        navigationBar: {
            docked: 'top'
        },

        /**
         * @cfg {String} defaultBackButtonText
         * The text to be displayed on the back button if:
         * a) The previous view does not have a title
         * b) The {@link #useTitleForBackButtonText} configuration is true.
         * @accessor
         */
        defaultBackButtonText: 'Back',

        /**
         * @cfg {Boolean} useTitleForBackButtonText
         * Set to false if you always want to display the {@link #defaultBackButtonText} as the text
         * on the back button. True if you want to use the previous views title.
         * @accessor
         */
        useTitleForBackButtonText: false,

        /**
         * @cfg {Array/Object} items The child items to add to this NavigationView. This is usually an array of Component
         * configurations or instances, for example:
         *
         *    Ext.create('Ext.Container', {
         *        items: [
         *            {
         *                xtype: 'panel',
         *                title: 'My title',
         *                html: 'This is an item'
         *            }
         *        ]
         *    });
         *
         * If you want a title to be displayed in the {@link #navigationBar}, you must specify a `title` configuration in your
         * view, like above.
         *
         * Note: only one view will be visible at a time. If you want to change to another view, use the {@link #method-push} or
         * {@link #setActiveItem} methods.
         * @accessor
         */

        /**
         * @hide
         */
        layout: {
            type: 'card',
            animation: {
                duration: 300,
                easing: 'ease-out',
                type: 'slide',
                direction: 'left'
            }
        }

        // See https://sencha.jira.com/browse/TOUCH-1568
        // If you do, add to #navigationBar config docs:
        //
        //     If you want to add a button on the right of the NavigationBar,
        //     use the {@link #rightButton} configuration.
    },

    /**
     * @event push
     * Fires when a view is pushed into this navigation view
     * @param {Ext.navigation.View} this The component instance
     * @param {Mixed} view The view that has been pushed
     */

    /**
     * @event pop
     * Fires when a view is popped from this navigation view
     * @param {Ext.navigation.View} this The component instance
     * @param {Mixed} view The view that has been popped
     */

    // @private
    initialize: function() {
        //add a listener onto the back button in the navigationbar
        this.getNavigationBar().on({
            scope: this,
            back: this.onBackButtonTap
        });

        this.relayEvents(this, {
            add: 'push',
            remove: 'pop'
        });
    },

    /**
     * @private
     * Disable all animations on Android
     */
    applyLayout: function(config) {
        config = config || {};

        if (Ext.os.is.Android) {
            config.animation = false;
        }

        return config;
    },

    /**
     * @private
     * Called when the user taps on the back button
     */
    onBackButtonTap: function() {
        this.pop();

        this.fireEvent('back', this);
    },

    /**
     * Pushes a new view into this navigation view using the default animation that this view has.
     * @param {Object} view The view to push
     * @return {Ext.Component} The new item you just pushed
     */
    push: function(view) {
        return this.add(view);
    },

    /**
     * Removes the current active view from the stack and sets the previous view using the default animation
     * of this view.
     * @param {Number} count The number of views you want to pop
     * @return {Ext.Component} The new active item
     */
    pop: function(count) {
        if (this.onBeforePop(count)) {
            return this.doPop();
        }
    },

    /**
     * @private
     * Calculates whether it needs to remove any items from the stack when you are popping more than 1
     * item. If it does, it removes those views from the stack and returns `true`.
     * @return {Boolean} True if it has removed views
     */
    onBeforePop: function(count) {
        var me = this,
            innerItems = this.getInnerItems(),
            ln = innerItems.length,
            toRemove, last, i;

        //default to 1 pop
        if (!Ext.isNumber(count) || count < 1) {
            count = 1;
        }

        //check if we are trying to remove more items than we have
        count = Math.min(count, ln - 1);

        if (count) {
            //we need to reset the backButtonStack in the navigation bar
            me.getNavigationBar().onBeforePop(count);

            //get the items we need to remove from the view and remove theme
            toRemove = innerItems.splice(-count, count - 1);
            for (i = 0; i < toRemove.length; i++) {
                this.remove(toRemove[i]);
            }

            return true;
        }

        return false;
    },

    /**
     * @private
     */
    doPop: function(config) {
        var me = this,
            innerItems = this.getInnerItems(),
            layout = me.getLayout(),
            animation = layout.getAnimation();

        //set the new active item to be the new last item of the stack
        me.remove(innerItems[innerItems.length - 1]);

        return this.getActiveItem();
    },

    /**
     * Returns the previous item, if one exists.
     * @return {Mixed} The previous view
     */
    getPreviousItem: function() {
        var innerItems = this.getInnerItems();
        return innerItems[innerItems.length - 1];
    },

    /**
     * Updates the backbutton text accordingly in the {@link #navigationBar}
     * @private
     */
    updateUseTitleForBackButtonText: function(useTitleForBackButtonText) {
        var navigationBar = this.getNavigationBar();
        if (navigationBar) {
            navigationBar.setUseTitleForBackButtonText(useTitleForBackButtonText);
        }
    },

    // @private
    applyNavigationBar: function(config) {
        if (!config) {
            config = {
                hidden: true,
                docked: 'top'
            };
        }

        if (config.title) {
            delete config.title;
            //<debug>
            Ext.Logger.warn("Ext.navigation.View: The 'navigationBar' configuration does not accept a 'title' property. You " +
                            "set the title of the navigationBar by giving this navigation view's children a 'title' property.");
            //</debug>
        }

        config.view = this;

        return Ext.factory(config, Ext.navigation.Bar, this.getNavigationBar());
    },

    // @private
    updateNavigationBar: function(newNavigationBar, oldNavigationBar) {
        if (oldNavigationBar) {
            this.remove(oldNavigationBar);
        }

        if (newNavigationBar) {
            var layout = this.getLayout(),
                animation = (layout && layout.isLayout) ? layout.getAnimation() : false;

            if (animation && animation.isAnimation) {
                newNavigationBar.setAnimation(animation.config);
            }
            this.add(newNavigationBar);
        }
    },

    /**
     * @private
     */
    applyActiveItem: function(activeItem, currentActiveItem) {
        var innerItems = this.getInnerItems();

        // Make sure the items are already initialized
        this.getItems();

        // If we are not initialzed yet, we should set the active item to the last item in the stack
        if (!this.initialized) {
            activeItem = innerItems.length - 1;
        }

        if (typeof activeItem == 'number') {
            activeItem = Math.max(0, Math.min(activeItem, innerItems.length - 1));
            activeItem = innerItems[activeItem];

            if (activeItem) {
                return activeItem;
            }
            else if (currentActiveItem) {
                return null;
            }
        } else if (activeItem) {
            if (!activeItem.isComponent) {
                activeItem = this.factoryItem(activeItem);
            }

            //<debug error>
            if (!activeItem.isInnerItem()) {
                Ext.Logger.error("Setting activeItem to be a non-inner item");
            }
            //</debug>

            if (!this.has(activeItem)) {
                this.add(activeItem);
            }

            return activeItem;
        }
    },

    remove: function(item, destroy) {
        var me = this,
            index = me.indexOf(item),
            innerItems = this.getInnerItems(),
            animation = this.getLayout().getAnimation(),
            innerIndex;

        if (destroy === undefined) {
            destroy = me.getAutoDestroy();
        }

        if (index !== -1) {
            if (!this.removingAll && innerItems.length > 1 && item === this.getActiveItem()) {
                this.on({
                    activeitemchange: 'doRemove',
                    scope: this,
                    single: true,
                    order: 'after',
                    args: [item, index, destroy]
                });

                innerIndex = innerItems.indexOf(item);

                if (innerIndex > 0) {
                    if (animation && animation.isAnimation) {
                        animation.setReverse(true);
                    }

                    this.setActiveItem(innerIndex - 1);

                    this.getNavigationBar().onViewRemove(this, innerItems[innerIndex], innerIndex);
                }
            }
            else {
                this.doRemove(item, index, destroy);
            }
        }

        return me;
    },

    /**
     * @private
     */
    doRemove: function() {
        var animation = this.getLayout().getAnimation();

        if (animation && animation.isAnimation) {
            animation.setReverse(false);
        }

        this.callParent(arguments);
    },

    /**
     * @private
     */
    onItemAdd: function(item, index) {
        this.doItemLayoutAdd(item, index);

        if (!this.isItemsInitializing && item.isInnerItem()) {
            this.setActiveItem(item);
            this.getNavigationBar().onViewAdd(this, item, index);
        }

        if (this.initialized) {
            this.fireEvent('add', this, item, index);
        }
    },

    /**
     * Resets the view by removing all items between the first and last item.
     * @return {Ext.Component} The view that is now active
     */
    reset: function() {
        this.pop(this.getInnerItems().length);
    }
});