/**
 * This class is a generic implementation of a Style. This should be extended to provide Style
 * implementations for different use cases. Check out {@link Ext.exporter.file.excel.Style} and
 * {@link Ext.exporter.file.html.Style}.
 */
Ext.define('Ext.exporter.file.Style', {
    extend: 'Ext.exporter.file.Base',

    config: {
        /**
         * @cfg {String} id
         * A unique name within the document that identifies this style.
         *
         */

        /**
         * @cfg {String} [name]
         *
         * This property identifies this style as a named style.
         *
         */
        name: null,

        /**
         * @cfg {Object} [alignment]
         *
         * Following keys are allowed on this object and are all optional:
         *
         * @cfg {String} alignment.horizontal
         * Specifies the left-to-right alignment of text within a cell. Possible values: `Left`,
         * `Center`, `Right`, `Justify` and `Automatic`.
         *
         * @cfg {Number} alignment.indent
         * Specifies the number of indents.
         *
         * @cfg {String} alignment.readingOrder
         * Specifies the default right-to-left text entry mode for a cell. Possible values:
         * `LeftToRight`, `RightToLeft` and `Context`.
         *
         * @cfg {Number} alignment.rotate
         * Specifies the rotation of the text within the cell.
         *
         * @cfg {String} alignment.vertical
         * Specifies the top-to-bottom alignment of text within a cell. Possible values: `Top`,
         * `Bottom`, `Center` and `Automatic`.
         *
         */
        alignment: null,

        /**
         * @cfg {Object} [font]
         * Defines the font attributes to use in this style.
         *
         *
         * Following keys are allowed on this object:
         *
         * @cfg {Boolean} font.bold
         * Specifies the bold state of the font.
         *
         * @cfg {String} font.color
         * Specifies the color of the font. This value should be a 6-hexadecimal digit number in
         * "#rrggbb" format.
         *
         * @cfg {String} font.fontName
         * Specifies the name of the font.
         *
         * @cfg {Boolean} font.italic
         * Similar to `font.bold` in behavior, this attribute specifies the italic state of the
         * font.
         *
         * @cfg {Number} font.size
         * Specifies the size of the font.
         *
         * @cfg {Boolean} font.strikeThrough
         * Similar to `font.bold` in behavior, this attribute specifies the strike-through state
         * of the font.
         *
         * @cfg {String} font.underline
         * Specifies the underline state of the font. Possible values: `None` and `Single`.
         *
         * @cfg {String} font.family
         * Font family name.
         *
         */
        font: null,

        /**
         * @cfg {Object} [interior]
         * Defines the fill properties to use in this style. Each attribute that is specified is
         * considered an override from the default.
         *
         * Following keys are allowed on this object:
         *
         * @cfg {String} interior.color
         * Specifies the fill color of the cell. This value should be a 6-hexadecimal digit number
         * in "#rrggbb" format.
         *
         * @cfg {String} interior.pattern
         * Specifies the fill pattern in the cell. Possible values: `None`, `Solid`.
         *
         */
        interior: null,

        /**
         * @cfg {String} [format]
         *
         * This can be one of the following values:
         * `General`, `General Number`, `General Date`, `Long Date`, `Medium Date`, `Short Date`,
         * `Long Time`, `Medium Time`, `Short Time`, `Currency`, `Euro Currency`, `Fixed`,
         * `Standard`, `Percent`, `Scientific`, `Yes/No`, `True/False`, or `On/Off`.
         *
         * `Currency` is the currency format with two decimal places.
         *
         * `Euro Currency` is the same as `Currency` using the Euro currency symbol instead.
         *
         */
        format: null,

        /**
         * @cfg {Object[]} [borders]
         *
         * Array of border objects. Following keys are allowed for border objects:
         *
         * @cfg {String} borders.position
         * Specifies which of the possible borders this element represents. Duplicate
         * borders are not permitted and are considered invalid. Possible values: `Left`, `Top`,
         * `Right`, `Bottom`.
         *
         * @cfg {String} borders.color
         * Specifies the color of this border. This value should be a 6-hexadecimal digit number
         * in "#rrggbb" format.
         *
         * @cfg {String} borders.lineStyle
         * Specifies the appearance of this border. Possible values: `None`, `Continuous`, `Dash`
         * and `Dot`.
         *
         * @cfg {Number} borders.weight
         * Specifies the weight (or thickness) of this border.
         *
         */
        borders: null,

        // used to validate the provided values for Style configs
        checks: {
            alignment: {
                horizontal: ['Automatic', 'Left', 'Center', 'Right', 'Justify'],
                readingOrder: ['LeftToRight', 'RightToLeft', 'Context'],
                vertical: ['Automatic', 'Top', 'Bottom', 'Center']
            },
            font: {
                bold: [true, false],
                italic: [true, false],
                strikeThrough: [true, false],
                underline: ['None', 'Single']
            },
            border: {
                position: ['Left', 'Top', 'Right', 'Bottom'],
                lineStyle: ['None', 'Continuous', 'Dash', 'Dot']
            },
            interior: {
                pattern: ['None', 'Solid']
            }
        }

    },

    datePatterns: {
        'General Date': 'Y-m-d H:i:s',
        'Long Date': 'l, F d, Y',
        'Medium Date': 'Y-m-d',
        'Short Date': 'n/j/Y',
        'Long Time': 'g:i:s A',
        'Medium Time': 'H:i:s',
        'Short Time': 'g:i A'
    },
    numberPatterns: {
        'General Number': '0',
        'Fixed': '0.00',
        'Standard': '0.00'
    },
    booleanPatterns: {
        'Yes/No': ['Yes', 'No'],
        'True/False': ['True', 'False'],
        'On/Off': ['On', 'Off']
    },

    isStyle: true,

    autoGenerateKey: ['alignment', 'font', 'interior', 'format', 'borders'],

    constructor: function(config) {
        this.callParent([this.uncapitalizeKeys(config)]);
    },

    /**
     * Parse object keys and uncapitalize them. This is useful to keep compatibility
     * with prior versions.
     *
     * @param config
     * @return {Object}
     *
     * @private
     */
    uncapitalizeKeys: function(config) {
        var ret = config,
            keys, len, i, key;

        if (Ext.isObject(config)) {
            ret = {};
            keys = Ext.Object.getAllKeys(config);
            len = keys.length;

            for (i = 0; i < len; i++) {
                key = keys[i];
                ret[Ext.String.uncapitalize(key)] = this.uncapitalizeKeys(config[key]);
            }
        }
        else if (Ext.isArray(config)) {
            ret = [];
            len = config.length;

            for (i = 0; i < len; i++) {
                ret.push(this.uncapitalizeKeys(config[i]));
            }
        }

        return ret;
    },

    destroy: function() {
        var me = this;

        me.setAlignment(null);
        me.setFont(null);
        me.setInterior(null);
        me.setBorders(null);
        me.setChecks(null);

        me.callParent();
    },

    updateAlignment: function(data) {
        this.checkAttribute(data, 'alignment');
    },

    updateFont: function(data) {
        this.checkAttribute(data, 'font');
    },

    updateInterior: function(data) {
        this.checkAttribute(data, 'interior');
    },

    applyBorders: function(borders, oldBolders) {
        if (!borders) {
            return borders;
        }

        borders = Ext.Array.from(borders);

        //<debug>
        if (Ext.Array.unique(Ext.Array.pluck(borders, 'position')).length !== borders.length) {
            Ext.raise('Invalid border positions supplied');
        }
        //</debug>

        return borders;
    },

    updateBorders: function(data) {
        this.checkAttribute(data, 'border');
    },

    checkAttribute: function(data, checkName) {
        var checks = this.getChecks(),
            values, keys, len, i, j, arr, key, obj, lenV, valid;

        if (!data || !checks || !checks[checkName]) {
            return;
        }

        values = Ext.Array.from(data);
        lenV = values.length;

        for (i = 0; i < lenV; i++) {
            obj = values[i];
            keys = Ext.Object.getKeys(obj || {});
            len = keys.length;

            for (j = 0; j < len; j++) {
                key = keys[j];
                arr = checks[checkName][key];

                if (arr && obj[key]) {
                    valid = Ext.isArray(arr)
                        ? Ext.Array.indexOf(arr, obj[key]) > -1
                        : arr === obj[key];

                    if (!valid) {
                        delete(obj[key]);

                        //<debug>
                        Ext.raise(Ext.String.format(
                            'Invalid key (%0) or value (%1) provided for Style!', key, obj[key]
                        ));
                        //</debug>
                    }
                }
            }
        }
    },

    /**
     * Returns the specified value formatted according to the format of this style.
     * @param v
     */
    getFormattedValue: function(v) {
        var me = this,
            f = me.getFormat(),
            ret = v,
            fmt = Ext.util.Format;

        if (!f || f === 'General' || Ext.isEmpty(v)) {
            return ret;
        }

        if (f === 'Currency') {
            return fmt.currency(v);
        }
        else if (f === 'Euro Currency') {
            return fmt.currency(v, '€');
        }
        else if (f === 'Percent') {
            return fmt.number(v * 100, '0.00') + '%';
        }
        else if (f === 'Scientific') {
            return Number(v).toExponential();
        }
        else if (me.datePatterns[f]) {
            return fmt.date(v, me.datePatterns[f]);
        }
        else if (me.numberPatterns[f]) {
            return fmt.number(v, me.numberPatterns[f]);
        }
        else if (me.booleanPatterns[f]) {
            return v ? me.booleanPatterns[f][0] : me.booleanPatterns[f][1];
        }
        else if (Ext.isFunction(f)) {
            return f(v);
        }

        return fmt.number(v, f);
    }
});
