/**
 * @file llerror.h
 * @date   December 2006
 * @brief error message system
 *
 * $LicenseInfo:firstyear=2006&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2010, Linden Research, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
 */

#ifndef LL_LLERROR_H
#define LL_LLERROR_H

#include <sstream>
#include <string>
#include <typeinfo>
#include <vector>

#include "stdtypes.h"

#include "llprofiler.h"
#include "llpreprocessor.h"

#include <boost/static_assert.hpp>
#include <functional> // std::function

const int LL_ERR_NOERR = 0;

// Define one of these for different error levels in release...
// #define RELEASE_SHOW_DEBUG // Define this if you want your release builds to show lldebug output.
#define RELEASE_SHOW_INFO // Define this if you want your release builds to show llinfo output
#define RELEASE_SHOW_WARN // Define this if you want your release builds to show llwarn output.

#ifdef _DEBUG
#define SHOW_DEBUG
#define SHOW_WARN
#define SHOW_INFO
#define SHOW_ASSERT
#else // _DEBUG

#ifdef LL_RELEASE_WITH_DEBUG_INFO
#define SHOW_ASSERT
#endif // LL_RELEASE_WITH_DEBUG_INFO

#ifdef RELEASE_SHOW_DEBUG
#define SHOW_DEBUG
#endif

#ifdef RELEASE_SHOW_WARN
#define SHOW_WARN
#endif

#ifdef RELEASE_SHOW_INFO
#define SHOW_INFO
#endif

#ifdef RELEASE_SHOW_ASSERT
#define SHOW_ASSERT
#endif

#endif // !_DEBUG

#define llassert_always_msg(func, msg) if (LL_UNLIKELY(!(func))) LL_ERRS() << "ASSERT (" << msg << ")" << LL_ENDL

#define llassert_always(func)   llassert_always_msg(func, #func)

#ifdef SHOW_ASSERT
#define llassert(func)          llassert_always_msg(func, #func)
#define llassert_msg(func, msg) llassert_always_msg(func, msg)
#define llverify(func)          llassert_always_msg(func, #func)
#else
#define llassert(func)
#define llassert_msg(func, msg)
#define llverify(func)          do {if (func) {}} while(0)
#endif

#ifdef LL_WINDOWS
#define LL_STATIC_ASSERT(func, msg) static_assert(func, msg)
#define LL_BAD_TEMPLATE_INSTANTIATION(type, msg) static_assert(false, msg)
#else
#if LL_LINUX
// We need access to raise and SIGSEGV
#include <signal.h>
#endif

#define LL_STATIC_ASSERT(func, msg) BOOST_STATIC_ASSERT(func)
#define LL_BAD_TEMPLATE_INSTANTIATION(type, msg) BOOST_STATIC_ASSERT(sizeof(type) != 0 && false);
#endif


/** Error Logging Facility

    Information for most users:

    Code can log messages with constructions like this:

        LL_INFOS("StringTag") << "request to fizzbip agent " << agent_id
            << " denied due to timeout" << LL_ENDL;

    Messages can be logged to one of four increasing levels of concern,
    using one of four "streams":

        LL_DEBUGS("StringTag")  - debug messages that are normally suppressed
        LL_INFOS("StringTag")   - informational messages that are normal shown
        LL_WARNS("StringTag")   - warning messages that signal a problem
        LL_ERRS("StringTag")    - error messages that are major, unrecoverable failures

    The later (LL_ERRS("StringTag")) automatically crashes the process after the message
    is logged.

    Note that these "streams" are actually #define magic.  Rules for use:
        * they cannot be used as normal streams, only to start a message
        * messages written to them MUST be terminated with LL_ENDL
        * between the opening and closing, the << operator is indeed
          writing onto a std::ostream, so all conversions and stream
          formating are available

    These messages are automatically logged with function name, and (if enabled)
    file and line of the message.  (Note: Existing messages that already include
    the function name don't get name printed twice.)

    If you have a class, adding LOG_CLASS line to the declaration will cause
    all messages emitted from member functions (normal and static) to be tagged
    with the proper class name as well as the function name:

        class LLFoo
        {
            LOG_CLASS(LLFoo);
        public:
            ...
        };

        void LLFoo::doSomething(int i)
        {
            if (i > 100)
            {
                LL_WARNS("FooBarTag") << "called with a big value for i: " << i << LL_ENDL;
            }
            ...
        }

    will result in messages like:

        WARN #FooBarTag# llcommon/llfoo(100) LLFoo::doSomething : called with a big value for i: 283

    the syntax is:
        <timestamp> SPACE <level> SPACE <tags> SPACE <location> SPACE <function> SPACE COLON SPACE <message>

    where each SPACE is a single space character; note that if a field is empty (such as when no
    tags are specified), all the SPACEs are still present.

    The tags must be a single word (may not contain a space); if more than one tag is specified,
    they are all surrounded by '#' ( #FooTag#BarTag# ).

    Which messages are logged and which are suppressed can be controlled at run
    time from the configuration file. The default configuration is in newview/app_settings/logcontrol.xml
    A copy of that file named logcontrol-dev.xml can be made in the users personal settings
    directory; that will override the installed default file.  See the logcontrol.xml
    file or http://wiki.secondlife.com/wiki/Logging_System_Overview for configuration details.

    Lastly, logging is now very efficient in both compiled code and execution
    when skipped.  There is no need to wrap messages, even debugging ones, in
    #ifdef _DEBUG constructs.  LL_DEBUGS("StringTag") messages are compiled into all builds,
    even release.  Which means you can use them to help debug even when deployed
    to a real grid.
*/
namespace LLError
{
    enum ELevel
    {
        LEVEL_ALL = 0,
            // used to indicate that all messages should be logged

        LEVEL_DEBUG = 0,
        LEVEL_INFO = 1,
        LEVEL_WARN = 2,
        LEVEL_ERROR = 3,    // used to be called FATAL

        LEVEL_NONE = 4
            // not really a level
            // used to indicate that no messages should be logged
    };
    // If you change ELevel, please update llvlog() macro below.

    /*  Macro support
        The classes CallSite and Log are used by the logging macros below.
        They are not intended for general use.
    */

    struct CallSite;

    class LL_COMMON_API Log
    {
    public:
        static bool shouldLog(CallSite&);
        static void flush(const std::ostringstream&, const CallSite&);
        static std::string demangle(const char* mangled);
        /// classname<TYPE>()
        template <typename T>
        static std::string classname()             { return demangle(typeid(T).name()); }
        /// classname(some_pointer)
        template <typename T>
        static std::string classname(T* const ptr) { return ptr? demangle(typeid(*ptr).name()) : "nullptr"; }
        /// classname(some_reference)
        template <typename T>
        static std::string classname(const T& obj) { return demangle(typeid(obj).name()); }
    };

    struct LL_COMMON_API CallSite
    {
        // Represents a specific place in the code where a message is logged
        // This is public because it is used by the macros below.  It is not
        // intended for public use.
        CallSite(ELevel level,
                const char* file,
                int line,
                const std::type_info& class_info,
                const char* function,
                bool print_once,
                const char** tags,
                size_t tag_count);

        ~CallSite();

#ifdef LL_LIBRARY_INCLUDE
        bool shouldLog();
#else // LL_LIBRARY_INCLUDE
        bool shouldLog()
        {
            return mCached
                    ? mShouldLog
                    : Log::shouldLog(*this);
        }
            // this member function needs to be in-line for efficiency
#endif // LL_LIBRARY_INCLUDE

        void invalidate();

        // these describe the call site and never change
        const ELevel            mLevel;
        const char* const       mFile;
        const int               mLine;
        const std::type_info&   mClassInfo;
        const char* const       mFunction;
        const char**            mTags;
        size_t                  mTagCount;
        const bool              mPrintOnce;
        const char*             mLevelString;
        std::string             mLocationString,
                                mFunctionString,
                                mTagString;
        bool                    mCached,
                                mShouldLog;

        friend class Log;
    };


    class End { };
    inline std::ostream& operator<<(std::ostream& s, const End&)
        { return s; }
        // used to indicate the end of a message

    class LL_COMMON_API NoClassInfo { };
        // used to indicate no class info known for logging

    //LLCallStacks keeps track of call stacks and output the call stacks to log file
    //
    //Note: to be simple, efficient and necessary to keep track of correct call stacks,
    //LLCallStacks is designed not to be thread-safe.
    //so try not to use it in multiple parallel threads at same time.
    //Used in a single thread at a time is fine.
    class LL_COMMON_API LLCallStacks
    {
    private:
        typedef std::vector<std::string> StringVector;
        static StringVector sBuffer ;

    public:
        static void push(const char* function, const int line) ;
        static void insert(std::ostream& out, const char* function, const int line) ;
        static void print() ;
        static void clear() ;
        static void end(const std::ostringstream& out) ;
        static void cleanup();
    };

    // class which, when streamed, inserts the current stack trace
    struct LLStacktrace
    {
        friend std::ostream& operator<<(std::ostream& out, const LLStacktrace&);
    };

    // Provides access to OS notification popup on error, since
    // not everything has access to OS's messages
    class LLUserWarningMsg
    {
    public:
        typedef std::function<void(const std::string&, const std::string&)> Handler;
        static void setHandler(const Handler&);
        static void setOutOfMemoryStrings(const std::string& title, const std::string& message);

        // When viewer encounters bad alloc or can't access files try warning user about reasons
        static void showOutOfMemory();
        static void showMissingFiles();
        // Genering error
        static void show(const std::string&);

    private:
        // needs to be preallocated before viewer runs out of memory
        static std::string sLocalizedOutOfMemoryTitle;
        static std::string sLocalizedOutOfMemoryWarning;
        static Handler sHandler;
    };
}

//this is cheaper than llcallstacks if no need to output other variables to call stacks.
#define LL_PUSH_CALLSTACKS() LLError::LLCallStacks::push(__FUNCTION__, __LINE__)

#define llcallstacks                                                    \
    {                                                                   \
        std::ostringstream _out;                                        \
        LLError::LLCallStacks::insert(_out, __FUNCTION__, __LINE__) ;   \
        _out

#define llcallstacksendl                   \
        LLError::End();                    \
        LLError::LLCallStacks::end(_out) ; \
    }

#define LL_CLEAR_CALLSTACKS() LLError::LLCallStacks::clear()
#define LL_PRINT_CALLSTACKS() LLError::LLCallStacks::print()

/*
    Class type information for logging
 */

#define LOG_CLASS(s)    typedef s _LL_CLASS_TO_LOG
    // Declares class to tag logged messages with.
    // See top of file for example of how to use this

typedef LLError::NoClassInfo _LL_CLASS_TO_LOG;
    // Outside a class declaration, or in class without LOG_CLASS(), this
    // typedef causes the messages to not be associated with any class.

/////////////////////////////////
// Error Logging Macros
// See top of file for common usage.
/////////////////////////////////

// Instead of using LL_DEBUGS(), LL_INFOS() et al., it may be tempting to
// directly code the lllog() macro so you can pass in the LLError::ELevel as a
// variable. DON'T DO IT! The reason is that the first time control passes
// through lllog(), it initializes a local static LLError::CallSite with that
// *first* ELevel value. All subsequent visits will decide whether or not to
// emit output based on the *first* ELevel value bound into that static
// CallSite instance. Use LL_VLOGS() instead. lllog() assumes its ELevel
// argument never varies.

// this macro uses a one-shot do statement to avoid parsing errors when
// writing control flow statements without braces:
// if (condition) LL_INFOS() << "True" << LL_ENDL; else LL_INFOS()() << "False" << LL_ENDL;

#define lllog(level, once, ...)                                         \
    do {                                                                \
        LL_PROFILE_ZONE_NAMED("lllog");                                 \
        const char* tags[] = {"", ##__VA_ARGS__};                       \
        static LLError::CallSite _site(lllog_site_args_(level, once, tags)); \
        lllog_test_()

#define lllog_test_()                           \
        if (LL_UNLIKELY(_site.shouldLog()))     \
        {                                       \
            std::ostringstream _out;            \
            _out

#define lllog_site_args_(level, once, tags)                 \
    level, __FILE__, __LINE__, typeid(_LL_CLASS_TO_LOG),    \
    __FUNCTION__, once, &tags[1], LL_ARRAY_SIZE(tags)-1

//Use this construct if you need to do computation in the middle of a
//message:
//
//  LL_INFOS("AgentGesture") << "the agent " << agend_id;
//  switch (f)
//  {
//      case FOP_SHRUGS:    LL_CONT << "shrugs";                break;
//      case FOP_TAPS:      LL_CONT << "points at " << who; break;
//      case FOP_SAYS:      LL_CONT << "says " << message;  break;
//  }
//  LL_CONT << " for " << t << " seconds" << LL_ENDL;
//
//Such computation is done iff the message will be logged.
#define LL_CONT _out

#define LL_NEWLINE '\n'

// Use this only in LL_ERRS or in a place that LL_ERRS may not be used

#ifndef LL_LINUX
#define LLERROR_CRASH                                   \
{                                                       \
    crashdriver([](int* ptr){ *ptr = 0; exit(*ptr); }); \
}
#else
// For Linux we just call raise and be done with it. No fighting the compiler to create a crashing code snippet.
#define LLERROR_CRASH raise(SIGSEGV );
#endif

#define LL_ENDL                                         \
            LLError::End();                             \
            LLError::Log::flush(_out, _site);           \
            if (_site.mLevel == LLError::LEVEL_ERROR)   \
            {                                           \
                LLERROR_CRASH                           \
            }                                           \
        }                                               \
    } while(0)

// NEW Macros for debugging, allow the passing of a string tag

// Pass comma separated list of tags (currently only supports up to 0, 1, or 2)
#define LL_DEBUGS(...)  lllog(LLError::LEVEL_DEBUG, false, ##__VA_ARGS__)
#define LL_INFOS(...)   lllog(LLError::LEVEL_INFO, false, ##__VA_ARGS__)
#define LL_WARNS(...)   lllog(LLError::LEVEL_WARN, false, ##__VA_ARGS__)
#define LL_ERRS(...)    lllog(LLError::LEVEL_ERROR, false, ##__VA_ARGS__)
// alternative to llassert_always that prints explanatory message
// note ## token paste operator hack used above will only work in gcc following
// a comma and is completely unnecessary in VS since the comma is automatically
// suppressed
// https://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html
// https://docs.microsoft.com/en-us/cpp/preprocessor/variadic-macros?view=vs-2015
#define LL_WARNS_IF(exp, ...)   if (exp) LL_WARNS(__VA_ARGS__) << "(" #exp ")"
#define LL_ERRS_IF(exp, ...)    if (exp) LL_ERRS(__VA_ARGS__) << "(" #exp ")"

// Only print the log message once (good for warnings or infos that would otherwise
// spam the log file over and over, such as tighter loops).
#define LL_DEBUGS_ONCE(...) lllog(LLError::LEVEL_DEBUG, true, ##__VA_ARGS__)
#define LL_INFOS_ONCE(...)  lllog(LLError::LEVEL_INFO, true, ##__VA_ARGS__)
#define LL_WARNS_ONCE(...)  lllog(LLError::LEVEL_WARN, true, ##__VA_ARGS__)

// Use this if you need to pass LLError::ELevel as a variable.
#define LL_VLOGS(level, ...)      llvlog(level, false, ##__VA_ARGS__)
#define LL_VLOGS_ONCE(level, ...) llvlog(level, true,  ##__VA_ARGS__)

// The problem with using lllog() with a variable level is that the first time
// through, it initializes a static CallSite instance with whatever level you
// pass. That first level is bound into the CallSite; the level parameter is
// never again examined. One approach to variable level would be to
// dynamically construct a CallSite instance every call -- which could get
// expensive, depending on context. So instead, initialize a static CallSite
// for each level value we support, then dynamically select the CallSite
// instance for the passed level value.
// Compare implementation to lllog() above.
#define llvlog(level, once, ...)                                        \
    do {                                                                \
        const char* tags[] = {"", ##__VA_ARGS__};                       \
        /* Need a static CallSite instance per expected ELevel value. */ \
        /* Since we intend to index this array with the ELevel, */      \
        /* _sites[0] should be ELevel(0), and so on -- avoid using */   \
        /* ELevel symbolic names when initializing -- except for */     \
        /* the last entry, which handles anything beyond the end. */    \
        /* (Commented ELevel value names are from 2016-09-01.) */       \
        /* Passing an ELevel past the end of this array is itself */    \
        /* a fatal error, so ensure the last is LEVEL_ERROR. */         \
        static LLError::CallSite _sites[] =                             \
        {                                                               \
            /* LEVEL_DEBUG */                                           \
            LLError::CallSite(lllog_site_args_(LLError::ELevel(0), once, tags)), \
            /* LEVEL_INFO */                                            \
            LLError::CallSite(lllog_site_args_(LLError::ELevel(1), once, tags)), \
            /* LEVEL_WARN */                                            \
            LLError::CallSite(lllog_site_args_(LLError::ELevel(2), once, tags)), \
            /* LEVEL_ERROR */                                           \
            LLError::CallSite(lllog_site_args_(LLError::LEVEL_ERROR, once, tags)) \
        };                                                              \
        /* Clamp the passed 'level' to at most last entry */            \
        std::size_t which((std::size_t(level) >= LL_ARRAY_SIZE(_sites)) ? \
                          (LL_ARRAY_SIZE(_sites) - 1) : std::size_t(level)); \
        /* selected CallSite *must* be named _site for LL_ENDL */       \
        LLError::CallSite& _site(_sites[which]);                        \
        lllog_test_()

/*
// Check at run-time whether logging is enabled, without generating output.
Resist the temptation to add a function like this because it incurs the
expense of locking and map-searching every time control reaches it.
bool debugLoggingEnabled(const std::string& tag);

Instead of:

if debugLoggingEnabled("SomeTag")
{
    // ... presumably expensive operation ...
    LL_DEBUGS("SomeTag") << ... << LL_ENDL;
}

Use this:

LL_DEBUGS("SomeTag");
// ... presumably expensive operation ...
LL_CONT << ...;
LL_ENDL;

LL_DEBUGS("SomeTag") performs the locking and map-searching ONCE, then caches
the result in a static variable.
*/

// used by LLERROR_CRASH
void crashdriver(void (*)(int*));

#endif // LL_LLERROR_H