/**
 * This component extends the D3 HeatMap to work with a pivot matrix.
 *
 * Basically this component needs a pivot matrix to be configured. The values
 * calculated by the pivot matrix are distributed as following:
 *
 *  - `leftAxis` maps to HeatMap `xAxis`
 *  - `topAxis` maps to HeatMap `yAxis`
 *  - `aggregate` maps to HeatMap `colorAxis`
 *
 * The pivot matrix should be configured with maximum one dimension per
 * `leftAxis`, `topAxis` or `aggregate`.
 *
 */
Ext.define('Ext.pivot.d3.HeatMap', {
    extend: 'Ext.d3.HeatMap',
    xtype: 'pivotheatmap',

    requires: [
        'Ext.pivot.matrix.Local',
        'Ext.pivot.matrix.Remote'
    ],

    padding: {
        top: 20,
        right: 30,
        bottom: 20,
        left: 80
    },

    config: {
        /**
         * @cfg {String} defaultFormatter
         *
         * Default formatter used to render cells on colorAxis
         */
        defaultFormatter: 'number("0.00")',

        /**
         * @cfg {Ext.pivot.matrix.Base} matrix
         *
         * Pivot matrix specific configuration
         */
        matrix: {
            type: 'local',
            rowGrandTotalsPosition: 'none',
            colGrandTotalsPosition: 'none'
        },

        xAxis: {
            axis: {
                orient: 'bottom'
            },
            scale: {
                type: 'band'
            },
            step: 100
        },

        yAxis: {
            axis: {
                orient: 'left'
            },
            scale: {
                type: 'band'
            },
            step: 100
        },

        colorAxis: {
            scale: {
                type: 'linear',
                range: ['white', 'green']
            }
        },

        tiles: {
            attr: {
                'stroke': 'green',
                'stroke-width': 1
            },
            labels: true
        },

        legend: {
            docked: 'bottom',
            padding: 60,
            items: {
                count: 10,
                slice: [1],
                size: {
                    x: 40,
                    y: 20
                }
            }
        }

    },

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

    applyMatrix: function(matrix) {
        if (matrix) {
            if (!matrix.isPivotMatrix) {
                if (!matrix.type) {
                    matrix.type = 'local';
                }

                matrix.cmp = this;
                matrix = Ext.Factory.pivotmatrix(matrix);
            }
        }

        return matrix;
    },

    updateMatrix: function(matrix, oldMatrix) {
        var me = this;

        Ext.destroy(oldMatrix, me.matrixListeners);
        me.matrixListeners = null;

        if (matrix) {
            me.matrixListeners = matrix.on({
                done: me.onMatrixDataReady,
                scope: me,
                destroyable: true
            });
        }
    },

    updateStore: Ext.emptyFn,
    updateXAxis: Ext.emptyFn,
    updateYAxis: Ext.emptyFn,
    updateColorAxis: Ext.emptyFn,

    onMatrixDataReady: function(matrix) {
        // let's configure the heatmap widget axis
        var me = this,
            xAxis = me.getXAxis(),
            yAxis = me.getYAxis(),
            zAxis = me.getColorAxis(),
            xDim = matrix.topAxis.dimensions,
            yDim = matrix.leftAxis.dimensions,
            zDim = matrix.aggregate,
            dim;

        if (xDim.getCount() && xAxis) {
            dim = xDim.getAt(0);
            xAxis.setField(dim.getDataIndex());
            xAxis.setTitle({
                text: dim.getHeader()
            });
        }

        if (yDim.getCount() && yAxis) {
            dim = yDim.getAt(0);
            yAxis.setField(dim.getDataIndex());
            yAxis.setTitle({
                text: dim.getHeader()
            });
        }

        if (zDim.getCount() && zAxis) {
            dim = zDim.getAt(0);
            zAxis.setField(dim.getId());
        }

        this.processData();
        this.performLayout();
    },

    bindFormatter: function(format, scope) {
        var me = this;

        return function(v) {
            return format(v, scope || me.resolveListenerScope());
        };
    },

    getStoreData: function() {
        var me = this,
            matrix = me.getMatrix(),
            leftItems = matrix.leftAxis.getTree(),
            lenLeft = leftItems.length,
            topItems = matrix.topAxis.getTree(),
            lenTop = topItems.length,
            items = [],
            xAxis = me.getXAxis(),
            yAxis = me.getYAxis(),
            colorAxis = me.getColorAxis(),
            xField = xAxis.getField(),
            yField = yAxis.getField(),
            zField = colorAxis.getField(),
            i, j, leftItem, topItem, result, obj;

        for (i = 0; i < lenLeft; i++) {
            leftItem = leftItems[i];

            for (j = 0; j < lenTop; j++) {
                topItem = topItems[j];
                result = matrix.results.get(leftItem.key, topItem.key);
                obj = { data: { } };
                obj.data[xField] = topItem.name;
                obj.data[yField] = leftItem.name;
                obj.data[zField] = result ? result.getValue(zField) : null;
                obj.data.records = result ? result.records.length : 0;

                items.push(obj);
            }
        }

        return items;
    },

    onUpdateTiles: function(selection) {
        var me = this,
            matrix = me.getMatrix(),
            colorAxis = me.getColorAxis(),
            colorField = colorAxis.getField(),
            dimension, formatter, scope, parser;

        if (matrix.aggregate.getCount()) {
            dimension = matrix.aggregate.getAt(0);
            formatter = dimension.getFormatter() || me.getDefaultFormatter();
            scope = dimension.getScope();

            parser = Ext.app.bind.Parser.fly(formatter);
            formatter = me.bindFormatter(parser.compileFormat(), scope);
            parser.release();
        }

        me.callParent([selection]);

        selection.select('text')
            .text(function(item) {
                var v = item.data[colorField] || null;

                if (v !== null && dimension && formatter) {
                    v = formatter(v);
                }

                return v !== null ? v : null;
            });
    }
});
