//  This file is distributed as part of the bit-babbler package.
//  Copyright 2003 - 2015,  Ron <ron@debian.org>
//
// You must include bit-babbler/impl/log.h exactly once in some translation unit
// of any program using the Log() functions.

#ifndef _BB_LOG_H
#define _BB_LOG_H

#include <bit-babbler/exceptions.h>

#ifdef _REENTRANT
    #include <pthread.h>
#endif

#if EM_PLATFORM_POSIX

    #include <syslog.h>

#else

    // Stub definitions of the syslog functionality we use.
    // The openlog and vsyslog functions will need to be implemented suitably
    // for platforms that don't provide them natively.

    // Option flag for openlog
    #define LOG_PID     0x01	// Log the Process ID with each message

    // Syslog facility
    #define LOG_DAEMON  (3<<3)  // system daemons

    // Syslog priority
    #define LOG_NOTICE  5       // normal but significant condition

    #define LOG_MAKEPRI(facility, priority) ((facility) | (priority))

    void openlog(const char *ident, int option, int facility);
    void vsyslog(int priority, const char *format, va_list ap);

#endif

#include <sys/time.h>
#include <stdint.h>


#if EM_USE_GETTEXT

    #include <libintl.h>

    #define _(x)        gettext(x)
    #define _P(x,y,n)  ngettext(x,y,n)

#else

    #define _(x) x
    #define _P(singular, plural, number)    ( number == 1 ? singular : plural )

#endif


#define BB_CATCH_STD( LogLevel, Message, ... )                          \
    catch( const std::exception &e )                                    \
    {                                                                   \
        BitB::Log< LogLevel >( "%s: %s\n", Message, e.what() );         \
        __VA_ARGS__                                                     \
    }                                                                   \
    catch( ... )                                                        \
    {                                                                   \
        BitB::Log< LogLevel >( "%s\n", Message );                       \
        __VA_ARGS__                                                     \
    }

#define BB_CATCH_ALL( LogLevel, Message, ... )                          \
    catch( const abi::__forced_unwind& )                                \
    {                                                                   \
        BitB::Log< LogLevel >( "%s: thread cancelled\n", Message );     \
        __VA_ARGS__                                                     \
        throw;                                                          \
    }                                                                   \
    BB_CATCH_STD( LogLevel, Message, __VA_ARGS__ )


namespace BitB
{
    typedef std::basic_string<uint8_t>      OctetString;

    std::string OctetsToHex( const OctetString &octets, size_t wrap = 0, bool short_form = false );

    std::string OctetsToShortHex( const OctetString &octets, size_t wrap = 0 )
    {
        return OctetsToHex( octets, wrap, true );
    }

    template< typename T >
    std::string AsBinary( T n )
    { //{{{

        std::string     s;

        for( int i = sizeof(T) * 8 - 1; i >= 0; --i )
            s.push_back( n & 1ull<<i ? '1' : '0' );

        return s;

    } //}}}

    static inline unsigned long StrToUL( const char *s, int base = 0 )
    { //{{{

        char           *e;
        unsigned long   v = strtoul( s, &e, base );

        if( e == s || *e != '\0' )
            throw Error( _("StrToUL(%s): not a number"), s );

        return v;

    } //}}}

    static inline unsigned long StrToUL( const std::string &s, int base = 0 )
    {
        return StrToUL( s.c_str(), base );
    }


    unsigned long StrToScaledUL( const char *s, unsigned scale = 1000 );

    static inline unsigned long StrToScaledUL( const std::string &s, unsigned scale = 1000 )
    {
        return StrToScaledUL( s.c_str(), scale );
    }


    double StrToScaledD( const char *s );

    static inline double StrToScaledD( const std::string &s )
    {
        return StrToScaledD( s.c_str() );
    }



    BB_PRINTF_FORMAT(1,2)
    std::string stringprintf( const char *format, ... );

    std::string vstringprintf( const char *format, va_list arglist );


    void GetFutureTimespec( timespec &ts, unsigned ms );

    static inline timeval GetWallTimeval()
    { //{{{

        timeval     t;

      #if HAVE_GETTIMEOFDAY

        if( gettimeofday( &t, NULL ) == -1 )
            throw Error( _("gettimeofday() failed") );

      #else
        // We can implement this in alternative ways if needed
        #error "No suitable wall time function available"
      #endif

        return t;

    } //}}}

    std::string timeprintf( std::string format, const timeval &tv = GetWallTimeval() );


   #ifdef _REENTRANT

    void SetThreadName( const char *name, pthread_t tid = pthread_self() )
    {
       #if HAVE_PTHREAD_SETNAME_NP
        pthread_setname_np( tid, name );
       #else
        (void)name; (void)tid;
       #endif
    }

    void SetThreadName( const std::string &name, pthread_t tid = pthread_self() )
    {
        SetThreadName( name.c_str(), tid );
    }

   #endif


    extern bool     opt_syslog;
    extern bool     opt_timestamp;
    extern int      opt_verbose;


    template< int N > void Logv( const char *format, va_list arglist )
    { //{{{

        if( opt_verbose < N )
            return;

        if( opt_timestamp )
        {
            std::string     fmt = timeprintf("%T.%%u") + ": " + format;

            if( opt_syslog )
                vsyslog( LOG_MAKEPRI(LOG_DAEMON, LOG_NOTICE), fmt.c_str(), arglist );
            else
                vfprintf( stderr, fmt.c_str(), arglist );

        } else {

            if( opt_syslog )
                vsyslog( LOG_MAKEPRI(LOG_DAEMON, LOG_NOTICE), format, arglist );
            else
                vfprintf( stderr, format, arglist );
        }

    } //}}}

    template< int N >
    BB_PRINTF_FORMAT(1,2)
    void Log( const char *format, ... )
    { //{{{

        va_list     arglist;

        va_start( arglist, format );
        Logv<N>( format, arglist );
        va_end( arglist );

    } //}}}

    template< int N >
    BB_PRINTF_FORMAT(1,2)
    void LogErr( const char *format, ... )
    { //{{{

        va_list         arglist;
        std::string     fmt( format );

        if( fmt.size() && fmt[fmt.size() - 1] == '\n' )
            fmt.erase( fmt.size() - 1 );

        fmt.append(": ").append( strerror(errno) ).append(1,'\n');

        va_start( arglist, format );
        Logv<N>( fmt.c_str(), arglist );
        va_end( arglist );

    } //}}}

    template< int N >
    BB_PRINTF_FORMAT(2,3)
    void LogErr( int code, const char *format, ... )
    { //{{{

        va_list         arglist;
        std::string     fmt( format );

        if( fmt.size() && fmt[fmt.size() - 1] == '\n' )
            fmt.erase( fmt.size() - 1 );

        fmt.append(": ").append( strerror(code) ).append(1,'\n');

        va_start( arglist, format );
        Logv<N>( fmt.c_str(), arglist );
        va_end( arglist );

    } //}}}


    std::string DemangleSymbol( const char *sym );

    #define EM_TYPEOF( T ) BitB::DemangleSymbol( typeid( T ).name() ).c_str()
}

#endif  // _BB_LOG_H

// vi:sts=4:sw=4:et:foldmethod=marker
