/**
 * Provides a visual indicator of scroll position while scrolling using a touch scroller
 *
 * @private
 */
Ext.define('Ext.scroll.Indicator', {
 
    config: {
        /**
         * @cfg {String} axis ('x' or 'y')
         */
        axis: null,
 
        /**
         * @cfg {Ext.dom.Element} containerEl The element in which to render the indicator
         */
        containerEl: null,
 
        /**
         * @cfg {Ext.scroll.Scroller} scroller The scroller instance
         */
        scroller: null,
 
        /**
         * @cfg {Number} minLength The minimum length for the indicator. Defaults to the
         * indicator's "size" (the width of vertical or height of horizontal indicators)
         */
        minLength: null,
 
        /**
         * @cfg {Number} scrollSize The scroller's size on the configured {@link #axis}
         */
        scrollSize: null,
 
        /**
         * @cfg {Number} maxScrollPosition The scroller's maximum scroll position on the
         * configured {@link #axis}
         */
        maxScrollPosition: null,
 
        /**
         * @cfg {Boolean} hasOpposite `true` if this indicator must leave room for a
         * second indicator on the opposite axis
         */
        hasOpposite: null
    },
 
    hideAnimConfig: {
        to: {
            opacity: 0
        },
        duration: 300
    },
 
    names: {
        x: {
            side: 'l',
            getSize: 'getHeight',
            clientSize: 'clientWidth',
            setLength: 'setWidth',
            setPosition: 'setLocalX'
        },
        y: {
            side: 't',
            getSize: 'getWidth',
            clientSize: 'clientHeight',
            setLength: 'setHeight',
            setPosition: 'setLocalY'
        }
    },
 
    cls: Ext.baseCSSPrefix + 'scroll-indicator',
 
    constructor: function(config) {
        var me = this,
            minLength, size, axis;
 
        me.initConfig(config);
 
        axis = me.getAxis();
 
        me.names = me.names[axis];
 
        me.el = me.getContainerEl().createChild({
            cls: me.cls + ' ' + me.cls + '-' + axis
        });
 
        if (!me.size) {
            me.cacheStyles();
        }
 
        minLength = me.getMinLength();
        size = me.size;
 
        if (!minLength || minLength < size) {
            me.setMinLength(size);
        }
    },
 
    cacheStyles: function() {
        var me = this,
            proto = me.self.prototype,
            el = me.el,
            axis = me.getAxis(),
            names = me.names;
 
        /**
         * @property {Number} size 
         * The indicator's size (width if vertical, height if horizontal)
         */
        proto.size = el[names.getSize]();
 
        /**
         * @property {Number} margin 
         * The indicator's margin (the space between the indicator and the container's edge)
         */
        proto.margin = el.getMargin(names.side);
    },
 
    hide: function() {
        this.el.animate(this.hideAnimConfig);
    },
 
    refreshLength: function() {
        var me = this,
            names = me.names,
            scrollSize = me.getScrollSize(),
            containerSize = me.getContainerEl().dom[names.clientSize],
            ratio = containerSize / scrollSize,
            hasOpposite = me.getHasOpposite(),
            baseSizeAdjust = me.margin * 2,
            sizeAdjust = hasOpposite ? (baseSizeAdjust + me.size) : baseSizeAdjust,
            length = Math.max(Math.round((containerSize - sizeAdjust) * ratio), me.getMinLength());
 
        me.sizeAdjust = sizeAdjust;
 
        /**
         * @property {Number} length 
         * The indicator's "length" (height for vertical indicators, or width for
         * horizontal indicators)
         */
        me.length = length;
        me.el[names.setLength](length);
    },
 
    /**
     * Sets the value of this scroll indicator.
     * @param {Number} value The scroll position on the configured {@link #axis}
     */
    setValue: function(value) {
        var me = this,
            el = me.el,
            names = me.names,
            maxScrollPosition = me.getMaxScrollPosition(),
            containerSize = me.getContainerEl().dom[names.clientSize],
            baseLength = me.length,
            minLength = me.getMinLength(),
            length = baseLength,
            maxPosition = containerSize - baseLength - me.sizeAdjust,
            round = Math.round,
            max = Math.max,
            position;
 
        if (value < 0) {
            length = round(max(
                baseLength + (baseLength * value / containerSize),
                minLength
            ));
            position = 0;
        } else if (value > maxScrollPosition) {
            length = round(max(
                baseLength - (baseLength *
                    (value - maxScrollPosition) / containerSize),
                minLength
            ));
            position = maxPosition + baseLength - length;
        } else {
            position = round(value / maxScrollPosition * maxPosition);
        }
 
        el[names.setPosition](position);
        el[names.setLength](length);
    },
 
    show: function() {
        var me = this,
            el = me.el,
            anim = el.getActiveAnimation();
 
        if (anim) {
            anim.end();
        }
 
        me.refreshLength();
        el.setStyle('opacity', '');
    },
 
    destroy: function() {
        this.el.destroy();
    }
 
});