/**
 * A {@link Ext.ux.statusbar.StatusBar} plugin that provides automatic error
 * notification when the associated form contains validation errors.
 */
Ext.define('Ext.ux.statusbar.ValidationStatus', {
    extend: 'Ext.Component',
    alias: 'plugin.validationstatus',
    requires: ['Ext.util.MixedCollection'],
    /**
     * @cfg {String} errorIconCls
     * The {@link Ext.ux.statusbar.StatusBar#iconCls iconCls} value to be applied
     * to the status message when there is a validation error.
     */
    errorIconCls: 'x-status-error',
    /**
     * @cfg {String} errorListCls
     * The css class to be used for the error list when there are validation errors.
     */
    errorListCls: 'x-status-error-list',
    /**
     * @cfg {String} validIconCls
     * The {@link Ext.ux.statusbar.StatusBar#iconCls iconCls} value to be applied
     * to the status message when the form validates.
     */
    validIconCls: 'x-status-valid',

    /**
     * @cfg {String} showText
     * The {@link Ext.ux.statusbar.StatusBar#text text} value to be applied when
     * there is a form validation error.
     */
    showText: 'The form has errors (click for details...)',
    /**
     * @cfg {String} hideText
     * The {@link Ext.ux.statusbar.StatusBar#text text} value to display when
     * the error list is displayed.
     */
    hideText: 'Click again to hide the error list',
    /**
     * @cfg {String} submitText
     * The {@link Ext.ux.statusbar.StatusBar#text text} value to be applied when
     * the form is being submitted.
     */
    submitText: 'Saving...',

    /**
     * @private
     */
    init: function(sb) {
        var me = this;

        me.statusBar = sb;
        sb.on({
            single: true,
            scope: me,
            render: me.onStatusbarRender
        });
        sb.on({
            click: {
                element: 'el',
                fn: me.onStatusClick,
                scope: me,
                buffer: 200
            }
        });
    },

    onStatusbarRender: function(sb) {
        var me = this,
            startMonitor = function() {
                me.monitor = true;
            };

        me.monitor = true;
        me.errors = Ext.create('Ext.util.MixedCollection');
        me.listAlign = (sb.statusAlign === 'right' ? 'br-tr?' : 'bl-tl?');

        if (me.form) {
            // Allow either an id, or a reference to be specified as the form name.
            me.formPanel = Ext.getCmp(me.form) ||
                           me.statusBar.lookupController().lookupReference(me.form);
            me.basicForm = me.formPanel.getForm();
            me.startMonitoring();
            me.basicForm.on({
                beforeaction: function(f, action) {
                    if (action.type === 'submit') {
                        // Ignore monitoring while submitting otherwise the field validation
                        // events cause the status message to reset too early
                        me.monitor = false;
                    }
                }
            });
            me.formPanel.on({
                beforedestroy: me.destroy,
                scope: me
            });
            me.basicForm.on('actioncomplete', startMonitor);
            me.basicForm.on('actionfailed', startMonitor);
        }
    },

    /**
     * @private
     */
    startMonitoring: function() {
        this.basicForm.getFields().each(function(f) {
            f.on('validitychange', this.onFieldValidation, this);
        }, this);
    },

    /**
     * @private
     */
    stopMonitoring: function() {
        var form = this.basicForm;

        if (!form.destroyed) {
            form.getFields().each(function(f) {
                f.un('validitychange', this.onFieldValidation, this);
            }, this);
        }
    },

    doDestroy: function() {
        Ext.destroy(this.msgEl);
        this.stopMonitoring();
        this.statusBar.statusEl.un('click', this.onStatusClick, this);
        this.callParent();
    },

    /**
     * @private
     */
    onFieldValidation: function(f, isValid) {
        var me = this,
            msg;

        if (!me.monitor) {
            return false;
        }

        msg = f.getErrors()[0];

        if (msg) {
            me.errors.add(f.id, { field: f, msg: msg });
        }
        else {
            me.errors.removeAtKey(f.id);
        }

        this.updateErrorList();

        if (me.errors.getCount() > 0) {
            if (me.statusBar.getText() !== me.showText) {
                me.statusBar.setStatus({
                    text: me.showText,
                    iconCls: me.errorIconCls
                });
            }
        }
        else {
            me.statusBar.clearStatus().setIcon(me.validIconCls);
        }
    },

    /**
     * @private
     */
    updateErrorList: function() {
        var me = this,
            msg,
            msgEl = me.getMsgEl();

        if (me.errors.getCount() > 0) {
            msg = ['<ul>'];
            this.errors.each(function(err) {
                msg.push('<li id="x-err-', err.field.id, '"><a href="#">', err.msg, '</a></li>');
            });
            msg.push('</ul>');
            msgEl.update(msg.join(''));
        }
        else {
            msgEl.update('');
        }

        // reset msgEl size
        msgEl.setSize('auto', 'auto');
    },

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

        if (!msgEl) {
            msgEl = me.msgEl = Ext.DomHelper.append(Ext.getBody(), {
                cls: me.errorListCls
            }, true);
            msgEl.hide();
            msgEl.on('click', function(e) {
                t = e.getTarget('li', 10, true);

                if (t) {
                    Ext.getCmp(t.id.split('x-err-')[1]).focus();
                    me.hideErrors();
                }
            }, null, { stopEvent: true }); // prevent anchor click navigation
        }

        return msgEl;
    },

    /**
     * @private
     */
    showErrors: function() {
        var me = this;

        me.updateErrorList();
        me.getMsgEl().alignTo(me.statusBar.getEl(), me.listAlign).slideIn(
            'b', { duration: 300, easing: 'easeOut' }
        );

        me.statusBar.setText(me.hideText);

        // hide if the user clicks directly into the form
        me.formPanel.body.on('click', me.hideErrors, me, { single: true });
    },

    /**
     * @private
     */
    hideErrors: function() {
        var el = this.getMsgEl();

        if (el.isVisible()) {
            el.slideOut('b', { duration: 300, easing: 'easeIn' });
            this.statusBar.setText(this.showText);
        }

        this.formPanel.body.un('click', this.hideErrors, this);
    },

    /**
     * @private
     */
    onStatusClick: function() {
        if (this.getMsgEl().isVisible()) {
            this.hideErrors();
        }
        else if (this.errors.getCount() > 0) {
            this.showErrors();
        }
    }
});
