/**
 * Validates that the value is a valid number.
 *
 * A valid number may include a leading + or -, comma separators, and a single decimal point.
 */
Ext.define('Ext.data.validator.Number', {
    extend: 'Ext.data.validator.Validator',
    alias: 'data.validator.number',

    type: 'number',

    config: {
        /**
         * @cfg {String} decimalSeparator
         * The decimal separator. Defaults to {@link Ext.util.Format#decimalSeparator}.
         */
        decimalSeparator: undefined,

        /**
         * @cfg {String} message
         * The error message to return when the value is not a valid number.
         * @locale
         */
        message: 'Is not a valid number',

        /**
         * @cfg {String} thousandSeparator
         * The thousand separator. Defaults to {@link Ext.util.Format#thousandSeparator}.
         */
        thousandSeparator: undefined
    },

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

    applyDecimalSeparator: function(v) {
        return v === undefined ? Ext.util.Format.decimalSeparator : v;
    },

    updateDecimalSeparator: function() {
        this.rebuildMatcher();
    },

    applyThousandSeparator: function(v) {
        return v === undefined ? Ext.util.Format.thousandSeparator : v;
    },

    updateThousandSeparator: function() {
        this.rebuildMatcher();
    },

    parse: function(v) {
        var sep = this.getDecimalSeparator(),
            N = Ext.Number;

        if (typeof v === 'string') {
            if (!this.matcher.test(v)) {
                return null;
            }

            v = this.parseValue(v);
        }

        return sep ? N.parseFloat(v) : N.parseInt(v);
    },

    validate: function(value) {
        return this.parse(value) === null ? this.getMessage() : true;
    },

    privates: {
        getMatcherText: function(preventSign) {
            var t = this.getThousandSeparator(),
                d = this.getDecimalSeparator(),
                s = '(?:';

            if (t) {
                t = Ext.String.escapeRegex(t);
                s += '(?:\\d{1,3}(' + t + '\\d{3})*)|';
            }

            s += '\\d*)';

            if (d) {
                d = Ext.String.escapeRegex(d);
                s += '(?:' + d + '\\d*)?';
            }

            if (!preventSign) {
                s = this.getSignPart() + s;
            }

            return s;
        },

        getSignPart: function() {
            return '(\\+|\\-)?';
        },

        parseValue: function(v) {
            var thousandMatcher = this.thousandMatcher,
                decimal;

            if (thousandMatcher) {
                v = v.replace(thousandMatcher, '');
            }

            decimal = this.getDecimalSeparator();

            if (decimal && decimal !== '.') {
                v = v.replace(decimal, '.');
            }

            return v;
        },

        rebuildMatcher: function() {
            var me = this,
                sep;

            if (!me.isConfiguring) {
                sep = me.getThousandSeparator();
                me.matcher = new RegExp('^' + me.getMatcherText() + '$');

                if (sep) {
                    me.thousandMatcher = sep ? new RegExp(Ext.String.escapeRegex(sep), 'g') : null;
                }
            }
        }
    }
});