/**
 * @private
 */
Ext.define('Ext.scroll.indicator.Overlay', {
    extend: 'Ext.scroll.indicator.Indicator',
    alias: 'scrollindicator.overlay',
 
    config: {
        /**
         * @cfg {'x'/'y'}
         * @private
         */
        axis: null,
 
        /**
         * @cfg {Boolean/Object}
         * @private
         */
        hideAnimation: true,
 
        /**
         * @cfg {Number} 
         * @private
         * Number of milliseconds to delay hiding Indicators when scrolling ends.
         */
        hideDelay: 100
    },
 
    defaultHideAnimation: {
        to: {
            opacity: 0
        },
        duration: 300
    },
 
    names: {
        x: {
            side: 'l',
            getSize: 'getHeight',
            setLength: 'setWidth',
            translate: 'translateX'
        },
        y: {
            side: 't',
            getSize: 'getWidth',
            setLength: 'setHeight',
            translate: 'translateY'
        }
    },
 
    oppositeAxis: {
        x: 'y',
        y: 'x'
    },
 
    classCls: Ext.baseCSSPrefix + 'scrolloverlay',
 
    visible: false,
 
    element: {
        reference: 'element',
        style: 'opacity: 0'
    },
 
    applyHideAnimation: function(hideAnimation) {
        if (hideAnimation) {
            hideAnimation = Ext.mergeIf({
                onEnd: this.onHideAnimationEnd,
                scope: this
            }, this.defaultHideAnimation, hideAnimation);
        }
 
        return hideAnimation;
    },
 
    constructor: function(config) {
        var me = this,
            axis;
 
        me.callParent([config]);
 
        if (Ext.os.is.iOS) {
            me.addUi('ios');
        }
 
        if (Ext.os.is.Android) {
            me.addUi('android');
        }
 
        axis = me.getAxis();
 
        me.names = me.names[axis];
    },
 
    /**
     * Hides this scroll indicator
     */
    hide: function() {
        var me = this,
            delay = me.getHideDelay();
 
        if (!me.visible) {
            return;
        }
 
        me.visible = false;
 
        if (delay) {
            me._hideTimer = Ext.defer(me.doHide, delay, me);
        }
        else {
            me.doHide();
        }
    },
 
    updateScroller: function(scroller) {
        scroller.getElement().appendChild(this.element);
    },
 
    /**
     * Sets the value of this scroll indicator.
     * @param {Number} value The scroll position on the configured {@link #axis}
     */
    updateValue: function(value) {
        var me = this,
            el = me.element,
            names = me.names,
            axis = me.getAxis(),
            scroller = me.getScroller(),
            maxScrollPosition = scroller.getMaxUserPosition()[axis],
            clientSize = scroller.getClientSize()[axis],
            baseLength = me.length,
            length = baseLength,
            maxPosition = clientSize - baseLength - me.sizeAdjust,
            round = Math.round,
            position;
 
        if (value < 0) {
            length = round(baseLength + (baseLength * value / clientSize));
            position = 0;
        }
        else if (value > maxScrollPosition) {
            length = round(baseLength - (baseLength * (value - maxScrollPosition) / clientSize));
            position = maxPosition + baseLength - length;
        }
        else {
            position = round(value / maxScrollPosition * maxPosition);
        }
 
        me[names.translate](position);
        el[names.setLength](length);
    },
 
    /**
     * Shows this scroll indicator
     */
    show: function() {
        var me = this,
            el = me.element,
            anim = el.getActiveAnimation();
 
        if (me.visible) {
            return;
        }
 
        me.visible = true;
 
        // Stop the fade out animation for both toolkit animation types.
        // TODO: remove classic version when classic Ext.dom.Element overrides retire.
        if (anim) {
            anim.end();
        }
 
        if (el.stopAnimation) {
            el.stopAnimation();
        }
 
        if (!me.size) {
            me.cacheStyles();
        }
 
        me.refreshLength();
        clearTimeout(me._hideTimer);
        el.setStyle('opacity', '');
    },
 
    destroy: function() {
        this.callParent();
        clearTimeout(this._hideTimer);
    },
 
    privates: {
        /**
         * Caches the values that are set via stylesheet rules (size and margin)
         * @private
         */
        cacheStyles: function() {
            var me = this,
                el = me.element,
                names = me.names;
 
            /**
             * @property {Number} size
             * @private
             * The indicator's size (width if vertical, height if horizontal)
             */
            me.size = el[names.getSize]();
 
            /**
             * @property {Number} margin
             * @private
             * The indicator's margin (the space between the indicator and the container's edge)
             */
            me.margin = el.getMargin(names.side);
        },
 
        doHide: function() {
            var animation = this.getHideAnimation(),
                el = this.element;
 
            if (animation) {
                el.animate(animation);
            }
            else {
                el.setStyle('opacity', 0);
            }
        },
 
        /**
         * Returns true if the scroller that this indicator is attached to has scrolling
         * enabled on the opposite axis
         * @private
         * @return {Boolean} 
         */
        hasOpposite: function() {
            return this.getScroller().isAxisEnabled(this.oppositeAxis[this.getAxis()]);
        },
 
        onHideAnimationEnd: function() {
            this.element.setStyle('opacity', '0');
        },
 
        onScroll: function() {
            var me = this;
 
            me.setValue(me.getScroller().position[me.getAxis()]);
        },
 
        onScrollEnd: function() {
            this.hide();
        },
 
        onScrollStart: function() {
            var me = this;
 
            me.setValue(me.getScroller().position[me.getAxis()]);
 
            me.show();
        },
 
        refreshLength: function() {
            var me = this,
                names = me.names,
                axis = me.getAxis(),
                scroller = me.getScroller(),
                scrollSize = scroller.getSize()[axis],
                clientSize = scroller.getClientSize()[axis],
                ratio = clientSize / scrollSize,
                baseSizeAdjust = me.margin * 2,
                sizeAdjust = me.hasOpposite() ? (baseSizeAdjust + me.size) : baseSizeAdjust,
                length = Math.round((clientSize - sizeAdjust) * ratio);
 
            me.sizeAdjust = sizeAdjust;
 
            /**
             * @property {Number} length
             * @private
             * The indicator's "length" (height for vertical indicators, or width for
             * horizontal indicators)
             */
            me.length = length;
            me.element[names.setLength](length);
        },
 
        translateX: function(value) {
            this.element.translate(value);
        },
 
        translateY: function(value) {
            this.element.translate(0, value);
        }
    }
});