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

/**
 * The Form Builder Layout Builder Component
 *
 * @module aui-form-builder
 * @submodule aui-form-builder-layout-builder
 */

var CSS_CHOOSE_COL_MOVE = A.getClassName('form', 'builder', 'choose', 'col', 'move'),
    CSS_CHOOSE_COL_MOVE_TARGET = A.getClassName('form', 'builder', 'choose', 'col', 'move', 'target'),
    CSS_FIELD = A.getClassName('form', 'builder', 'field'),
    CSS_FIELD_MOVE_BUTTON = A.getClassName('form', 'builder', 'field', 'move', 'button'),
    CSS_FIELD_MOVE_TARGET = A.getClassName('form', 'builder', 'field', 'move', 'target'),
    CSS_FIELD_MOVE_TARGET_INVALID = A.getClassName('form', 'builder', 'field', 'move', 'target', 'invalid'),
    CSS_FIELD_MOVING = A.getClassName('form', 'builder', 'field', 'moving'),
    CSS_LAYOUT = A.getClassName('form', 'builder', 'layout'),
    CSS_LAYOUT_BUILDER_MOVE_CANCEL = A.getClassName('layout', 'builder', 'move', 'cancel'),
    CSS_MOVE_COL_TARGET = A.getClassName('layout', 'builder', 'move', 'col', 'target'),
    CSS_MOVE_ROW_TARGET = A.getClassName('layout', 'builder', 'move', 'row', 'target'),
    CSS_MOVE_TARGET = A.getClassName('layout', 'builder', 'move', 'target'),
    CSS_REMOVE_ROW_MODAL = A.getClassName('form', 'builder', 'remove', 'row', 'modal');

/**
 * `A.FormBuilder` extension, which handles the `A.LayoutBuilder` inside it.
 *
 * @class A.FormBuilderLayoutBuilder
 * @param {Object} config Object literal specifying layout builder configuration
 *     properties.
 * @constructor
 */
A.FormBuilderLayoutBuilder = function() {};

A.FormBuilderLayoutBuilder.prototype = {

    /**
     * Construction logic executed during the `A.FormBuilderLayoutBuilder`
     * instantiation. Lifecycle.
     *
     * @method initializer
     * @protected
     */
    initializer: function() {
        this._initRemoveConfirmationModal();

        this.after({
            render: this._afterLayoutBuilderRender,
            'layout-row:colsChange': this._afterLayoutBuilderColsChange
        });
    },

    /**
     * Destructor implementation for the `A.FormBuilderLayoutBuilder` class.
     * Lifecycle.
     *
     * @method destructor
     * @protected
     */
    destructor: function() {
        if (this._layoutBuilder) {
            this._layoutBuilder.destroy();
        }

        this._removeConfirmationModal.destroy();
    },

    /**
     * Overrides default `addColMoveButton` attribute.
     *
     * @method _addColMoveButton
     * @param {Node} colNode
     * @param {Node} rowNode
     * @protected
     */
    _addColMoveButton: function(colNode, rowNode) {
        var targetNodes = colNode.all('.' + CSS_FIELD_MOVE_BUTTON);

        targetNodes.setData('node-col', colNode);
        targetNodes.setData('node-row', rowNode);

        this._renderEmptyColumns();
    },

    /**
     * Overrides default `addColMoveTarget` attribute.
     *
     * @method _addColMoveTarget
     * @param {A.LayoutCol} col
     * @protected
     */
    _addColMoveTarget: function(col) {
        var colNode = col.get('node'),
            targetNodes;

        colNode.addClass(CSS_CHOOSE_COL_MOVE_TARGET);

        targetNodes = colNode.all('.' + CSS_FIELD_MOVE_TARGET);
        targetNodes.setData('col', col);
    },

    /**
     * Executed after the `layout:rowsChange` is fired.
     *
     * @method _afterLayoutBuilderColsChange
     * @protected
     */
    _afterLayoutBuilderColsChange: function() {
        var activeLayout = this.getActiveLayout();

        this._checkLastRow(activeLayout);
    },

    /**
     * Executed after the `layout-builder:moveEnd` is fired.
     *
     * @method _afterLayoutBuilderMoveEnd
     * @protected
     */
    _afterLayoutBuilderMoveEnd: function() {
        this._detachCancelMoveRowEvents();
        this._enableAddFields();
        this._fieldToolbar.set('disabled', false);
        this._pageManager.enablePaginations();
    },

    /**
     * Executed after the `layout-builder:moveStart` is fired.
     *
     * @method _afterLayoutBuilderMoveStart
     * @param {EventFacade} event
     * @protected
     */
    _afterLayoutBuilderMoveStart: function(event) {
        if (event.moveElement instanceof A.LayoutRow) {
            this._bindMoveRowEvents();
        }

        this._disableAddFields();
        this._fieldToolbar.set('disabled', true);
        this._pageManager.disablePaginations();
    },

    /**
     * Fired after this widget is rendered.
     *
     * @method _afterLayoutBuilderRender
     * @protected
     */
    _afterLayoutBuilderRender: function() {
        var originalChooseColMoveTargetFn;

        this._layoutBuilder = new A.LayoutBuilder({
            addColMoveButton: A.bind(this._addColMoveButton, this),
            addColMoveTarget: A.bind(this._addColMoveTarget, this),
            clickColMoveTarget: A.bind(this._clickColMoveTarget, this),
            clickRemoveRow: A.bind(this._clickRemoveRow, this),
            container: this.get('contentBox').one('.' + CSS_LAYOUT),
            layout: this.getActiveLayout(),
            removeColMoveButtons: A.bind(this._removeColMoveButtons, this),
            removeColMoveTargets: A.bind(this._removeColMoveTargets, this),
            strings: this.get('strings')
        });

        this._layoutBuilder.after('layout-builder:moveStart', A.bind(this._afterLayoutBuilderMoveStart, this));
        this._layoutBuilder.after('layout-builder:moveEnd', A.bind(this._afterLayoutBuilderMoveEnd, this));
        
        originalChooseColMoveTargetFn = this._layoutBuilder.get('chooseColMoveTarget');
        this._layoutBuilder.set('chooseColMoveTarget', A.bind(this._chooseColMoveTarget, this,
            originalChooseColMoveTargetFn));

        this._eventHandles.push(
            this._fieldToolbar.on('onToolbarHasAddedToField', A.bind(this._onFormBuilderToolbarHasAddedToField, this))
        );

        this._removeLayoutCutColButtons();
    },

    /**
     * Bind events related to the cancel move row funcionality.
     *
     * @method _bindMoveRowEvents
     * @protected
     */
    _bindMoveRowEvents: function() {
        this._cancelMoveRowsHandles = [
            A.one(A.config.doc).on('click', A.bind(this._onClickOutsideMoveRowTarget, this))
        ];
    },

    /**
     * Checks if the last row has more than one col or if it has at least one field,
     * if true a new row is created and set on the last position.
     *
     * @method _checkLastRow
     * @param {A.Layout} layout
     * @protected
     */
    _checkLastRow: function(layout) {
        var cols,
            lastRow,
            nextToLast,
            rows;

        lastRow = this._getLastRow(layout);
        cols = lastRow.get('cols');

        if (cols.length > 1 || !this._isColumnEmpty(cols[0])) {
            this._createLastRow(layout);
        }
        else {
            rows = layout.get('rows');
            nextToLast = rows[rows.length - 2];

            if (nextToLast) {
                cols = nextToLast.get('cols');

                if (cols.length === 1 && this._isColumnEmpty(cols[0])) {
                    layout.removeRow(nextToLast);
                }
            }
        }

        this._getLastRow(layout).set('removable', false);
    },

    /**
     * Overrides default `chooseColMoveTarget` attribute.
     *
     * @method _chooseColMoveTarget
     * @param {Function} originalFn
     * @param {Node} cutButton
     * @param {A.LayoutCol} col
     * @protected
     */
    _chooseColMoveTarget: function(originalFn, cutButton, col) {
        var fieldNode = cutButton.ancestor('.' + CSS_FIELD),
            layout = this.getActiveLayout(),
            targetNode;

        this._fieldBeingMoved = fieldNode.getData('field-instance');
        this._fieldListBeingMoved = col.get('value');
        this._fieldBeingMovedCol = col;

        fieldNode.addClass(CSS_FIELD_MOVING);

        fieldNode.all('.' + CSS_FIELD_MOVE_TARGET).addClass(CSS_FIELD_MOVE_TARGET_INVALID);

        targetNode = fieldNode.previous('.' + CSS_FIELD_MOVE_TARGET);
        if (targetNode) {
            targetNode.addClass(CSS_FIELD_MOVE_TARGET_INVALID);
        }

        targetNode = fieldNode.next('.' + CSS_FIELD_MOVE_TARGET);
        if (targetNode) {
            targetNode.addClass(CSS_FIELD_MOVE_TARGET_INVALID);
        }

        originalFn(cutButton, col);
        this._addColMoveTarget(col);

        layout.normalizeColsHeight(layout.get('node').all('.row'));

        this._selectFirstValidMoveTarget();

        this._cancelMoveFieldHandles = [
            A.one(A.config.doc).on('click', A.bind(this._onClickOutsideMoveColTarget, this)),
            A.one(A.config.doc).on('key', A.bind(this._onEscKeyPressMoveTarget, this), 'down:27')
        ];
    },

    /**
     * Overrides default `clickColMoveTarget` attribute.
     *
     * @method _clickColMoveTarget
     * @param {Node} moveTarget
     * @protected
     */
    _clickColMoveTarget: function(moveTarget) {
        var layout = this.getActiveLayout(),
            parentFieldNode = this._fieldBeingMoved.get('content').ancestor('.' + CSS_FIELD),
            row,
            targetNestedParent = moveTarget.getData('nested-field-parent'),
            toolbarMoveIconCancelMode = this._fieldToolbar.getItem('.' + CSS_LAYOUT_BUILDER_MOVE_CANCEL);

        if (toolbarMoveIconCancelMode) {
            toolbarMoveIconCancelMode.removeClass(CSS_LAYOUT_BUILDER_MOVE_CANCEL);
        }

        if (parentFieldNode) {
            parentFieldNode.getData('field-instance').removeNestedField(this._fieldBeingMoved);
        }
        else {
            row = this.getFieldRow(this._fieldBeingMoved);
            this._fieldListBeingMoved.removeField(this._fieldBeingMoved);
        }

        this._enableAddFields();

        if (targetNestedParent) {
            this._addNestedField(
                targetNestedParent,
                this._fieldBeingMoved,
                moveTarget.getData('nested-field-index')
            );
        }
        else {
            moveTarget.getData('col').get('value').addField(
                this._fieldBeingMoved,
                moveTarget.getData('field-list-index')
            );
        }

        this._removeLayoutCutColButtons();

        layout.normalizeColsHeight(new A.NodeList(this.getFieldRow(this._fieldBeingMoved)));

        this._detachCancelMoveFieldEvents();
    },

    /**
     * Overrides default `clickRemoveRow` attribute. Check if the parameter `row` has fields.
     *
     * @method _clickRemoveRow
     * @param {A.LayoutRow} row
     * @return {Boolean}
     * @protected
     */
    _clickRemoveRow: function(row) {
        var cols = row.get('cols'),
            currentList,
            index;

        for (index = 0; index < cols.length; index++) {
            currentList = cols[index].get('value');

            if (currentList && currentList.get('fields').length) {
                this._removeConfirmationModal.show();
                this._removingRow = row;

                return false;
            }
        }

        return true;
    },

    /**
     * Creates a new row in the last position.
     *
     * @method _createLastRow
     * @param {A.Layout} layout
     * @protected
     */
    _createLastRow: function(layout) {
        var lastRow = new A.LayoutRow(),
            rows = layout.get('rows');

        layout.addRow(rows.length, lastRow);
    },

    /**
     * Detaches events related to the cancel move row funcionality.
     *
     * @method _detachCancelMoveRowEvents
     * @protected
     */
    _detachCancelMoveRowEvents: function() {
        new A.EventHandle(this._cancelMoveRowsHandles).detach();
    },

    /**
     * Detaches events related to the cancel move field funcionality.
     *
     * @method _detachCancelMoveFieldEvents
     * @protected
     */
    _detachCancelMoveFieldEvents: function() {
        new A.EventHandle(this._cancelMoveFieldHandles).detach();
    },

    /**
     * Disable add fields functionality.
     *
     * @method _disableAddFields
     * @protected
     */
    _disableAddFields: function() {
        this.getActiveLayout().get('rows').forEach(function(row) {
            row.get('cols').forEach(function(col) {
                col.get('value').set('enableAddFields', false);
            });
        });
    },

    /**
     * Enable add fields functionality.
     *
     * @method _enableAddFields
     * @protected
     */
    _enableAddFields: function() {
        this.getActiveLayout().get('rows').forEach(function(row) {
            row.get('cols').forEach(function(col) {
                col.get('value').set('enableAddFields', true);
            });
        });
    },

    /**
     * Removes a row even with field.
     *
     * @method _forceRemoveRow
     * @protected
     */
    _forceRemoveRow: function() {
        this.getActiveLayout().removeRow(this._removingRow);

        this._removeConfirmationModal.hide();
    },

    /**
     * Gets the last row.
     *
     * @method _getLastRow
     * @param {A.Layout} layout
     * @protected
     * @return {A.LayoutRow}
     */
    _getLastRow: function(layout) {
        var rows = layout.get('rows');

        return rows[rows.length - 1];
    },

    /**
     * Create a confirmation modal to be used when a remove row button from a row with
     * fields is clicked.
     *
     * @method _initRemoveConfirmationModal
     * @protected
     */
    _initRemoveConfirmationModal: function() {
        var modal = new A.Modal({
            bodyContent: this.get('strings').removeRowModal,
            centered: true,
            cssClass: CSS_REMOVE_ROW_MODAL,
            headerContent: this.get('strings').modalHeader,
            modal: true,
            resizable: false,
            visible: false,
            zIndex: 4
        }).render();

        modal.addToolbar([
            {
                cssClass: 'btn-primary',
                label: this.get('strings').confirmRemoveRow,
                on: {
                    click: A.bind(this._forceRemoveRow, this)
                },
                render: true
            },
            {
                label: this.get('strings').cancelRemoveRow,
                on: {
                    click: function() {
                        modal.hide();
                    }
                },
                render: true
            }
        ]);

        this._removeConfirmationModal = modal;
    },

    /**
     * Checks if the given column is empty.
     *
     * @method _isColumnEmpty
     * @param {A.LayoutCol} col
     * @return {Boolean}
     * @protected
     */
    _isColumnEmpty: function(col) {
        return !col.get('value') || !col.get('value').get('fields').length;
    },

    /**
     * Fires when click event is triggered.
     *
     * @method _onClickOutsideMoveColTarget
     * @param {EventFacade} event
     * @protected
     */
    _onClickOutsideMoveColTarget: function(event) {
        var targetNode = event.target,
        toolbarMoveIconCancelMode = this._fieldToolbar.getItem('.' + CSS_LAYOUT_BUILDER_MOVE_CANCEL);

        if (toolbarMoveIconCancelMode) {
            toolbarMoveIconCancelMode.removeClass(CSS_LAYOUT_BUILDER_MOVE_CANCEL);
        }

        if (!(targetNode.hasClass(CSS_MOVE_TARGET) && targetNode.hasClass(CSS_MOVE_COL_TARGET))) {
            this._layoutBuilder.cancelMove();
            this._detachCancelMoveFieldEvents();
        }
    },

    /**
     * Fires when click event is triggered.
     *
     * @method _onClickOutsideMoveRowTarget
     * @param {EventFacade} event
     * @protected
     */
    _onClickOutsideMoveRowTarget: function(event) {
        var targetNode = event.target;

        if (!(targetNode.hasClass(CSS_MOVE_ROW_TARGET))) {
            this._layoutBuilder.cancelMove();
        }
    },

    /**
     * Fires when esc key press event is triggered.
     *
     * @method _onEscKeyPressMoveTarget
     * @protected
     */
    _onEscKeyPressMoveTarget: function() {
        var toolbarMoveIconCancelMode = this._fieldToolbar.getItem('.' + CSS_LAYOUT_BUILDER_MOVE_CANCEL);

        if (toolbarMoveIconCancelMode) {
            toolbarMoveIconCancelMode.removeClass(CSS_LAYOUT_BUILDER_MOVE_CANCEL);
        }

        this._layoutBuilder.cancelMove();
        this._detachCancelMoveFieldEvents();
    },

    /**
     * Fired when mouse enters a toolbar's field.
     *
     * @method _onFormBuilderToolbarHasAddedToField
     * @params {EventFacade} event
     * @protected
     */
    _onFormBuilderToolbarHasAddedToField: function(event) {
        this._setMoveButtonData(event.colNode);
    },

    /**
     * Overrides default `removeColMoveButtons` attribute.
     *
     * @method _removeColMoveButtons
     * @protected
     */
    _removeColMoveButtons: function() {
        this.get('contentBox').all('.' + CSS_CHOOSE_COL_MOVE).removeClass(CSS_CHOOSE_COL_MOVE);
    },

    /**
     * Overrides default `removeColMoveTargets` attribute.
     *
     * @method _removeColMoveTargets
     * @protected
     */
    _removeColMoveTargets: function() {
        var contentBox = this.get('contentBox'),
            layout = this.getActiveLayout();

        contentBox.all('.' + CSS_CHOOSE_COL_MOVE_TARGET).removeClass(CSS_CHOOSE_COL_MOVE_TARGET);
        contentBox.all('.' + CSS_FIELD_MOVING).removeClass(CSS_FIELD_MOVING);
        contentBox.all('.' + CSS_FIELD_MOVE_TARGET_INVALID).removeClass(CSS_FIELD_MOVE_TARGET_INVALID);

        layout.normalizeColsHeight(layout.get('node').all('.row'));
    },

    /**
     * Remove original layout cut col buttons.
     *
     * @method _removeLayoutCutColButtons
     * @protected
     */
    _removeLayoutCutColButtons: function() {
        this._layoutBuilder.get('removeColMoveButtons')();
    },

    /**
     * Find and focus on first valid move target.
     *
     * @method _selectFirstValidMoveTarget
     * @protected
     */
    _selectFirstValidMoveTarget: function() {
        var moveTarget = A.one('.' + CSS_FIELD_MOVE_TARGET + ':not(.' + CSS_FIELD_MOVE_TARGET_INVALID + ')');

        moveTarget.focus();
    },

    /**
     * Show or hide move item in toolbar.
     *
     * @method _setMoveButtonData
     * @param {Node} colNode
     * @protected
     */
    _setMoveButtonData: function(colNode) {
        var moveItem = this._fieldToolbar.getItem('.layout-builder-move-cut-button');

        moveItem.setData('layout-row', colNode.ancestor('.row').getData('layout-row'));
        moveItem.setData('node-col', colNode);
    }
};


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