/**
 * Value filter class. Use this filter type when you want to filter
 * the left/top axis results by the grand total summary values.
 *
 * Example for a value filter:
 *
 *      {
 *          xtype: 'pivotgrid',
 *
 *          matrix: {
 *              // This example will generate a column for each year
 *              // that has its grand total value between 1,000 and 15,000.
 *              aggregate: [{
 *                  id:         'agg',
 *                  dataIndex:  'value',
 *                  aggregator: 'sum',
 *                  header:     'Total'
 *              }],
 *              topAxis: [{
 *                  dataIndex:  'year',
 *                  header:     'Year',
 *                  filter: {
 *                      type:           'value',
 *                      operator:       'between',
 *                      dimensionId:    'agg',  // this is the id of an aggregate dimension
 *                      value:          [1000, 15000]
 *                  }
 *              }]
 *          }
 *      }
 *
 *
 * Top 10 filter works as following:
 *
 * First of all sort axis groups by grand total value of the specified dimension. The sorting
 * order depends on top/bottom configuration.
 *
 *  - Top/Bottom 10 Items: Keep only the top x items from the groups array
 *
 *  - Top/Bottom 10 Percent: Find first combined values to total at least x percent of the parent
 * grandtotal
 *
 *  - Top/Bottom 10 Sum: Find first combined values to total at least x
 *
 *
 * Example for a top 10 value filter:
 *
 *      {
 *          xtype: 'pivotgrid',
 *
 *          matrix: {
 *              // This example will return the bottom 3 years that have the smallest
 *              // sum of value.
 *              aggregate: [{
 *                  id:         'agg',
 *                  dataIndex:  'value',
 *                  aggregator: 'sum',
 *                  header:     'Total'
 *              }],
 *              leftAxis: [{
 *                  dataIndex:  'year',
 *                  header:     'Year',
 *                  filter: {
 *                      type:           'value',
 *                      operator:       'top10',
 *                      dimensionId:    'agg',   // this is the id of an aggregate dimension
 *                      topType:        'items',
 *                      topOrder:       'bottom',
 *                      value:          3
 *                  }
 *              }]
 *          }
 *      }
 *
 */
Ext.define('Ext.pivot.filter.Value', {
    alternateClassName: [
        'Mz.aggregate.filter.Value'
    ],

    extend: 'Ext.pivot.filter.Base',

    alias: 'pivotfilter.value',

    /**
     * @cfg operator
     * @inheritdoc
     * @localdoc
     *    * `top10`
     *
     */

    /**
     * @cfg {String} dimensionId (required)
     *
     * Id of the aggregate dimension used to filter by the specified value
     *
     */
    dimensionId: '',

    /**
     * @cfg {String} topType
     *
     * Specify here which kind of Top10 we need to perform.
     * Possible values: items, percent, sum
     *
     */
    topType: 'items',

    /**
     * @cfg {String} topOrder
     *
     * Which kind of top10 do you want? Possible values: top, bottom
     *
     */
    topOrder: 'top',

    /**
     * @cfg {Boolean} topSort
     *
     * Should the top10 results be sorted? If True then the dimension sorting is ignored and
     * the results are sorted by the grand total in DESC (topOrder = top) or ASC (topOrder = bottom)
     * order.
     *
     */
    topSort: true,

    /**
     * @property {Boolean} isTopFilter
     * @readonly
     *
     * Is this a top10 type of filter?
     *
     */
    isTopFilter: false,

    constructor: function(config) {
        var ret = this.callParent([config]);

        //<debug>
        if (Ext.isEmpty(this.dimensionId)) {
            Ext.raise('dimensionId is mandatory on Value filters');
        }
        //</debug>

        this.isTopFilter = (this.operator === 'top10');

        return ret;
    },

    destroy: function() {
        this.dimension = null;
        this.callParent();
    },

    getDimension: function() {
        //<debug>
        if (!this.parent.matrix.aggregate.getByKey(this.dimensionId)) {
            Ext.raise('There is no aggregate dimension that matches the dimensionId provided');
        }
        //</debug>

        return this.parent.matrix.aggregate.getByKey(this.dimensionId);
    },

    /**
     * @method getSerialArgs
     * @inheritdoc
     */
    getSerialArgs: function() {
        var me = this;

        return {
            dimensionId: me.dimensionId,
            topType: me.topType,
            topOrder: me.topOrder,
            topSort: me.topSort
        };
    },

    /**
     * This function performs top10 on the specified array.
     *
     * @param axis
     * @param treeItems
     *
     * @private
     */
    applyFilter: function(axis, treeItems) {
        var me = this,
            items = me.topSort ? treeItems : Ext.Array.clone(treeItems),
            ret = [];

        if (treeItems.length === 0) {
            return ret;
        }

        // sort the items by the grand total value in ASC(top)/DESC(bottom) order
        me.sortItemsByGrandTotal(axis, items);

        switch (me.topType) {
            case 'items':
                ret = me.extractTop10Items(items);
                break;

            case 'sum':
                ret = me.extractTop10Sum(items);
                break;

            case 'percent':
                ret = me.extractTop10Percent(axis, items);
                break;
        }

        if (!me.topSort) {
            items.length = 0;
        }

        return ret;
    },

    /**
     *
     * @param items
     * @returns {Array}
     *
     * @private
     */
    extractTop10Items: function(items) {
        // we have to extract all values which are not part of the top
        // ie: we need to extract top2 but there are 3 values which are the same
        var me = this,
            uniqueValues = [],
            i;

        for (i = 0; i < items.length; i++) {
            if (uniqueValues.indexOf(items[i].tempVar) < 0) {
                uniqueValues.push(items[i].tempVar);

                if (uniqueValues.length > me.value || (me.value < i + 1 && i > 0)) {
                    break;
                }
            }
        }

        return Ext.Array.slice(items, i);
    },

    /**
     *
     * @param items
     * @returns {Array}
     *
     * @private
     */
    extractTop10Sum: function(items) {
        var me = this,
            sum = 0,
            i;

        for (i = 0; i < items.length; i++) {
            sum += items[i].tempVar;

            if (sum >= me.value) {
                break;
            }
        }

        return Ext.Array.slice(items, i + 1);
    },

    /**
     *
     * @param axis
     * @param items
     * @returns {Array}
     *
     * @private
     */
    extractTop10Percent: function(axis, items) {
        var me = this,
            sum = 0,
            keys = items[0].key.split(axis.matrix.keysSeparator),
            i, leftKey, topKey, parentKey, result, grandTotal;

        // let's find the parent grand total
        keys.length--;
        parentKey =
            (keys.length > 0 ? keys.join(axis.matrix.keysSeparator) : axis.matrix.grandTotalKey);
        leftKey = (axis.isLeftAxis ? parentKey : axis.matrix.grandTotalKey);
        topKey = (axis.isLeftAxis ? axis.matrix.grandTotalKey : parentKey);

        result = axis.matrix.results.get(leftKey, topKey);
        grandTotal = (result ? result.getValue(me.dimensionId) : 0);

        for (i = 0; i < items.length; i++) {
            sum += items[i].tempVar;

            if ((sum * 100 / grandTotal) >= me.value) {
                break;
            }
        }

        return Ext.Array.slice(items, i + 1);
    },

    /**
     *
     * @param axis
     * @param items
     *
     * @private
     */
    sortItemsByGrandTotal: function(axis, items) {
        var me = this,
            leftKey, topKey, result, i;

        // let's fetch the grandtotal values and store them in a temp var on each item
        for (i = 0; i < items.length; i++) {
            leftKey = (axis.isLeftAxis ? items[i].key : axis.matrix.grandTotalKey);
            topKey = (axis.isLeftAxis ? axis.matrix.grandTotalKey : items[i].key);
            result = axis.matrix.results.get(leftKey, topKey);

            items[i].tempVar = (result ? result.getValue(me.dimensionId) : 0);
        }

        Ext.Array.sort(items, function(a, b) {
            var result = axis.matrix.naturalSort(a.tempVar, b.tempVar);

            if (result < 0 && me.topOrder === 'top') {
                return 1;
            }

            if (result > 0 && me.topOrder === 'top') {
                return -1;
            }

            return result;
        });
    }
});
