/**
 * This class specifies the definition for a column inside a {@link Ext.grid.Grid}. It encompasses
 * both the grid header configuration as well as displaying data within the grid itself.
 * In general an array of column configurations will be passed to the grid:
 *
 *     @example
 *     Ext.create('Ext.data.Store', {
 *         storeId: 'employeeStore',
 *         fields: ['firstname', 'lastname', 'seniority', 'dep', 'hired'],
 *         data: [
 *             {firstname:"Michael", lastname:"Scott", seniority:7, dep:"Management", hired:"01/10/2004"},
 *             {firstname:"Dwight", lastname:"Schrute", seniority:2, dep:"Sales", hired:"04/01/2004"},
 *             {firstname:"Jim", lastname:"Halpert", seniority:3, dep:"Sales", hired:"02/22/2006"},
 *             {firstname:"Kevin", lastname:"Malone", seniority:4, dep:"Accounting", hired:"06/10/2007"},
 *             {firstname:"Angela", lastname:"Martin", seniority:5, dep:"Accounting", hired:"10/21/2008"}
 *         ]
 *     });
 *
 *     var grid = Ext.create('Ext.grid.Grid', {
 *         title: 'Column Demo',
 *         store: Ext.data.StoreManager.lookup('employeeStore'),
 *         columns: [
 *             {text: 'First Name',  dataIndex:'firstname'},
 *             {text: 'Last Name',  dataIndex:'lastname'},
 *             {text: 'Hired Month',  dataIndex:'hired', xtype:'datecolumn', format:'M'},
 *             {text: 'Department (Yrs)', xtype:'templatecolumn', tpl:'{dep} ({seniority})'}
 *         ],
 *         width: 400
 *     });
 *     Ext.ViewPort.add(grid);
 *
 * # Convenience Subclasses
 *
 * There are several column subclasses that provide default rendering for various data types
 *
 *  - {@link Ext.grid.column.Boolean}: Renders for boolean values
 *  - {@link Ext.grid.column.Date}: Renders for date values
 *  - {@link Ext.grid.column.Number}: Renders for numeric values
 *  - {@link Ext.grid.column.Template}: Renders a value using an {@link Ext.XTemplate} using the record data
 *
 * # Setting Sizes
 *
 * The columns can be only be given an explicit width value. If no width is specified the grid will
 * automatically the size the column to 20px.
 *
 * # Header Options
 *
 *  - {@link #text}: Sets the header text for the column
 *  - {@link #sortable}: Specifies whether the column can be sorted by clicking the header or using the column menu
 *
 * # Data Options
 *
 *  - {@link #dataIndex}: The dataIndex is the field in the underlying {@link Ext.data.Store} to use as the value for the column.
 *  - {@link #renderer}: Allows the underlying store value to be transformed before being displayed in the grid
 */
Ext.define('Ext.grid.column.Column', {
    extend: 'Ext.Component',

    xtype: 'column',

    config: {
        /**
         * @cfg {String} dataIndex
         * The name of the field in the grid's {@link Ext.data.Store}'s {@link Ext.data.Model} definition from
         * which to draw the column's value. **Required.**
         */
        dataIndex: null,

        /**
         * @cfg {String} text
         * The header text to be used as innerHTML (html tags are accepted) to display in the Grid.
         * **Note**: to have a clickable header with no text displayed you can use the default of ` ` aka ` `.
         */
        text: ' ',

        /**
         * @cfg {Boolean} sortable
         * False to disable sorting of this column. Whether local/remote sorting is used is specified in
         * `{@link Ext.data.Store#remoteSort}`.
         */
        sortable: true,

        /**
         * @cfg {Boolean} resizable
         * False to prevent the column from being resizable.
         * Note that this configuration only works when the {@link Ext.grid.plugin.ColumnResizing ColumnResizing} plugin
         * is enabled on the {@link Ext.grid.Grid Grid}.
         */
        resizable: true,

        /**
         * @cfg {Boolean} hideable
         * False to prevent the user from hiding this column.
         * TODO: Not implemented yet
         * @private
         */
        hideable: true,

        /**
         * @cfg {Function/String} renderer
         * A renderer is an 'interceptor' method which can be used to transform data (value, appearance, etc.)
         * before it is rendered. Example:
         *
         *     {
         *         renderer: function(value, record){
         *             if (value === 1) {
         *                 return '1 person';
         *             }
         *             return value + ' people';
         *         }
         *     }
         *
         * @cfg {Object} renderer.value The data value for the current cell
         * @cfg {Ext.data.Model} renderer.record The record for the current row
         * @cfg {Number} renderer.rowIndex The index of the current row
         * @cfg {String} renderer.return The HTML string to be rendered.
         */
        renderer: false,

        /**
         * @cfg {Object} scope
         * The scope to use when calling the {@link #renderer} function.
         */
        scope: null,

        /**
         * @cfg {String} align
         * Sets the alignment of the header and rendered columns.
         * Possible values are: `'left'`, `'center'`, and `'right'`.
         */
        align: 'left',

        /**
         * @cfg {Boolean} editable
         * Set this to true to make this column editable.
         * Only applicable if the grid is using an {@link Ext.grid.plugin.Editable Editable} plugin.
         * @type {Boolean}
         */
        editable: false,

        /**
         * @cfg {Object/String} editor
         * An optional xtype or config object for a {@link Ext.field.Field Field} to use for editing.
         * Only applicable if the grid is using an {@link Ext.grid.plugin.Editable Editable} plugin.
         * Note also that {@link #editable} has to be set to true if you want to make this column editable.
         * If this configuration is not set, and {@link #editable} is set to true, the {@link #defaultEditor} is used.
         */
        editor: null,

        /**
         * @cfg {Object/Ext.field.Field}
         * An optional config object that should not really be modified. This is used to create
         * a default editor used by the {@link Ext.grid.plugin.Editable Editable} plugin when no
         * {@link #editor} is specified.
         * @type {Object}
         */
        defaultEditor: {
            xtype: 'textfield',
            required: true
        },

        /**
         * @cfg {Boolean} ignore
         * This configuration should be left alone in most cases. This is used to prevent certain columns
         * (like the MultiSelection plugin column) to show up in plugins (like the {@link Ext.grid.plugin.ViewOptions} plugin).
         */
        ignore: false,

        /**
         * @cfg {String} summaryType
         * This configuration specifies the type of summary. There are several built in summary types.
         * These call underlying methods on the store:
         *
         *  - {@link Ext.data.Store#count count}
         *  - {@link Ext.data.Store#sum sum}
         *  - {@link Ext.data.Store#min min}
         *  - {@link Ext.data.Store#max max}
         *  - {@link Ext.data.Store#average average}
         *
         * Note that this configuration only works when the grid has the {@link Ext.grid.plugin.SummaryRow SummaryRow}
         * plugin enabled.
         */
        summaryType: null,

        /**
         * @cfg {Function} summaryRenderer
         * This summaryRenderer is called before displaying a value in the SummaryRow. The function is optional,
         * if not specified the default calculated value is shown. The summaryRenderer is called with:
         *  - value {Object} - The calculated value.
         *
         * Note that this configuration only works when the grid has the {@link Ext.grid.plugin.SummaryRow SummaryRow}
         * plugin enabled.
         */
        summaryRenderer: null,

        minWidth: 20,
        baseCls: Ext.baseCSSPrefix + 'grid-column',
        cellCls: null,
        sortedCls: Ext.baseCSSPrefix + 'column-sorted',
        sortDirection: null
    },

    updateAlign: function(align, oldAlign) {
        if (oldAlign) {
            this.removeCls(Ext.baseCSSPrefix + 'grid-column-align-' + align);
        }
        if (align) {
            this.addCls(Ext.baseCSSPrefix + 'grid-column-align-' + align);
        }
    },

    initialize: function() {
        this.callParent();

        this.element.on({
            tap: 'onColumnTap',
            longpress: 'onColumnLongPress',
            scope: this
        });
    },

    onColumnTap: function(e) {
        this.fireEvent('tap', this, e);
    },

    onColumnLongPress: function(e) {
        this.fireEvent('longpress', this, e);
    },

    updateText: function(text) {
        this.setHtml(text);
    },

    doSetWidth: function(width) {
        this.callParent(arguments);
        this.fireEvent('columnresize', this, width);
    },

    updateDataIndex: function(dataIndex) {
        var editor = this.getEditor();
        if (editor) {
            editor.name = dataIndex;
        } else {
            this.getDefaultEditor().name = dataIndex;
        }
    },

    updateSortDirection: function(direction, oldDirection) {
        if (!this.getSortable()) {
            return;
        }

        var sortedCls = this.getSortedCls();

        if (oldDirection) {
            this.element.removeCls(sortedCls + '-' + oldDirection.toLowerCase());
        }

        if (direction) {
            this.element.addCls(sortedCls + '-' + direction.toLowerCase());
        }

        this.fireEvent('sort', this, direction, oldDirection);
    },

    getCellContent: function(record) {
        var me = this,
            dataIndex = me.getDataIndex(),
            renderer = me.getRenderer(),
            scope = me.getScope(),
            value = dataIndex && record.get(dataIndex);

        return renderer ? renderer.call(scope || me, value, record, dataIndex) : me.defaultRenderer(value, record);
    },

    /**
     * @method defaultRenderer
     * When defined this will take precedence over the {@link Ext.grid.column.Column#renderer renderer} config.
     * This is meant to be defined in subclasses that wish to supply their own renderer.
     * @protected
     * @template
     */
    defaultRenderer: function(value) {
        return value;
    },

    updateCell: function(cell, record, content) {
        if (cell && (record || content)) {
            cell.firstChild.nodeValue = content || this.getCellContent(record);
        }
    }
});