/**
 * This class functions **between siblings of a {@link Ext.layout.container.VBox VBox} or
 * {@link Ext.layout.container.HBox HBox} layout** to resize both immediate siblings.
 *
 * A Splitter will preserve the flex ratio of any flexed siblings it is required to resize.
 * It does this by setting the `flex` property of *all* flexed siblings to equal their pixel size.
 * The actual numerical `flex` property in the Components will change, but the **ratio**
 * to the total flex value will be preserved.
 *
 * A Splitter may be configured to show a centered mini-collapse tool orientated to collapse
 * the {@link #collapseTarget}. The Splitter will then call that sibling Panel's
 * {@link Ext.panel.Panel#method-collapse collapse} or {@link Ext.panel.Panel#method-expand expand}
 * method to perform the appropriate operation (depending on the sibling collapse state).
 * To create the mini-collapse tool but take care of collapsing yourself, configure the splitter
 * with `{@link #performCollapse}: false`.
 */
Ext.define('Ext.resizer.Splitter', {
    extend: 'Ext.Component',
    xtype: 'splitter',
 
    requires: ['Ext.XTemplate'],
    uses: ['Ext.resizer.SplitterTracker'],
 
    childEls: [
        'collapseEl'
    ],
 
    /* eslint-disable indent, max-len */
    renderTpl: [
        '<tpl if="collapsible===true">',
            '<div id="{id}-collapseEl" data-ref="collapseEl" role="presentation" class="', Ext.baseCSSPrefix, 'collapse-el ',
                Ext.baseCSSPrefix, 'layout-split-{collapseDir}{childElCls}">',
            '</div>',
        '</tpl>'
    ],
    /* eslint-enable indent, max-len */
 
    isSplitter: true,
 
    baseCls: Ext.baseCSSPrefix + 'splitter',
    collapsedClsInternal: Ext.baseCSSPrefix + 'splitter-collapsed',
 
    // Default to tree, allow internal classes to disable resizing
    canResize: true,
 
    /**
     * @cfg {Boolean} [collapsible]
     * True to show a mini-collapse tool in the Splitter to toggle expand and collapse
     * on the {@link #collapseTarget} Panel.
     * Defaults to the {@link Ext.panel.Panel#collapsible collapsible} setting of the Panel.
     */
    collapsible: null,
 
    /**
     * @cfg {Boolean} performCollapse
     * Set to false to prevent this Splitter's mini-collapse tool from managing the collapse
     * state of the {@link #collapseTarget}.
     */
 
    /**
     * @cfg {Boolean} collapseOnDblClick
     * True to enable dblclick to toggle expand and collapse on the {@link #collapseTarget} Panel.
     */
    collapseOnDblClick: true,
 
    /**
     * @cfg {Number} defaultSplitMin
     * Provides a default minimum width or height for the two components
     * that the splitter is between.
     */
    defaultSplitMin: 40,
 
    /**
     * @cfg {Number} defaultSplitMax
     * Provides a default maximum width or height for the two components
     * that the splitter is between.
     */
    defaultSplitMax: 1000,
 
    /**
     * @cfg {String} collapsedCls
     * A class to add to the splitter when it is collapsed. See {@link #collapsible}.
     */
 
    /**
     * @cfg {String/Ext.panel.Panel} collapseTarget
     * A string describing the relative position of the immediate sibling Panel to collapse.
     * May be 'prev' or 'next'.
     *
     * Or the immediate sibling Panel to collapse.
     *
     * The orientation of the mini-collapse tool will be inferred from this setting.
     *
     * **Note that only Panels may be collapsed.**
     */
    collapseTarget: 'next',
 
    /**
     * @property {String} orientation
     * Orientation of this Splitter. `'vertical'` when used in an hbox layout, `'horizontal'`
     * when used in a vbox layout.
     */
 
    horizontal: false,
    vertical: false,
 
    touchAction: undefined, // so applier/updater always run
 
    /**
     * @cfg {Number} size
     * The size of the splitter. This becomes the height for vertical splitters and 
     * width for horizontal splitters.
     */
    size: 5,
 
    /**
     * @cfg {Object} [tracker]
     * Any configuration options to be passed to the underlying {@link Ext.resizer.SplitterTracker}.
     */
    tracker: null,
 
    ariaRole: 'separator',
 
    focusable: true,
 
    tabIndex: 0,
 
    applyTouchAction: function(touchAction, oldTouchAction) {
        if (touchAction === undefined) {
            touchAction = this.vertical ? { panX: false } : { panY: false };
        }
 
        return this.callParent([touchAction, oldTouchAction]);
    },
 
    /**
     * Returns the config object (with an `xclass` property) for the splitter tracker. This
     * is overridden by {@link Ext.resizer.BorderSplitter BorderSplitter} to create a
     * {@link Ext.resizer.BorderSplitterTracker BorderSplitterTracker}.
     * @protected
     */
    getTrackerConfig: function() {
        return Ext.apply({
            xclass: 'Ext.resizer.SplitterTracker',
            el: this.el,
            splitter: this
        }, this.tracker);
    },
 
    beforeRender: function() {
        var me = this,
            target = me.getCollapseTarget(),
            collapsible = me.collapsible;
 
        me.callParent();
 
        if (target.collapsed) {
            me.addCls(me.collapsedClsInternal);
        }
 
        if (!me.canResize) {
            me.addCls(me.baseCls + '-noresize');
        }
 
        Ext.applyIf(me.renderData, {
            collapseDir: me.getCollapseDirection(),
            collapsible: (collapsible !== null) ? collapsible : target.collapsible
        });
 
        me.ariaRenderAttributes = me.ariaRenderAttributes || {};
 
        // Calling getCollapseDirection() above will set the orientation property
        me.ariaRenderAttributes['aria-orientation'] = me.orientation;
 
        me.protoEl.unselectable();
    },
 
    onRender: function() {
        var me = this,
            target, collapseEl;
 
        me.callParent(arguments);
 
        // Add listeners on the mini-collapse tool unless performCollapse is set to false
        if (me.performCollapse !== false) {
            if (me.renderData.collapsible) {
                me.mon(me.collapseEl, 'click', me.toggleTargetCmp, me);
            }
 
            if (me.collapseOnDblClick) {
                me.mon(me.el, 'dblclick', me.toggleTargetCmp, me);
            }
        }
 
        // Ensure the mini collapse icon is set to the correct direction
        // when the target is collapsed/expanded by any means.
        // Make sure we're only listening to collapse/expand events on Panels!
        target = me.getCollapseTarget();
 
        if (target && target.isPanel) {
            target.on({
                collapse: me.onTargetCollapse,
                expand: me.onTargetExpand,
                beforeexpand: me.onBeforeTargetExpand,
                beforecollapse: me.onBeforeTargetCollapse,
                scope: me
            });
        }
 
        if (me.canResize) {
            me.tracker = Ext.create(me.getTrackerConfig());
            // Relay the most important events to our owner (could open wider later):
            me.relayEvents(me.tracker, [ 'beforedragstart', 'dragstart', 'dragend' ]);
        }
 
        collapseEl = me.collapseEl;
 
        if (collapseEl) {
            collapseEl.lastCollapseDirCls = me.collapseDirProps[me.collapseDirection].cls;
        }
    },
 
    getCollapseDirection: function() {
        var me = this,
            dir = me.collapseDirection,
            collapseTarget, idx, items, type;
 
        if (!dir) {
            collapseTarget = me.collapseTarget;
 
            if (collapseTarget.isComponent) {
                dir = collapseTarget.collapseDirection;
            }
 
            if (!dir) {
                // Avoid duplication of string tests.
                // Create a two bit truth table of the configuration of the Splitter:
                // Collapse Target | orientation
                //        0              0             = next, horizontal
                //        0              1             = next, vertical
                //        1              0             = prev, horizontal
                //        1              1             = prev, vertical
                type = me.ownerCt.layout.type;
 
                if (collapseTarget.isComponent) {
                    items = me.ownerCt.items;
                    idx = Number(items.indexOf(collapseTarget) === items.indexOf(me) - 1) << 1 |
                          Number(type === 'hbox');
                }
                else {
                    idx = Number(me.collapseTarget === 'prev') << 1 | Number(type === 'hbox');
                }
 
                // Read the data out the truth table
                dir = ['bottom', 'right', 'top', 'left'][idx];
            }
 
            me.collapseDirection = dir;
        }
 
        me.setOrientation((dir === 'top' || dir === 'bottom') ? 'horizontal' : 'vertical');
 
        return dir;
    },
 
    getCollapseTarget: function() {
        var me = this;
 
        return me.collapseTarget.isComponent
            ? me.collapseTarget
            : me.collapseTarget === 'prev' ? me.previousSibling() : me.nextSibling();
    },
 
    setCollapseEl: function(display) {
        var el = this.collapseEl;
 
        if (el) {
            el.setDisplayed(display);
        }
    },
 
    onBeforeTargetExpand: function(target) {
        this.setCollapseEl('none');
    },
 
    onBeforeTargetCollapse: function() {
        this.setCollapseEl('none');
    },
 
    onTargetCollapse: function(target) {
        var me = this;
 
        // Only add the collapsed class if the collapse was from our target
        // (not bubbled from below as in a Dashboard Column) and was in the dimension
        // which this Splitter controls.
        if (target === me.getCollapseTarget() &&
            target[me.orientation === 'vertical' ? 'collapsedHorizontal' : 'collapsedVertical']()) {
            me.el.addCls(me.collapsedClsInternal + ' ' + (me.collapsedCls || ''));
        }
 
        me.setCollapseEl('');
    },
 
    onTargetExpand: function(target) {
        var me = this;
 
        me.el.removeCls(me.collapsedClsInternal + ' ' + (me.collapsedCls || ''));
        me.setCollapseEl('');
    },
 
    collapseDirProps: {
        top: {
            cls: Ext.baseCSSPrefix + 'layout-split-top'
        },
        right: {
            cls: Ext.baseCSSPrefix + 'layout-split-right'
        },
        bottom: {
            cls: Ext.baseCSSPrefix + 'layout-split-bottom'
        },
        left: {
            cls: Ext.baseCSSPrefix + 'layout-split-left'
        }
    },
 
    orientationProps: {
        horizontal: {
            opposite: 'vertical',
            fixedAxis: 'height',
            stretchedAxis: 'width'
        },
        vertical: {
            opposite: 'horizontal',
            fixedAxis: 'width',
            stretchedAxis: 'height'
        }
    },
 
    applyCollapseDirection: function() {
        var me = this,
            collapseEl = me.collapseEl,
            collapseDirProps = me.collapseDirProps[me.collapseDirection],
            cls;
 
        if (collapseEl) {
            cls = collapseEl.lastCollapseDirCls;
 
            if (cls) {
                collapseEl.removeCls(cls);
            }
 
            collapseEl.addCls(collapseEl.lastCollapseDirCls = collapseDirProps.cls);
        }
    },
 
    applyOrientation: function() {
        var me = this,
            orientation = me.orientation,
            orientationProps = me.orientationProps[orientation],
            defaultSize = me.size,
            fixedSizeProp = orientationProps.fixedAxis,
            stretchSizeProp = orientationProps.stretchedAxis,
            cls = me.baseCls + '-';
 
        me[orientation] = true;
        me[orientationProps.opposite] = false;
 
        if (!me.hasOwnProperty(fixedSizeProp) || me[fixedSizeProp] === '100%') {
            me[fixedSizeProp] = defaultSize;
        }
 
        if (!me.hasOwnProperty(stretchSizeProp) || me[stretchSizeProp] === defaultSize) {
            me[stretchSizeProp] = '100%';
        }
 
        me.removeCls(cls + orientationProps.opposite);
        me.addCls(cls + orientation);
    },
 
    setOrientation: function(orientation) {
        var me = this;
 
        if (me.orientation !== orientation) {
            me.orientation = orientation;
            me.applyOrientation();
        }
    },
 
    updateOrientation: function() {
        delete this.collapseDirection; // recompute
        this.getCollapseDirection();
        this.applyCollapseDirection();
    },
 
    toggleTargetCmp: function(e, t) {
        var cmp = this.getCollapseTarget(),
            placeholder = cmp.placeholder,
            toggle;
 
        // We can only toggle the target if it offers the expand/collapse API
        if (Ext.isFunction(cmp.expand) && Ext.isFunction(cmp.collapse)) {
            if (placeholder && !placeholder.hidden) {
                toggle = true;
            }
            else {
                toggle = !cmp.hidden;
            }
 
            if (toggle) {
                if (cmp.collapsed || cmp.floatedFromCollapse) {
                    cmp.expand();
                }
                else if (cmp.collapseDirection) {
                    cmp.collapse();
                }
                else {
                    cmp.collapse(this.renderData.collapseDir);
                }
            }
        }
    },
 
    /*
     * Work around IE bug. %age margins do not get recalculated on element resize
     * unless repaint called.
     */
    setSize: function() {
        var me = this;
 
        me.callParent(arguments);
 
        if (Ext.isIE && me.el) {
            me.el.repaint();
        }
    },
 
    doDestroy: function() {
        Ext.destroy(this.tracker);
 
        this.callParent();
    }
});