/**
 * @author Slava Smolin <vsmolin@gmail.com>
 * Developed for Solve360 CRM's touch version (www.solve360.com), owned by
 * Norada corp. (www.norada.com)
 *
 * Free for non-commercial and commercial usage provided all the information
 * at the top of the file stays intact.
 *
 * @class Ext.ux.TwinPanel
 * @extends Ext.Panel
 *
 * Version 1.0.0
 *
 * <p>iOs style two panel component, a panel consisting of two sub-panels: nav (usually for controls) and res (for
 * content corresponding to the current state of controls at nav panel. res panel is draggable.</p>
 *
 * Custom events:
 *  respanelswiped - fires when res panel gets expanded/collapsed
 *  params:
 *      panel {Ext.ux.TwinPanel} Reference to the panel
 *      uxResCollapsed {Boolean} True if collapsed
 */
Ext.define('Ext.ux.TwinPanel', {
    extend: 'Ext.Panel',

    /**
     * D&D instance for res panel
     * @property uxDraggable
     * @type {Ext.util.Draggable}
     * @private
     */
    uxDraggable: null,
    /**
     * True if res panel is being dragged
     * @property uxDragging
     * @type {Boolean}
     * @private
     */
    uxDragging: false,
    /**
     * Res panel's offset from the last 'drag' event
     * @property uxPrevOffset
     * @type {Number}
     */
    uxPrevOffset: 0,

    config: {
        cls: 'ux-twin-panel',
        fullscreen: true,
        layout: 'default',

        /**
         * Width of navPanel's part visible when it is swiped to the right
         * @type {Number}
         */
        uxOverlapSize: 50,
        /**
         * A panel with controls; non-draggable, non-swipable; placed on the left
         * @type {Ext.Panel}
         */
        uxNavPanel: null,
        /**
         * A panel with results corresponding the current controls' state at navPanel; draggable, swipable;
         * placed on the right
         * @type {Ext.Panel}
         */
        uxResPanel: null,
        /**
         * Period of time "drag" event is fired (ms)
         * @type {Number}
         */
        uxDragBuffer: 50,
        /**
         * Min difference between current and previous position of res panel to swipe the panel to the very end
         * @type {Number}
         */
        uxDragThreshold: 50,
        /**
         * True if res panel is collapsed
         * @type {Boolean}
         */
        uxResCollapsed: true
    },

    constructor : function (config) {
        config = config || {};

        config.items = [config.uxNavPanel, config.uxResPanel];

        this.callParent([config]);

        // to prevent panel's position jumping on initial positioning; shown in resetPanelsPosSize
        this.getUxResPanel().hide();
    },

    initialize: function() {
        this.callParent();

        this.on({
            painted: {
                fn: 'initPanels',
                single: true
            },
            resize: {
                fn: 'resetPanelsPosSize'
            }
        });
    },

    /**
     * Position nav and res panels properly and add &d functionality
     */
    initPanels: function () {
        var draggable;

        this.uxPrevOffset = this.getUxResCollapsed() ? this.getResPanelMaxOffset() : 0;

        // res panel is draggable
        draggable = Ext.create('Ext.util.Draggable', {
            element: this.getUxResPanel().element,
            direction: 'horizontal'
        });

        draggable.on({
            dragstart: function () {
                this.uxDragging = true;
            },
            drag: {
                buffer: this.getUxDragBuffer(),
                fn: 'onDrag',
                scope: this
            },
            dragend: 'onDragEnd',
            scope: this
        });

        draggable.on({
            drag: function (drag, ev, offsetX) {
                this.setNavPanelPos(offsetX);
            },
            scope: this
        });

        this.uxDraggable = draggable;
    },

    /**
     * Remember the last offset of res panel
     * @param {Ext.util.Draggable} drag Draggable object
     * @param {Ext.event.Event} ev Event object
     * @param {Number} offsetX Current offset
     */
    onDrag: function (drag, ev, offsetX) {
        if (!this.uxDragging) return;

        this.uxPrevOffset = offsetX;
    },

    /**
     * Position res panel when dragging if over
     * @param {Ext.util.Draggable} drag Draggable object
     * @param {Ext.event.Event} ev Event object
     * @param {Number} offsetX Current offset
     */
    onDragEnd: function (drag, ev, offsetX) {
        var diff = this.uxPrevOffset - offsetX,
            navWidth = this.getResPanelMaxOffset(),//this.element.getWidth() - this.getUxOverlapSize(),
            newPos;

        if (Math.abs(diff) > this.getUxDragThreshold()) {
            newPos = diff > 0 ? 0 : navWidth;
        } else { // animate sliding to the closest side
            newPos = offsetX < this.element.getWidth() / 2 ? 0 : navWidth;
        }
        drag.setOffset(newPos);

        this.setNavPanelPos(newPos);

        this.uxPrevOffset = newPos;
        this.setUxResCollapsed(newPos != 0);

        this.uxDragging = false;
    },

    /**
     * Called automatically by ST every time uxResCollapsed changes
     * @param {Number|String|Boolean} value New value
     */
    updateUxResCollapsed: function (value) {
        this.fireEvent('respanelswiped', this, value);
    },

    /**
     * Position nav panel based on position of res panel (1/3 of its offset)
     * @param {Number} offsetX Current offset of res panel
     */
    setNavPanelPos: function (offsetX) {
        var maxOffset = this.getResPanelMaxOffset();//this.element.getWidth() - this.getUxOverlapSize();
        // shift nav panel to 1/3 of res panel's shift from the right-most position
        this.getUxNavPanel().translate(Math.min(0, (offsetX - maxOffset) / 3));
    },

    /**
     * Sets initial position and size of child panels
     */
    resetPanelsPosSize: function () {
        var navPanel = this.getUxNavPanel(),
            resPanel = this.getUxResPanel(),
            parentWidth = this.element.getWidth(),
            overlapSize = this.getUxOverlapSize(),
            navWidth = parentWidth - overlapSize,
            draggable = this.uxDraggable;

        navPanel.setWidth(navWidth);
        resPanel.setWidth(parentWidth);
        draggable.setConstraint({
            min: { x: 0, y: 0 },
            max: { x: navWidth, y: 0 }
        });
        draggable.setOffset(this.getUxResCollapsed() ? navWidth : 0);

        // hidden when positioning first time after render
        if (resPanel.isHidden()) resPanel.show();
    },

    /**
     * Returns max allowed offset for res panel
     */
    getResPanelMaxOffset: function () {
        return this.element.getWidth() - this.getUxOverlapSize();
    },

    /**
     * Returns max allowed offset for res panel
     * @param {Boolean} collapse True if should collapse res panel
     */
    changeResPanelState: function (collapse) {
        var resPos = collapse ? this.getResPanelMaxOffset() : 0;

        this.setNavPanelPos(resPos);
        this.uxDraggable.setOffset(resPos);
        this.setUxResCollapsed(collapse);
    }
});
