/*

  vdb3.cmn.log


 */

#pragma once

#include <vdb3/kfc/ref.hpp>
#include <vdb3/kfc/except.hpp>
#include <vdb3/cmn/buffmt.hpp>
#include <vdb3/cmn/logger.hpp>

#include <time.h>

namespace vdb3
{
    
    /*=====================================================*
     *                    LOGGING MACROS                   *
     *=====================================================*/

    /**
     * @macro LOG
     */
#define LOG( rsrc, lvl, print )                 \
    ( rsrc ) . log . msg ( rsrc, ( rsrc ) . logger, lvl, print )

    /**
     * @macro LOGQ
     */
#define LOGQ( rsrc, qid, lvl, print )           \
    ( rsrc ) . log . msg ( rsrc, ( rsrc ) . logger, qid, lvl, print )

    
    /*=====================================================*
     *                        LogMgr                       *
     *=====================================================*/

    /**
     * LogMgrItf
     *  @brief it is unlikely that more than one implementation
     *   would be needed for Log, but making it a dynamically
     *   allocated ref-counted object eases its use as a
     *   RsrcKfc manager.
     */
    interface LogMgrItf
    {

        /**
         * getThreshold
         *  @return threshold for issuing log messages
         */
        MSG ( LogLevel getThreshold () const noexcept );

        /**
         * getThreshold
         *  @return threshold for issuing log messages to queue
         */
        MSG ( LogLevel getThreshold ( const LogQueueId & qid ) const noexcept );

        /* setThreshold
         *  @brief set threshold for issuing log messages
         */
        MSG ( void setThreshold ( LogLevel lvl ) );

        /* setThreshold
         *  @brief set threshold for issuing log messages to queue
         */
        MSG ( void setThreshold ( const LogQueueId & qid, LogLevel lvl ) );

        /**
         * msg
         *  @brief evaluates a log-level and executes provided function if applicable
         *  @param priority execute "print" iff <= current thread logging level
         *  @param print function to generate formatted log data
         */
        MSG ( TextStreamWriterRef msg ( const RsrcKfc & rsrc, const Logger & logger, LogLevel priority ) );

        /**
         * msg
         *  @brief evaluates a log-level and executes provided function if applicable
         *  @param priority execute "print" iff <= queue's current thread logging level
         *  @param print function to generate formatted log data
         */
        MSG ( TextStreamWriterRef msg ( const RsrcKfc & rsrc, const Logger & logger, const LogQueueId & qid, LogLevel priority ) );


        /*=================================================*
         *                       C++                       *
         *=================================================*/

        MSG ( IRef < LogMgrItf > clone () const );

        virtual ~ LogMgrItf () noexcept {}
    };

    /**
     * LogMgrRef
     */
    typedef IRef < LogMgrItf > LogMgrRef;

    /**
     * LogMgr
     * @brief this object typically lives within the thread's
     *  RsrcKfc block as "log". A user will invoke rsrc.log.msg()
     *  with a logging level to indicate the severity of the issue
     *  and the priority of it being communicated. A version of
     *  this message can be directed at a particular queue as well.
     *
     *  The log object makes a decision based upon the current
     *  logging threshold whether to go ahead with logging or
     *  to ignore it. The decision is based upon either the thread
     *  local log threshold or a queue-specific one. When the decision
     *  is to log the message, the provided print function is called
     *  with a Fmt object for formatted printing.
     *
     *  The Fmt object is embedded within Log, and is a BufferedFmt
     *  in particular. It uses an internal Buffer to collect the
     *  message generated by the user-supplied "print()" function,
     *  and then flushes the buffer to output upon return.
     *
     *  When the BufferedFmt flushes to output, the text needs to
     *  be augmented with information such as hostname, pid, qid,
     *  and timestamp. These are gathered before printing and stored
     *  in a LogWriter, which is an object capable of supplying a
     *  StreamWriter interface to the BufferedFmt, and forwarding
     *  the message on to the Logger.
     *
     *  The Logger is finally an object that processes rich message
     *  records with multiple fields that get delivered to a destination
     *  such as stderr, a file, syserr, a logging service, or a database.
     */
    class LogMgr
    {
    public:

        /**
         * getThreshold
         *  @return threshold for issuing log messages
         */
        inline LogLevel getThreshold () const noexcept
        { return lvl; }

        /**
         * getThreshold
         *  @return threshold for issuing log messages to queue
         */
        LogLevel getThreshold ( const LogQueueId & qid ) const noexcept;


        /* setThreshold
         *  @brief set threshold for issuing log messages
         */
        void setThreshold ( LogLevel lvl ) const;


        /* setThreshold
         *  @brief set threshold for issuing log messages to queue
         */
        void setThreshold ( const LogQueueId & qid, LogLevel lvl ) const;

        /**
         * msg
         *  @brief evaluates log-level and executes provided function if applicable
         *  @param rsrc resource manager for buffered formatter
         *  @param logger logging output resource
         *  @param priority execute "print" iff <= thread's current logging level
         *  @param print function to generate formatted log data
         */
        template < class T >
        void msg ( const RsrcKfc & rsrc, const Logger & logger, LogLevel priority, T print ) const
        {
            if ( priority >= LOG_EMERG && priority <= getThreshold () )
            {
                BufferedFmt fmt ( rsrc, log -> msg ( rsrc, logger, priority ) );
                print ( fmt );
            }
        }

        /**
         * msg
         *  @brief evaluates a log-level and executes provided function if applicable
         *  @param rsrc resource manager for buffered formatter
         *  @param logger logging output resource
         *  @param priority execute "print" iff <= thread's current logging level
         *  @param zstr a line of text to print to log
         */
        void msg ( const RsrcKfc & rsrc, const Logger & logger, LogLevel priority, const UTF8 * zstr ) const
        {
            if ( priority >= LOG_EMERG && priority <= getThreshold () )
            {
                BufferedFmt fmt ( rsrc, log -> msg ( rsrc, logger, priority ) );
                fmt << zstr << endm;
            }
        }

        /**
         * msg
         *  @brief evaluates a log-level and executes provided function if applicable
         *  @param rsrc resource manager for buffered formatter
         *  @param logger logging output resource
         *  @param priority execute "print" iff <= thread's or queue's current logging level
         *  @param print function to generate formatted log data
         */
        template < class T >
        void msg ( const RsrcKfc & rsrc, const Logger & logger,
            const LogQueueId & qid, LogLevel priority, T print ) const
        {
            if ( priority >= LOG_EMERG )
            {
                if ( priority <= getThreshold () || priority <= getThreshold ( qid ) )
                {
                    BufferedFmt fmt ( rsrc, log -> msg ( rsrc, logger, qid, priority ) );
                    print ( fmt );
                }
            }
        }

        /**
         * msg
         *  @brief evaluates a log-level and executes provided function if applicable
         *  @param rsrc resource manager for buffered formatter
         *  @param logger logging output resource
         *  @param priority execute "print" iff <= thread's or queue's current logging level
         *  @param zstr a line of text to print to log
         */
        void msg ( const RsrcKfc & rsrc, const Logger & logger,
            const LogQueueId & qid, LogLevel priority, const UTF8 * zstr ) const
        {
            if ( priority >= LOG_EMERG )
            {
                if ( priority <= getThreshold () || priority <= getThreshold ( qid ) )
                {
                    BufferedFmt fmt ( rsrc, log -> msg ( rsrc, logger, qid, priority ) );
                    fmt << zstr << endm;
                }
            }
        }


        /*=================================================*
         *                       C++                       *
         *=================================================*/

        LogMgrRef clone () const;

        CXX_RULE_OF_EIGHT_EQOPS_NE ( LogMgr, log );

    private:

        LogMgrRef log;
        mutable LogLevel lvl;
    };
    


    /*=====================================================*
     *                     EXCEPTIONS                      *
     *=====================================================*/

}
