/**
 * @private
 */
Ext.define('Ext.exporter.file.ooxml.excel.Cell', {
    extend: 'Ext.exporter.file.Base',

    isCell: true,

    config: {
        /**
         * @cfg {Ext.exporter.file.ooxml.excel.Row} row
         *
         * Reference to the parent row
         */
        row: null,

        /**
         * @cfg {String} dataType (required)
         *
         * The cell's data type. Possible values:
         *
         * - `b` (Boolean) Cell containing a boolean.
         * - `d` (Date) Cell contains a date in the ISO 8601 format.
         * - `e` (Error) Cell containing an error.
         * - `inlineStr` (InlineString) Cell containing an (inline) rich string, i.e., one not in
         * the shared string table. If this cell type is used, then the cell value is in the `is`
         * element rather than the `v` element in the cell (`c` element).
         * - `n` (Number) Cell containing a number.
         * - `s` (SharedString) Cell containing a shared string.
         * - `str` (String) Cell containing a formula string.
         */
        dataType: null,

        /**
         * @cfg {Boolean} [showPhonetic]
         *
         * `true` if the cell should show phonetic.
         */
        showPhonetic: null,

        /**
         * @cfg {Number} index
         *
         * Specifies the column index of this cell within the containing row. If this tag is not
         * specified, the first instance of a Cell element within a row has an assumed Index="1".
         *
         */
        index: null,

        /**
         * @cfg {String} styleId
         *
         * The index of this cell's style. Style records are stored in the Styles Part.
         */
        styleId: null,

        /**
         * @cfg {Number} mergeAcross
         *
         * Number of cells to merge to the right side of this cell
         */
        mergeAcross: null,

        /**
         * @cfg {Number} mergeDown
         *
         * Number of cells to merge below this cell
         */
        mergeDown: null,

        /**
         * @cfg {Number/Date/String} value (required)
         *
         * Value assigned to this cell
         */
        value: null,

        /**
         * @cfg {Boolean} serializeDateToNumber
         *
         * Set to `true` to serialize Date values to numbers like the function DATEVALUE from Excel
         * does.
         *
         * Google docs can only handle Dates serialized as numbers.
         */
        serializeDateToNumber: true
    },

    isMergedCell: false,

    tpl: [
        '<c r="{ref}"',
        '<tpl if="value != null"> t="{dataType}"</tpl>',
        '<tpl if="showPhonetic"> ph="1"</tpl>',
        '<tpl if="styleId"> s="{styleId}"</tpl>',
        '<tpl if="value == null">/><tpl else>><v>{value}</v></c></tpl>'
    ],

    constructor: function(config) {
        var cfg = config;

        if (config == null || Ext.isDate(config) || Ext.isPrimitive(config)) {
            cfg = {
                value: config
            };
        }

        return this.callParent([cfg]);
    },

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

    /**
     * Returns the cell reference using the A4 notation
     * @return {String}
     */
    getRef: function() {
        return this.getNotation(this._index) + this._row._index;
    },

    getRenderData: function() {
        var me = this,
            data = {},
            ws = me._row && me._row._worksheet,
            wb = ws && ws._workbook;

        data.dataType = me._dataType;
        data.value = me._value;
        data.showPhonetic = me._showPhonetic;
        data.styleId = me._styleId;

        if (this.isMergedCell && ws) {
            ws.setMergedCellsNo(ws._mergedCellsNo + 1);
        }

        if (data.dataType === 's' && wb) {
            data.value = wb._sharedStrings.addString(data.value);
        }

        data.ref = this.getRef();

        return data;
    },

    applyValue: function(v) {
        var me = this,
            dt;

        if (v != null) {
            if (typeof v === 'number') {
                dt = 'n';
            }
            else if (typeof v === 'string') {
                dt = 's';
                v = Ext.util.Format.stripTags(v);
            }
            else if (v instanceof Date) {
                // there are 2 ways to store a date:
                if (me.getSerializeDateToNumber()) {
                    // 1: using dataType "n" for number
                    dt = 'n';
                    v = me.dateValue(v);
                }
                else {
                    // 2: using dataType "d" for Date
                    dt = 'd';
                    v = Ext.Date.format(v, 'Y-m-d\\TH:i:s.u');
                }
            }
            else {
                dt = 'b';
            }

            me.setDataType(dt);
        }

        return v;
    },

    updateMergeAcross: function(v) {
        this.isMergedCell = (v || this._mergeDown);
    },

    updateMergeDown: function(v) {
        this.isMergedCell = (v || this._mergeAcross);
    },

    getMergedCellRef: function() {
        var me = this,
            currIndex = me._index,
            rowIndex = me._row._index,
            mAcross = me._mergeAcross,
            mDown = me._mergeDown,
            s = me.getNotation(currIndex) + rowIndex + ':';

        if (mAcross) {
            currIndex += mAcross;
        }

        if (mDown) {
            rowIndex += mDown;
        }

        s += me.getNotation(currIndex) + rowIndex;

        return s;
    },

    /**
     * Formats a number to the A1 style
     *
     * @param index
     * @return {string}
     */
    getNotation: function(index) {
        var code = 65,
            length = 26,
            getChar = String.fromCharCode,
            r, n;

        if (index <= 0) {
            index = 1;
        }

        n = Math.floor(index / length);
        r = index % length;

        if (n === 0 || index === length) {
            return getChar(code + index - 1);
        }
        else if (r === 0) {
            return this.getNotation(n - 1) + 'Z';
        }
        else if (n < length) {
            return getChar(code + n - 1) + getChar(code + r - 1);
        }
        else {
            return this.getNotation(n) + getChar(code + r - 1);
        }
    },

    /**
     * Excel stores dates as sequential serial numbers so that they can be used in calculations.
     * By default, January 1, 1900, is serial number 1, and January 1, 2008, is serial number
     * 39448 because it is 39,448 days after January 1, 1900.
     *
     * The DATEVALUE function in Excel returns the serial number of the date that is represented
     * by the text date.
     *
     * This function converts a javascript date to a serial number.
     *
     * @param {Date} d
     * @private
     */
    dateValue: function(d) {
        // 25569 is the serial number for 1/1/1970
        // eslint-disable-next-line max-len
        return 25569.0 + ((d.getTime() - (d.getTimezoneOffset() * 60 * 1000)) / (1000 * 60 * 60 * 24));
    }
});
