/**
 * This class is used to initialize the dimensions defined on the pivot grid leftAxis,
 * topAxis and aggregate.
 *
 *
 */
Ext.define('Ext.pivot.dimension.Item', {
    alternateClassName: [
        'Mz.aggregate.dimension.Item'
    ],
 
    requires: [
        'Ext.pivot.MixedCollection',
        'Ext.pivot.filter.Label',
        'Ext.pivot.filter.Value'
    ],
    
    /**
     * @cfg {String} header (required)
     *
     * This text is visible in the pivot grid in the following cases:
     *
     * - the dimension is defined on the left axis. The pivot grid will generate one grid column per dimension and
     * this header will go into the grid column header.
     *
     * - the dimension is defined on the aggregate. The pivot grid will generate one grid column per dimension per top
     * axis label. If there are at least 2 aggregate dimensions then this header will be visible. When only one is
     * defined the aggregate dimension header is replaced by the top axis label.
     *
     * - if the {@link Ext.pivot.plugin.Configurator Configurator plugin} is used then this header will be visible
     * in the axis panels.
     *
     */
    header:             '',
 
    /**
     * @cfg {String} dataIndex (required)
     * The field name on the record from where this dimension extracts data.
     */
    dataIndex:          '', 
 
    /**
     * @cfg {String} sortIndex 
     * Field name on the record used when sorting this dimension results. Defaults to {@link #dataIndex} if
     * none is specified.
     */
    sortIndex:          '', 
 
    /**
     * @cfg {Number} [width=100]
     * Default column width when this dimension is used on the top/left axis.
     * Used by the generated columns.
     */
    width:              100,
 
    /**
     * @cfg {Number} [flex=0]
     * Column flex when this dimension is used on the top/left axis.
     * Used by the generated columns.
     */
    flex:               0,
 
    /**
     * @cfg {String} [align="left"]
     * Column alignment when this dimension is used on the top/left axis.
     * Used by the generated columns.
     */
    align:              'left',
 
    /**
     * @cfg {Boolean} [sortable=true]
     * Is this dimension sortable when the pivot is generated?
     */
    sortable:           true,
 
    /**
     * @cfg {"ASC"/"DESC"} [direction="ASC"]
     * If this dimension is sortable then this is the type of sorting.
     */
    direction:          'ASC',
 
    /**
     * @cfg {Function} sorterFn 
     * Provide here your own sorting function for this dimension.
     * If none is specified then the defaultSorterFn is used.
     */
    sorterFn:           null,
 
    /**
     * @cfg {Boolean} [caseSensitiveSort=true]
     * If this dimension is sortable, should we do a case sensitive sort?
     */
    caseSensitiveSort:  true,
    
    /**
     * @cfg {Ext.pivot.filter.Base} filter
     * Provide a filter configuration to filter your axis items.
     * This works only on left/top axis dimensions.
     *
     * Example for a label filter:
     *
     *      {
     *          dataIndex:  'year',
     *          header:     'Year',
     *          filter: {
     *              type:       'label',
     *              operator:   '=',
     *              value:      2012
     *          }
     *      }
     *
     *
     * Example for a value filter:
     *
     *      {
     *          dataIndex:  'year',
     *          header:     'Year',
     *          filter: {
     *              type:       'value',
     *              operator:   'between',
     *              value:      [2012, 2015]
     *          }
     *      }
     *
     *
     * Example for a top 10 value filter:
     *
     *      {
     *          dataIndex:  'year',
     *          header:     'Year',
     *          filter: {
     *              type:           'value',
     *              operator:       'top10',
     *              dimensionId:    'value',   // this is the id of an aggregate dimension
     *              topType:        'items',
     *              topOrder:       'bottom'
     *          }
     *      }
     */
    filter:             null,
    
    /**
     * @cfg {String/Function} renderer
     * Default renderer for this dimension. This renderer is used when displaying the data in the pivot table.
     * You can either provide a string value with a number format or your own function.
     * The renderer function will have only one parameter and that is the value that need to be formatted.
     * The renderer function is running in the Dimension scope.
     *
     * If a renderer function is defined for a left or top axis dimension then this is NOT used in the grid.
     * It is instead used for formatting the value as you wish.
     *
     * On the other hand if you define a renderer function on an aggregate dimension then this will be
     * used in the grid and allows you to change cell formatting as well.
     */
    renderer:           null,
 
    /**
     * @cfg {Function} grouperFn 
     * This function is used when the groups are generated for the axis.
     * It will return the value that will uniquely identify a group on the axis.
     * ie: you have a Date field that you want to group by year.
     * This renderer could return the year from that Date value.
     *
     * The function receives one parameter and that is the record.
     */
    grouperFn:          null,
    
    /**
     * @cfg {String} [blankText="(blank)"]
     * Default text to use when a group name is blank. This value is applied even if you set your own group renderer.
     */
    blankText:          '(blank)',
 
    /**
     * @cfg {Boolean} [showZeroAsBlank=false]
     * Should 0 values be displayed as blank? This config is used when
     * this is an aggregate dimension.
     */
    showZeroAsBlank:    false,
    
    /**
     * @cfg {String/Function} [aggregator="sum"]
     * This is the function that should be used to aggregate when this is an aggregate dimension.
     * You can either provide a function name available in {@link Ext.pivot.Aggregators} or
     * set your own function.
     *
     * It's probably best to override {@link Ext.pivot.Aggregators} to add you own function
     * and use that function name on this config. This way the stateles pivot will save this value.
     */
    aggregator:         'sum',
 
    /**
     * @property {Boolean} isAggregate 
     * True to identify a dimension of an aggregate configuration.
     */
    isAggregate:        false,
 
    /**
     * @cfg {String} id 
     * Unique id of this dimension.
     */
    id:                 '',
 
    /**
     * @cfg {Object[]} values
     * Collection of unique values on this dimension; each item has a "value" and a "display".
     */
    values:             null,
 
    /**
     * @property {Ext.pivot.matrix.Base} matrix
     * @readonly
     * Reference to the matrix object.
     */
    matrix:             null,
 
    constructor: function(config){
        var me = this,
            aggregators = Ext.pivot.Aggregators;
        
        me.initialConfig = config || {};
        
        if(config.isAggregate === true && Ext.isEmpty(config.align)){
            config.align = 'left';
        }
        Ext.apply(me, config || {});
        
        if(Ext.isEmpty(me.id)){
            // generate an internal id used by the matrix 
            me.id = Ext.id();
        }
        
        if(me.isAggregate){
            if(Ext.isEmpty(me.dataIndex) && Ext.isDefined(me.measure)){
                me.dataIndex = me.measure;
                delete me.measure;
            }
            if(Ext.isEmpty(me.aggregator)){
                me.aggregator = 'sum';
            }
            if(Ext.isString(me.aggregator) && Ext.isFunction(aggregators[me.aggregator])) {
                me.aggregatorFn = Ext.bind(aggregators[me.aggregator], aggregators);
            }else if(Ext.isFunction(me.aggregator)){
                me.aggregatorFn = me.aggregator;
            }
            me.filter = false;
        }else{
            if(Ext.isObject(me.filter)){
                Ext.applyIf(me.filter, {
                    type:   'label',
                    parent: me
                });
                me.filter = Ext.Factory.pivotfilter(me.filter);
            }else{
                me.filter = false;
            }
        }
        
        if(!Ext.isFunction(me.grouperFn)){
            me.grouperFn = me.defaultGrouperFn;
        }
        if(me.sortable && !me.sorterFn){
            me.sorterFn = me.defaultSorterFn;
        }
        if(Ext.isEmpty(me.sortIndex)){
            me.sortIndex = me.dataIndex;
        }
        
        if(!me.renderer){
            me.renderer = me.getDefaultFormatRenderer(me.isAggregate ? '0,000.00' : '');
        }else if(Ext.isString(me.renderer)){
            me.renderer = me.getDefaultFormatRenderer(me.renderer);
        }
        
        me.values = Ext.create('Ext.pivot.MixedCollection');
        me.values.getKey = function(item){
            return item.value;
        };
 
        me.callParent(arguments);
    },
    
    destroy: function(){
        var me = this;
 
        Ext.destroyMembers(me, 'values', 'filter');
        me.matrix = me.values = me.filter = null;
    },
    
    /**
     * Returns the serialized dimension data.
     */
    serialize: function(){
        var me = this;
        
        return {
            id:                 me.id,
            header:             me.header,
            dataIndex:          me.dataIndex,
            sortIndex:          me.sortIndex,
            width:              me.width,
            flex:               me.flex,
            align:              me.align,
            sortable:           me.sortable,
            direction:          me.direction,
            caseSensitiveSort:  me.caseSensitiveSort,
            filter:             me.filter ? me.filter.serialize() : null,
            aggregator:         Ext.isString(me.aggregator) ? me.aggregator : 'sum', // functions cannot be serialized 
            showZeroAsBlank:    me.showZeroAsBlank
        };
    },
    
    /**
     * Add unique values available for this dimension. These are used when filtering.
     *
     * @param value
     * @param display
     */
    addValue: function(value, display){
        if(!this.values.getByKey(value)){
            this.values.add({
                value:      value,
                display:    display
            });
        }
    },
    
    /**
     * Returns the collection of unique values available for this dimension.
     */
    getValues: function(){
        return this.values;
    },
    
    /**
     * Returns the internal id of this dimension.
     */
    getId: function(){
        return this.id;
    },
    
    /**
     * Default sorter function used to sort the axis dimensions on the same tree level.
     *
     * @param o1
     * @param o2
     *
     * @returns {Number}
     */
    defaultSorterFn: function(o1, o2){
        var me = this,
            s1 = o1.sortValue,
            s2 = o2.sortValue,
            result;
        
        if(s1 instanceof Date){
            s1 = s1.getTime();
        }
        if(s2 instanceof Date){
            s2 = s2.getTime();
        }
        if(!me.caseSensitiveSort){
            s1 = String(s1).toUpperCase();
            s2 = String(s2).toUpperCase();
        }
        result = Ext.pivot.matrix.Base.prototype.naturalSort(s1, s2);
        
        if(result < 0 && me.direction === 'DESC'){
            return 1;
        }
        if(result > 0 && me.direction === 'DESC'){
            return -1;
        }
        return result;
    },
    
    /**
     * Builds a renderer function by using the specified format.
     *
     * @param format Could be either a function or a string
     */
    getDefaultFormatRenderer: function(format){
        var me = this;
        
        return function(v){
            var positive;
            
            if(Ext.isEmpty(format)){
                return v;
            }
            
            if(Ext.isFunction(format)){
                return format.apply(me, arguments);
            }
            
            if(!Ext.isNumber(v)) {
                return v;
            }
 
            if(me.isAggregate && v === 0 && me.showZeroAsBlank){
                return '';
            }
            
            positive = (>= 0);
            v = Math.abs(v);
            v = Ext.util.Format.number(v, format);
 
            return positive ? v : '-' + v;
        }
    },
    
    /**
     * Default grouper function used for rendering axis item values.
     * The grouper function can be used to group together multiple items.
     * Returns a group value
     *
     * @param record
     */
    defaultGrouperFn: function(record) {
        return record.get(this.dataIndex);
    }
 
});