YUI.add('aui-form-builder-field-types', function (A, NAME) {

/**
 * The Form Builder Field Types Component
 *
 * @module aui-form-builder
 * @submodule aui-form-builder-field-types
 */

/**
 * `A.FormBuilder` extension, which is responsible for all the logic related
 * to field types.
 *
 * @class A.FormBuilderFieldTypes
 * @param {Object} config Object literal specifying layout builder configuration
 *     properties.
 * @constructor
 */
A.FormBuilderFieldTypes = function() {};

A.FormBuilderFieldTypes.prototype = {
    /**
     * Construction logic executed during the `A.FormBuilderFieldTypes`
     * instantiation. Lifecycle.
     *
     * @method initializer
     * @protected
     */
    initializer: function() {
        this.after('fieldTypesChange', this._afterFieldTypesChange);
        this.after('form-builder-field-types-modal:selectFieldType', this._afterSelectFieldType);
    },

    /**
     * Destructor lifecycle implementation for the `A.FormBuilderFieldTypes` class.
     * Lifecycle.
     *
     * @method destructor
     * @protected
     */
    destructor: function() {
        A.Array.each(this.get('fieldTypes'), function(field) {
            field.destroy();
        });

        this.get('fieldTypesModal').destroy();
    },

    /**
     * Disables unique fields for the field class that the given field is an
     * instance of.
     *
     * @method disableUniqueFieldType
     * @param {A.FormField} field
     */
    disableUniqueFieldType: function(field) {
        var fieldType = this.findTypeOfField(field);

        if (fieldType.get('unique')) {
            fieldType.set('disabled', true);
        }
    },

    /**
     * Finds the type of the given field.
     *
     * @method findTypeOfField
     * @param {A.FormBuilderFieldBase} field
     */
    findTypeOfField: function(field) {
        var fieldTypes = this.get('fieldTypes'),
            i;

        for (i = 0; i < fieldTypes.length; i++) {
            if (field.constructor === fieldTypes[i].get('fieldClass')) {
                return fieldTypes[i];
            }
        }
    },

    /**
     * Hides the fields modal.
     *
     * @method hideFieldsPanel
     */
    hideFieldsPanel: function() {
        var fieldTypesModal = this.get('fieldTypesModal');

        fieldTypesModal.hide();
    },

    /**
     * Adds a the given field types to this form builder.
     *
     * @method registerFieldTypes
     * @param {Array | Object | A.FormBuilderFieldType} typesToAdd This can be
     *   either an array of items or a single item. Each item should be either
     *   an instance of `A.FormBuilderFieldType`, or the configuration object
     *   to be used when instantiating one.
     */
    registerFieldTypes: function(typesToAdd) {
        var fieldTypes = this.get('fieldTypes');

        typesToAdd = A.Lang.isArray(typesToAdd) ? typesToAdd : [typesToAdd];

        A.Array.each(typesToAdd, function(type) {
            fieldTypes.push(type);
        });

        this.set('fieldTypes', fieldTypes);
    },

    /**
     * Shows the fields modal.
     *
     * @method showFieldsPanel
     */
    showFieldsPanel: function() {
        var fieldTypesModal = this.get('fieldTypesModal');

        if (!fieldTypesModal.get('rendered')) {
            fieldTypesModal.render();
        }

        fieldTypesModal.show();
    },

    /**
     * Removes the given field types from this form builder.
     *
     * @method unregisterFieldTypes
     * @param {Array | String | A.FormBuilderFieldType} typesToRemove This can be
     *   either an array of items, or a single one. For each item, if it's a
     *   string, the form builder will remove all registered field types with
     *   a field class that matches it. For items that are instances of
     *   `A.FormBuilderFieldType`, only the same instances will be removed.
     */
    unregisterFieldTypes: function(typesToRemove) {
        var instance = this;

        typesToRemove = A.Lang.isArray(typesToRemove) ? typesToRemove : [typesToRemove];

        A.Array.each(typesToRemove, function(type) {
            instance._unregisterFieldType(type);
        });

        this.set('fieldTypes', this.get('fieldTypes'));
    },

    /**
     * Fired after the `fieldTypes` attribute is set.
     *
     * @method _afterFieldTypesChange
     * @param {EventFacade} event
     * @protected
     */
    _afterFieldTypesChange: function(event) {
        this.get('fieldTypesModal').set('fieldTypes', event.newVal);
    },

    /**
     * Fired after a field type is selected by the user.
     *
     * @method _afterSelectFieldType
     * @param {EventFacade} event
     * @protected
     */
    _afterSelectFieldType: function(event) {
        var field,
            fieldType = event.fieldType;

        if (!fieldType.get('disabled')) {
            field = new(fieldType.get('fieldClass'))(fieldType.get('defaultConfig'));
            this.showFieldSettingsPanel(field, fieldType.get('label'));
        }
    },

    /**
     * Check on all created fields if there is one of the same type
     * of the parameter.
     *
     * @method _checkActiveLayoutHasFieldType
     * @param {Object} fieldType
     * @return {Boolean}
     * @protected
     */
    _checkActiveLayoutHasFieldType: function(fieldType) {
        var col,
            cols,
            fieldList,
            row,
            rows = this.getActiveLayout().get('rows');

        for (row = 0; row < rows.length; row++) {
            cols = rows[row].get('cols');
            for (col = 0; col < cols.length; col++) {
                fieldList = cols[col].get('value');
                if (fieldList && this._checkListHasFieldType(fieldList, fieldType)) {
                    return true;
                }
            }
        }

        return false;
    },

    /**
     * Checks on all fields of a field list if there is one of the
     * same type of the parameter.
     *
     * @method _checkListHasFieldType
     * @param {A.FormBuilderFIeldList} fieldList
     * @param {Object} fieldType
     * @return {Boolean}
     * @protected
     */
    _checkListHasFieldType: function(fieldList, fieldType) {
        var fields = fieldList.get('fields'),
            i;

        for (i = 0; i < fields.length; i++) {
            if (this._hasFieldType(fieldType, fields[i])) {
                return true;
            }
        }

        return false;
    },

    /**
     * Checks if the given field is of the given field type.
     *
     * @method _hasFieldType
     * @param  {A.FormBuilderFieldType} fieldType
     * @param  {A.FormField} field
     * @return {Boolean}
     * @protected
     */
    _hasFieldType: function(fieldType, field) {
        var i,
            nestedFields = field.get('nestedFields');

        if (field.constructor === fieldType.get('fieldClass')) {
            return true;
        }

        for (i = 0; i < nestedFields.length; i++) {
            if (this._hasFieldType(fieldType, nestedFields[i])) {
                return true;
            }
        }

        return false;
    },

    /**
     * Sets the `fieldTypes` attribute.
     *
     * @method _setFieldTypes
     * @param {Object | A.FormBuilderFieldType} val
     * @return {A.FormBuilderFieldType}
     * @protected
     */
    _setFieldTypes: function(val) {
        for (var i = 0; i < val.length; i++) {
            if (!A.instanceOf(val[i], A.FormBuilderFieldType)) {
                val[i] = new A.FormBuilderFieldType(val[i]);
            }
        }

        return val;
    },

    /**
     * Removes a single given field type from this form builder.
     *
     * @method _unregisterFieldType
     * @param {String | A.FormBuilderFieldType} fieldType
     * @protected
     */
    _unregisterFieldType: function(fieldType) {
        var fieldTypes = this.get('fieldTypes'),
            i;

        if (A.Lang.isFunction(fieldType)) {
            for (i = fieldTypes.length - 1; i >= 0; i--) {
                if (fieldTypes[i].get('fieldClass') === fieldType) {
                    this._unregisterFieldTypeByIndex(i);
                }
            }
        }
        else {
            this._unregisterFieldTypeByIndex(fieldTypes.indexOf(fieldType));
        }
    },

    /**
     * Unregisters the field type at the given index.
     *
     * @method _unregisterFieldTypeByIndex
     * @param {Number} index
     * @protected
     */
    _unregisterFieldTypeByIndex: function(index) {
        var fieldTypes = this.get('fieldTypes');

        if (index !== -1) {
            fieldTypes[index].destroy();
            fieldTypes.splice(index, 1);
        }
    },

    /**
     * Enable or disable unique FieldTypes based on created Fields.
     *
     * @method _updateUniqueFieldType
     * @protected
     */
    _updateUniqueFieldType: function() {
        var instance = this;

        A.Array.each(instance.get('fieldTypes'), function(fieldType) {
            if (fieldType.get('unique')) {
                fieldType.set('disabled', instance._checkActiveLayoutHasFieldType(fieldType));
            }
        });
    },

    /**
     * Default value for the modal displayed to select a field.
     *
     * @method _valueFieldTypesModal
     * @return {A.FormBuilderFieldTypesModal}
     * @protected
     */
    _valueFieldTypesModal: function() {
        var fieldTypesModal = new A.FormBuilderFieldTypesModal({
            centered: true,
            cssClass: 'form-builder-modal',
            draggable: false,
            fieldTypes: this.get('fieldTypes'),
            modal: true,
            resizable: false,
            visible: false,
            zIndex: 4
        });

        fieldTypesModal.addTarget(this);

        return fieldTypesModal;
    }
};

/**
 * Static property used to define the default attribute
 * configuration for the `A.FormBuilderFieldTypes`.
 *
 * @property ATTRS
 * @type Object
 * @static
 */
A.FormBuilderFieldTypes.ATTRS = {
    /**
     * The collection of field types that can be selected as fields for
     * this form builder.
     *
     * @attribute fieldTypes
     * @default []
     * @type Array
     */
    fieldTypes: {
        setter: '_setFieldTypes',
        validator: A.Lang.isArray,
        value: []
    },

    /**
     * The modal that will be used to select a field type.
     *
     * @attribute fieldTypesModal
     * @type `A.FormBuilderFieldTypesModal`
     */
    fieldTypesModal: {
        valueFn: '_valueFieldTypesModal'
    }
};


}, '3.1.0-deprecated.112', {
    "requires": [
        "aui-classnamemanager",
        "aui-form-builder-field-types-modal",
        "base",
        "node-base"
    ],
    "skinnable": true
});
