/**
 * A component to show indication of an active item.
 */
Ext.define('Ext.Indicator', {
    extend: 'Ext.Component',
    xtype: 'indicator',
 
    config: {
        /**
         * @cfg {Number} activeIndex The index of the active indicator
         * dot.
         */
        activeIndex: null,
 
        /**
         * @cfg {Number} count The number of indicator dots to show.
         */
        count: null,
 
        /**
         * @cfg {String} [direction=horizontal] The direction the indicator
         * will be shown. Can be `horizontal` to have the dots be shown
         * in a row or `vertical` to have the dots be shown in a column.
         */
        direction: 'horizontal',
 
        /**
         * @cfg {String} [tapMode=direction] Controls what happens when clicked
         * on. The following are valid options:
         *
         * - **`direction`** Depending where the click happened, if on the right
         * half of the indicator, the next item will be made active else the
         * previous item will be made active.
         * - **`item`** If the click was on an indicator dot, the associated item
         * to that dot will be made active. If the click was not on an indicator
         * dot, then `direction` mode will be used.
         */
        tapMode: 'direction'
    },
 
    /**
     * @property {String} [activeCls=active] The CSS class name that will
     * be added to the active indicator dot.
     * @protected
     */
    activeCls: Ext.baseCSSPrefix + 'indicator-active',
 
    baseCls: Ext.baseCSSPrefix + 'indicator',
 
    /**
     * @property {String} [itemCls=x-indicator-item] The CSS class name
     * that will be added to the indicator dots.
     * @protected
     */
    itemCls: Ext.baseCSSPrefix + 'indicator-item',
 
    defaultBindProperty: 'activeIndex',
 
    twoWayBindable: [
        'activeIndex'
    ],
 
    /**
     * @private
     */
    isIndicator: true,
 
    /**
     * @property {Array} indicators An array of indicator dot
     * elements.
     * @private
     */
 
    /**
     * @event indicatortap
     * Fires when an indicator dot has been tapped and {@link #tapMode}
     * is set to `item`.
     * @param {Ext.Indicator} this 
     * @param {Number} index The index of the indicator dot.
     * @param {Ext.dom.Elemnet} item The indicator dot item.
     */
 
    /**
     * @event next
     * Fires when this indicator is tapped on the right half
     * @param {Ext.Indicator} this 
     */
 
    /**
     * @event previous
     * Fires when this indicator is tapped on the left half
     * @param {Ext.Indicator} this 
     */
 
    constructor: function (config) {
        this.indicators = [];
 
        this.callParent([config]);
    },
 
    initialize: function () {
        this.callParent();
 
        this.element.on({
            tap: 'onTap',
            scope: this
        });
    },
 
    doDestroy: function () {
        Ext.destroy(this.indicators);
 
        this.callParent();
    },
 
    //<debug>
    applyActiveIndex: function (index) {
        var indicators = this.indicators,
            max = indicators.length - 1;
 
        if (index > max) {
            Ext.raise('Cannot set the active index greater than the number of indicators');
        }
 
        return index;
    },
    //</debug>
 
    updateActiveIndex: function (index, oldIndex) {
        var activeCls = this.activeCls,
            baseCls = this.baseCls,
            indicators = this.indicators,
            currentActiveItem = indicators[oldIndex],
            activeItem = indicators[index];
 
        if (currentActiveItem) {
            currentActiveItem.removeCls(activeCls);
        }
 
        if (activeItem) {
            activeItem.addCls(activeCls);
        }
    },
 
    updateCount: function (count) {
        var indicators = this.indicators;
 
        while (indicators.length < count) {
             this.doAdd()
        }
 
        while (indicators.length > count) {
            this.doRemove();
        }
    },
 
    //<debug>
    applyDirection: function (direction) {
        if (direction !== 'vertical' && direction !== 'horizontal') {
            Ext.raise('Invalid indicator direction provided: ' + direction);
 
            direction = 'horizontal';
        }
 
        return direction;
    },
    //</debug>
 
    updateDirection: function (newDirection, oldDirection) {
        this.element.replaceCls(oldDirection, newDirection, this.baseCls);
    },
 
    /**
     * Syncs the number of indicators and sets the active index.
     * @param {Number} count The number of indicators that needs to be shown. If `null`,
     * will skip syncing the number of indicators
     * @param {Number} activeIndex If specified, will set the {@link #activeIndex}.
     * @return {Ext.Indicator} this
     */
    sync: function (count, activeIndex) {
        if (Ext.isNumber(count)) {
            this.setCount(count);
        }
 
        if (Ext.isNumber(activeIndex)) {
            this.setActiveIndex(activeIndex);
        }
 
        return this;
    },
 
    /**
     * Adds an indicator dot at the end.
     * @return {Ext.Indicator} this
     */
    add: function () {
        var count = this.getCount();
 
        return this.setCount(++count);
    },
 
    /**
     * Removes all indicator dots.
     * @return {Ext.Indicator} this
     */
    removeAll: function () {
        return this.setCount(0);
    },
 
    /**
     * Removes the last indicator dot.
     * @return {Ext.Indicator} this
     */
    remove: function () {
        var count = this.getCount();
 
        return this.setCount(--count);
    },
 
    /**
     * Creates an indicator dot and addes it to {@link #indicators}.
     * @private
     * @return {Ext.Indicator} this
     */
    doAdd: function () {
        var indicators = this.indicators;
 
        indicators.push(this.element.createChild({
            tag: 'span',
            cls: this.itemCls
        }));
 
        return this;
    },
 
    /**
     * Removes and destroys the last indicator dot.
     * @private
     * @return {Ext.Indicator} this
     */
    doRemove: function () {
        var indicators = this.indicators,
            indicator = indicators.pop();
 
        if (indicator) {
            indicator.destroy();
        }
 
        return this;
    },
 
    /**
     * @private
     */
    onTap: function (e) {
        var mode = this.getTapMode();
 
        if (mode === 'item') {
            this.onTapItem(e);
        } else {
            this.onTapDirection(e);
        }
    },
 
    /**
     * Handles the tap when {@link #tapMode} is set to `item`.
     * @private
     */
    onTapItem: function (e) {
        var me = this,
            item = e.getTarget('.' + me.itemCls, 1, true),
            index;
 
        if (item) {
            index = me.indicators.indexOf(item);
 
            if (index !== -1) {
                me.fireEvent('indicatortap', me, index, item);
            }
        } else {
            //tap wasn't on a dot, go with direction
            me.onTapDirection(e);
        }
    },
 
    /**
     * Handles the tap when {@link #tapMode} is set to `direction`.
     * @private
     */
    onTapDirection: function (e) {
        var me = this,
            direction = me.getDirection(),
            touch = e.touch,
            box = me.element.getBox(),
            centerX = box.left + (box.width / 2),
            centerY = box.top + (box.height / 2),
            event = (direction === 'horizontal' && touch.pageX >= centerX) ||
                    (direction === 'vertical' && touch.pageY >= centerY) ? 'next' : 'previous';
 
        me.fireEvent(event, me);
    }
});