/* eslint-disable max-len */
/**
 * A helper class for the native JavaScript Error object that adds a few useful capabilities for handling
 * errors in an application. When you use Ext.Error to {@link #raise} an error from within any class that
 * uses the Class System, the Error class can automatically add the source class and method from which
 * the error was raised. It also includes logic to automatically log the error to the console, if available,
 * with additional metadata about the error. In all cases, the error will always be thrown at the end so that
 * execution will halt.
 *
 * Ext.Error also offers a global error {@link #handle handling} method that can be overridden in order to
 * handle application-wide errors in a single spot. You can optionally {@link #ignore} errors altogether,
 * although in a real application it's usually a better idea to override the handling function and perform
 * logging or some other method of reporting the errors in a way that is meaningful to the application.
 *
 * At its simplest you can simply raise an error as a simple string from within any code:
 *
 * Example usage:
 *
 *     Ext.raise('Something bad happened!');
 *
 * If raised from plain JavaScript code, the error will be logged to the console (if available) and the message
 * displayed. In most cases however you'll be raising errors from within a class, and it may often be useful to add
 * additional metadata about the error being raised.  The {@link #raise} method can also take a config object.
 * In this form the `msg` attribute becomes the error description, and any other data added to the config gets
 * added to the error object and, if the console is available, logged to the console for inspection.
 *
 * Example usage:
 *
 *     Ext.define('Ext.Foo', {
 *         doSomething: function(option){
 *             if (someCondition === false) {
 *                 Ext.raise({
 *                     msg: 'You cannot do that!',
 *                     option: option,   // whatever was passed into the method
 *                     'error code': 100 // other arbitrary info
 *                 });
 *             }
 *         }
 *     });
 *
 * If a console is available (that supports the `console.dir` function) you'll see console output like:
 *
 *     An error was raised with the following data:
 *     option:         Object { foo: "bar"}
 *         foo:        "bar"
 *     error code:     100
 *     msg:            "You cannot do that!"
 *     sourceClass:   "Ext.Foo"
 *     sourceMethod:  "doSomething"
 *
 *     uncaught exception: You cannot do that!
 *
 * As you can see, the error will report exactly where it was raised and will include as much information as the
 * raising code can usefully provide.
 *
 * If you want to handle all application errors globally you can simply override the static {@link #handle} method
 * and provide whatever handling logic you need. If the method returns true then the error is considered handled
 * and will not be thrown to the browser. If anything but true is returned then the error will be thrown normally.
 *
 * Example usage:
 *
 *     Ext.Error.handle = function(err) {
 *         if (err.someProperty == 'NotReallyAnError') {
 *             // maybe log something to the application here if applicable
 *             return true;
 *         }
 *         // any non-true return value (including none) will cause the error to be thrown
 *     }
 *
 * @class Ext.Error
 */
/* eslint-enable max-len */
/* eslint-disable indent */
(function() {
// @define Ext.lang.Error
// @define Ext.Error
// @require Ext
 
    function toString() {
        var me = this,
            cls = me.sourceClass,
            method = me.sourceMethod,
            msg = me.msg;
 
        if (method) {
            if (msg) {
                method += '(): ';
                method += msg;
            }
            else {
                method += '()';
            }
        }
 
        if (cls) {
            method = method ? (cls + '.' + method) : cls;
        }
        
        return method || msg || '';
    }
 
    Ext.Error = function(config) {
        var error = new Error();
 
        if (Ext.isString(config)) {
            config = { msg: config };
        }
 
        Ext.apply(error, config);
 
        error.message = error.message || error.msg; // 'message' is standard ('msg' is non-standard)
        // note: the above does not work in old WebKit (me.message is readonly) (Safari 4)
 
        error.toString = toString;
 
        return error;
    };
 
    Ext.apply(Ext.Error, {
        /**
         * @property {Boolean} ignore
         * Static flag that can be used to globally disable error reporting to the browser if set
         * to true (defaults to false). Note that if you ignore Ext errors it's likely that some
         * other code may fail and throw a native JavaScript error thereafter, so use with caution.
         * In most cases it will probably be preferable to supply a custom error
         * {@link #handle handling} function instead.
         *
         * Example usage:
         *
         *     Ext.Error.ignore = true;
         *
         * @static
         */
        ignore: false,
 
        /**
         * This method is called internally by {@link Ext#raise}. Application code should
         * call {@link Ext#raise} instead of calling this method directly.
         *
         * @static
         * @deprecated 6.0.0 Use {@link Ext#raise} instead.
         */
        raise: function(err) {
            var me = this,
                method = me.raise.caller,
                msg, name;
 
            err = err || {};
            
            if (Ext.isString(err)) {
                err = { msg: err };
            }
 
            if (method === Ext.raise) {
                method = method.caller;
            }
            
            if (method) {
                if (!err.sourceMethod && (name = method.$name)) {
                    err.sourceMethod = name;
                }
                
                if (!err.sourceClass && (name = method.$owner) && (name = name.$className)) {
                    err.sourceClass = name;
                }
            }
 
            if (me.handle(err) !== true) {
                msg = toString.call(err);
 
                //<debug>
                Ext.log({
                    msg: msg,
                    level: 'error',
                    dump: err,
                    stack: true
                });
                //</debug>
 
                throw new Ext.Error(err);
            }
        },
 
        /**
         * Globally handle any Ext errors that may be raised, optionally providing custom logic
         * to handle different errors individually. Return true from the function to bypass 
         * throwing the error to the browser, otherwise the error will be thrown and execution
         * will halt.
         *
         * Example usage:
         *
         *     Ext.Error.handle = function(err) {
         *         if (err.someProperty == 'NotReallyAnError') {
         *             // maybe log something to the application here if applicable
         *             return true;
         *         }
         *         // any non-true return value (including none) will cause the error to be thrown
         *     }
         *
         * @param {Object} err The error being raised. It will contain any attributes that were
         * originally raised with it, plus properties about the method and class from which
         * the error originated (if raised from a class that uses the Class System).
         * @static
         */
        handle: function() {
            return this.ignore;
        }
    });
})();
 
/**
 * Create a function that will throw an error if called (in debug mode) with a message that
 * indicates the method has been removed.
 * @param {String} suggestion Optional text to include in the message (a workaround perhaps).
 * @return {Function} The generated function.
 * @private
 */
Ext.deprecated = function(suggestion) {
    //<debug>
    if (!suggestion) {
        suggestion = '';
    }
 
    function fail() {
        Ext.raise('The method "' + fail.$owner.$className + '.' + fail.$name +
                  '" has been removed. ' + suggestion);
    }
 
    return fail;
    //</debug>
    
    return Ext.emptyFn; // eslint-disable-line no-unreachable
};
 
/**
 * Raise an error that can include additional data and supports automatic console logging
 * if available. You can pass a string error message or an object with the `msg` attribute
 * which will be used as the error message. The object can contain any other name-value
 * attributes (or objects) to be logged along with the error.
 *
 * Note that after displaying the error message a JavaScript error will ultimately be
 * thrown so that execution will halt.
 *
 * Example usage:
 *
 *     Ext.raise('A simple string error message');
 *
 *     // or...
 *
 *     Ext.define('Ext.Foo', {
 *         doSomething: function(option){
 *             if (someCondition === false) {
 *                 Ext.raise({
 *                     msg: 'You cannot do that!',
 *                     option: option,   // whatever was passed into the method
 *                     code: 100 // other arbitrary info
 *                 });
 *             }
 *         }
 *     });
 *
 * @param {String/Object} err The error message string, or an object containing the
 * attribute "msg" that will be used as the error message. Any other data included in the
 * object will also be logged to the browser console, if available.
 * @method raise
 * @member Ext
 */
Ext.raise = function() {
    Ext.Error.raise.apply(Ext.Error, arguments);
};
 
/*
 * This mechanism is used to notify the user of the first error encountered on the page. In
 * most cases errors go unobserved especially on IE. This mechanism pushes this information
 * to the status bar so that users don't miss it.
 */
//<debug>
(function(skipNotify) {
    if (skipNotify || typeof window === 'undefined') {
        return; // build system or some such environment...
    }
 
    // eslint-disable-next-line vars-on-top
    var last = 0,
        // This method is called to notify the user of the current error status.
        notify = function() {
            var cnt = Ext.log && Ext.log.counters,
                n = cnt && (cnt.error + cnt.warn + cnt.info + cnt.log),
                msg;
 
            // Put log counters to the status bar (for most browsers):
            if (&& last !== n) {
                msg = [];
                
                if (cnt.error) {
                    msg.push('Errors: ' + cnt.error);
                }
                
                if (cnt.warn) {
                    msg.push('Warnings: ' + cnt.warn);
                }
                
                if (cnt.info) {
                    msg.push('Info: ' + cnt.info);
                }
                
                if (cnt.log) {
                    msg.push('Log: ' + cnt.log);
                }
                
                window.status = '*** ' + msg.join(' -- ');
                last = n;
            }
        };
    
    // Allow unit tests to skip this when checking for dangling timers
    notify.$skipTimerCheck = true;
 
    // window.onerror sounds ideal but it prevents the built-in error dialog from doing
    // its (better) thing. We deliberately use setInterval() here instead of going with
    // Ext.interval() to keep it basic and simple.
    setInterval(notify, 1000);
}(!!window.__UNIT_TESTING__));
//</debug>