(function ($, BEM, undefined) {

    'use strict';

    var invalidBlocksDescriptions = [
        // отображается попап с ошибкой
        {block: 'b-form-input', modName: 'notification', modVal: 'error'},
        // попап может быть скрыт, а ошибка есть
        {block: 'b-form-input', modName: 'error', modVal: 'yes'},
        {block: 'b-form-select', modName: 'error', modVal: 'yes'},
        {block: 'b-form-radio', modName: 'error', modVal: 'yes'},
        {block: 'b-form-radiobox', modName: 'error', modVal: 'yes'},
        {block: 'b-form-checkbox', modName: 'error', modVal: 'yes'},
        {block: 'b-form-address', modName: 'error', modVal: 'yes'},
        {block: 'b-form-nr-address', modName: 'error', modVal: 'yes'},
        {block: 'b-form-clid-settings', modName: 'error', modVal: 'yes'},
        {block: 'b-form-attach', modName: 'error', modVal: 'yes'},
        {block: 'b-form-pfr', modName: 'error', modVal: 'yes'}
    ];

    function showError(block) {
        if (typeof block.showError === 'function') {
            block.showError();
        }
    }

    function hideError(block) {
        if (typeof block.hideError === 'function') {
            block.hideError();
        }
    }

    /**
     * Вызывает метод destructPopup, если он есть у блока
     *
     * @param {bem-block} block
     **/
    function destructPopup(block) {
        if (typeof block.destructPopup === 'function') {
            block.destructPopup();
        }
    }

    function flatten(result, item) {
        return result.concat(item);
    }

    function getTopmostBlock(topmostBlock, block) {
        var blockOffset, topmostBlockOffset;

        if (topmostBlock === null) {
            return block;
        }

        blockOffset = block.domElem.offset();
        topmostBlockOffset = topmostBlock.domElem.offset();

        if (blockOffset.top < topmostBlockOffset.top) {
            return block;
        }

        if (blockOffset.top === topmostBlockOffset.top && blockOffset.left < topmostBlockOffset.left) {
            return block;
        }

        return topmostBlock;
    }

    function getErrorBlocksIn(block) {
        return invalidBlocksDescriptions
            .map(function (blockFilter) {
                return this.findBlocksInside(blockFilter);
            }, block)
            .reduce(flatten, []);
    }

    BEM.DOM.decl({block: 'b-form', modName: 'errorable', modVal: 'yes'}, {

        onSetMod: {
            js: function () {
                this._validate();
                $(this.domElem).submit($.proxy(this._onSubmit, this));
            }
        },
        destruct: function () {
            getErrorBlocksIn(this).forEach(destructPopup);
            this.__base.apply(this, arguments);
        },
        isValid: function () {
            return this._validate();
        },

        showErrors: function () {
            getErrorBlocksIn(this).forEach(showError);
        },

        hideErrors: function () {
            getErrorBlocksIn(this).forEach(hideError);
        },

        _validate: function () {
            var errorBlocks = getErrorBlocksIn(this),
                topmostErrorBlock = errorBlocks.reduce(getTopmostBlock, null);

            errorBlocks.forEach(showError);

            if (topmostErrorBlock !== null) {
                topmostErrorBlock.setMod('focused', 'yes');
            }

            return errorBlocks.length === 0;
        },

        _onSubmit: function () {
            if (this._validate() === false) {
                this.setMod('valid', 'no');
                return false;
            }
            this.trigger('submit');
            this.setMod('valid', 'yes');
            return true;
        }
    });

})(jQuery, BEM);
