/**
 * A data field that deals with array values. See the {@link #itemType} config for controlling
 * the array elements.
 *
 *     @example
 *     Ext.define('Task', {
 *         extend: 'Ext.data.Model',
 *         fields: [{
 *             name: 'milestoneDates',
 *             type: 'array',
 *             itemType: {
 *                 type: 'date',
 *                 dateFormat: 'Y-m-d'
 *             }
 *         }]
 *     });
 *
 *     var record = Ext.create('Task', { milestoneDates: [
 *         '2010-01-01',
 *         '2010-02-01',
 *         '2010-03-01'
 *     ]}), value = record.get('milestoneDates');
 *
 *     Ext.toast(value.join(', '));
 *
 */
Ext.define('Ext.data.field.Array', {
    extend: 'Ext.data.field.Field',

    alias: 'data.field.array',

    isArrayField: true,

    /**
     * @cfg {String/Object/Ext.data.field.Field} itemType
     *
     * Controls the individual elements of the array. This is a field type or configuration
     * that is used to {@link #collate}, {@link #compare}, {@link #isEqual compare equality},
     * {@link #convert} and {@link #deserialize} each element.
     */
    itemType: {
        type: 'auto'
    },

    getType: function() {
        return 'array';
    },

    collate: function(v1, v2) {
        return this.collateOrCompare(v1, v2, 'collate');
    },

    compare: function(v1, v2) {
        return this.collateOrCompare(v1, v2, 'compare');
    },

    convert: function(v) {
        return this.doForEach((v || v === 0) && !Ext.isArray(v) ? [v] : v, 'convert');
    },

    serialize: function(value) {
        return this.doForEach(value, 'serialize');
    },

    /**
     * @private
     */
    getItemField: function() {
        var me = this,
            t = me.itemType;

        if (!me.itemField) {
            if (typeof t === 'string') {
                t = {
                    type: t
                };
            }
            else if (!(t && t.isDataField)) {
                t = Ext.apply({
                    type: 'auto'
                }, t);
            }

            me.itemField = Ext.data.field.Field.create(t);
        }

        return me.itemField;
    },

    /**
     * @private
     */
    doForEach: function(v, fn) {
        if (!v) {
            return this.allowNull ? null : [];
        }

        // eslint-disable-next-line vars-on-top
        var len = v.length,
            ret = Array(len),
            field = this.getItemField(),
            i;

        for (i = 0; i < len; ++i) {
            ret[i] = field[fn] ? field[fn](v[i]) : v[i];
        }

        return ret;
    },

    /**
     * @private
     */
    collateOrCompare: function(v1, v2, fn) {
        var field = this.getItemField(),
            i, len1, len2, len, result;

        if ((!v1 && !v2) || (v1 === v2)) {
            return 0;
        }

        if (!v1 || !v2) {
            return v1 ? 1 : -1;
        }

        len1 = v1.length;
        len2 = v2.length;

        len = Math.min(len1, len2);

        for (i = 0; i < len; ++i) {
            result = field[fn](v1[i], v2[i]);

            if (result !== 0) {
                return result;
            }
        }

        return len1 === len2 ? 0 : (len1 < len2 ? -1 : 1);
    }
});