/**
 * @class Ext.Panel
 */
Ext.define('Ext.panel.Collapsible', {
    override: 'Ext.Panel',
 
    config: {
        /**
         * @cfg {Boolean} collapsed
         * `true` to start collapsed.
         */
        collapsed: null,
 
        /**
         * @cfg {'top'/'right'/'bottom'/'left'/Boolean/Object} collapsible
         * A configuration for a {@link Ext.panel.Collapser Collapser}.
         *
         * True to make the panel collapsible and have an expand/collapse toggle Tool added
         * into the header tool button area.
         *
         * You can also set `top`/`right`/`bottom`/`left` to directly specify the collapse
         * direction.
         *
         * **IMPORTANT**: To use this config, the application must require the following
         * class:
         *
         *      requires: [
         *          'Ext.panel.Collapser'
         *      ]
         *
         * @since 6.5.0
         */
        collapsible: null,
 
        /**
         * @cfg {Boolean} titleCollapse
         * `true` to allow expanding and collapsing the panel (when `{@link #collapsible} = true`)
         * by clicking anywhere in the header bar, `false` to allow it only by clicking to tool
         * button.
         * @since 7.0
         */
        titleCollapse: null
    },
 
    /**
     * @event beforecollapse
     * Fires before collapse starts. Return `false` to cancel collapse.
     * @param {Ext.panel.Panel} this 
     *
     * @since 6.5.0
     */
 
    /**
     * @event beforeexpand
     * Fires before expand starts. Return `false` to cancel expand.
     * @param {Ext.panel.Panel} this 
     *
     * @since 6.5.0
     */
 
    /**
     * @event collapse
     * Fires when the collapse starts.
     * @param {Ext.panel.Panel} this 
     *
     * @since 6.5.0
     */
 
    /**
     * @event drawerhide
     * Fires then the {@link Ext.panel.Collapser#cfg-drawer drawer} hides.
     *
     * @param {Ext.panel.Panel} this 
     * @since 6.5.0
     */
 
    /**
     * @event drawershow
     * Fires then the {@link Ext.panel.Collapser#cfg-drawer drawer} shows.
     *
     * @param {Ext.panel.Panel} this 
     * @since 6.5.0
     */
 
    /**
     * @event expand
     * Fires when the expand starts.
     * @param {Ext.panel.Panel} this 
     *
     * @since 6.5.0
     */
 
    /**
     * @property {Boolean} hasCollapsible
     * `true` if this panel has the collapsible override added.
     *
     * @since 6.5.0
     */
    hasCollapsible: true,
 
    collapsed: false,
    collapsing: false,
    expanded: false,
    expanding: false,
 
    defaultCollapserCls: 'Ext.panel.Collapser',
 
    doDestroy: function() {
        this.setCollapsible(null);
        this.callParent();
    },
 
    /**
     * @method collapse
     * @inheritdoc Ext.panel.Collapser#method-collapse
     */
    collapse: function(animation) {
        return this.getCollapsible().collapse(animation);
    },
 
    /**
     * @method expand
     * @inheritdoc Ext.panel.Collapser#method-expand
     */
    expand: function(animation) {
        return this.getCollapsible().expand(animation);
    },
 
    saveState: function(state, stateful) {
        var me = this,
            collapsible = me.getCollapsible(),
            saved = collapsible && collapsible.getCollapsed() && collapsible.savedProps,
            direction, property, value;
 
        me.callParent([ state, stateful ]);
 
        if (saved) {
            direction = collapsible.getDirection();
            property = (direction === 'top' || direction === 'bottom') ? 'height' : 'width';
 
            // When we are collapsed, we save either width or height.
            if (stateful && stateful[property] && (value = saved[property]) !== null) {
                state.set(property, value);
            }
        }
    },
 
    /**
     * @method toggleCollapsed
     * @inheritdoc Ext.panel.Collapser#method-toggleCollapsed
     */
    toggleCollapsed: function(collapsed, animation) {
        return this.getCollapsible().toggleCollapsed(collapsed, animation);
    },
 
    getCollapsed: function() {
        // The collapsed state should always be governed by the collapsible
        var collapsible = this.getCollapsible();
 
        return collapsible ? collapsible.getCollapsed() : false;
    },
 
    updateCollapsed: function(collapsed) {
        var collapsible = this.getCollapsible();
 
        if (collapsible) {
            collapsible.setCollapsed(collapsed);
        }
    },
 
    applyCollapsible: function(collapsible, collapser) {
        if (collapsible === true) {
            collapsible = { direction: this.getHeaderPosition() };
        }
        else if (typeof collapsible === 'string') {
            collapsible = { direction: collapsible };
        }
        else if (!collapsible) {
            return null;
        }
 
        if (collapser) {
            collapser.setConfig(collapsible);
        }
        else {
            collapsible = Ext.apply({
                xclass: this.defaultCollapserCls,
                target: this
            }, collapsible);
            collapser = Ext.create(collapsible);
        }
 
        return collapser;
    },
 
    updateCollapsible: function(collapsible, oldCollapsible) {
        if (oldCollapsible) {
            // before making the panel uncollapsible let's expand it without animations
            if (!this.destroying) {
                oldCollapsible.doExpandCollapse(false);
            }
 
            oldCollapsible.destroy();
        }
 
        if (collapsible && this.rendered) {
            this.initCollapsible(collapsible);
        }
    },
 
    updateHeader: function(header, oldHeader) {
        var collapsible = this.getCollapsible();
 
        this.callParent([header, oldHeader]);
 
        if (this.isConfiguring && collapsible) {
            collapsible.toggleCollapsed(collapsible.getCollapsed(), false);
        }
    },
 
    updateHeaderPosition: function(headerPosition, oldHeaderPosition) {
        var collapsible = this.getCollapsible();
 
        if (collapsible && collapsible.getCollapsed()) {
            headerPosition = collapsible.getDirection();
        }
 
        this.moveHeaderPosition(headerPosition, oldHeaderPosition);
    },
 
    privates: {
        hasHiddenContent: function() {
            return this.getCollapsed() || this.callParent();
        },
 
        initCollapsible: function(collapsible) {
            this.ensureHeader();
            collapsible.initialize();
        },
 
        onCollapsibleRendered: function() {
            var collapsible = this.getCollapsible();
 
            if (collapsible) {
                this.initCollapsible(collapsible);
            }
        },
 
        reattachBodyWrap: function() {
            var me = this,
                header = me._header,
                el = me.maxHeightElement || me.element,
                bodyWrap = me.bodyWrapElement;
 
            if (bodyWrap.parent() !== el) {
                // We need to make sure the collapser node
                // is after the header in case the resizer
                // node is present. The resizer node needs
                // to be on top to function.
                if (header) {
                    bodyWrap.insertAfter(header.element);
                }
                else {
                    el.insertFirst(bodyWrap);
                }
            }
        }
    }
});