// -*- c++ -*-
//------------------------------------------------------------------------------
//                            Logger.h
//------------------------------------------------------------------------------
//  Copyright (c) 2001 by Vladislav Grinchenko
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Library General Public
//  License as published by the Free Software Foundation; either
//  version 2 of the License, or (at your option) any later version.
//------------------------------------------------------------------------------
#ifndef LOGGER_H
#define LOGGER_H

//System headers
#include <sys/types.h>
#include <string.h>
#include <stdio.h>

#include <string>
#include <stack>

using std::string;
using std::stack;

//ASSA headers

#include "assa/Logger_Impl.h"
#include "assa/Singleton.h"
#include "assa/MemDump.h"

namespace ASSA {				// @start namespace ASSA

/** @file Logger.h 

	An abstraction to message logging facility.
*/

class Reactor;

/******************************************************************************
  Class Logger
******************************************************************************/ 

class Logger : public Singleton<Logger>
{
public:
    Logger () : m_impl (NULL), m_app_name ("zombie") { /* no-op */ }
    ~Logger () { this->log_close (); }

public:
	/** Set application name. This should be the first call made
	 *  to the newly created Logger object.
	 */
	void set_app_name (const std::string& appname_) { m_app_name = appname_; }

	/** Enable logging for group g_.
	 *  @param g_ Group to enable
	 */
    void enable_group  (Group g_);

	/** Disable logging for group g_.
	 *	@param g_ Group to disable
	 */
    void disable_group (Group g_);

	/** Enable logging for groups_.
	 *  @param groups_ Groups to enable
	 */
    void enable_groups  (u_long groups_);

	/** Disable logging for groups_.
	 *	@param groups_ Groups to disable
	 */
    void disable_groups (u_long groups_);

    void enable_all_groups  (void);
    void disable_all_groups (void);

    bool group_enabled (Group g_) const;

    /// Add optional timezone: GMT vs. Local
    void enable_timestamp (void);
    void disable_timestamp (void);
    bool timestamp_enabled (void) const;

    /// 0 - GMT, 1 - LOCAL
    void set_timezone (int zone);

    void sign_on (const string& func_name_);
    void sign_off (void);

    /** Write log messages to standard output
	 *  @return 0 on success, -1 on error with errno set
	 */
    int log_open (u_long groups_);

    /** Write log messages to the logfile
		@param logfiname_ Name of the logfile
		@param groups_ Logging masks enabled
		@param maxsize_ Maximum size of the logfile
		@return 0 on success, -1 on error with errno set
	 */
    int log_open (const char* logfname_,
				  u_long groups_, u_long maxsize_);

    /** Write log messages to the log server assa-logd.

		@param logsvr_    Address of assa-logd (assalogd@hostname)
		@param logfiname_ Name of the logfile
		@param groups_    Logging masks enabled
		@param maxsize_   Maximum size of the logfile
		@param reactor_   Pointer to the Reactor to use for communication.

		@return: 0 on success; -1 on error with errno set
	*/
 	int log_open (const std::string& logsvr_, const char* logfname_,
				  u_long groups_, u_long maxsize_, Reactor* reactor_);

    void log_resync (void);
    int  log_close (void);

    int log_msg  (u_long g_, const char* fmt_, ...);
	int log_func (u_long g_, marker_t type_);

private:
    Logger_Impl*      m_impl;	/// Logger implementation
    stack<string>  m_context;	/// Stack of all contexts
	std::string   m_app_name;	/// Application name (+instance number)
};

/** @def LOGGER
 *  A shortcut to locate a singleton object of class Logger
 */
#define LOGGER ASSA::Logger::getInstance()

/*******************************************************************************
  Inline functions
*******************************************************************************/

inline void 
Logger::
enable_group (Group g_) 
{ 
    if (m_impl) {
		m_impl->enable_group (g_);
    }
}

inline void 
Logger::
disable_group (Group g_)
{
    if (m_impl) {
		m_impl->disable_group (g_);
    }
}

inline void 
Logger::
enable_groups (u_long g_) 
{ 
    if (m_impl) {
		m_impl->enable_groups (g_);
    }
}

inline void 
Logger::
disable_groups (u_long g_)
{
    if (m_impl) {
		m_impl->disable_groups (g_);
    }
}

inline bool 
Logger::
group_enabled (Group g_) const
{
    return (m_impl) ? m_impl->group_enabled (g_) : false;
}

inline void 
Logger::
enable_all_groups  (void)
{
    if (m_impl) {
		m_impl->enable_all_groups ();
    }
}

inline void 
Logger::
disable_all_groups (void)
{
    if (m_impl) {
		m_impl->disable_all_groups ();
    }
}

inline void 
Logger::
enable_timestamp (void)
{
    if (m_impl) {
		m_impl->enable_timestamp ();
    }
}

inline void 
Logger::
disable_timestamp (void)
{
    if (m_impl) {
		m_impl->disable_timestamp ();
    }
}

inline bool 
Logger::
timestamp_enabled (void) const
{
    return (m_impl) ? m_impl->timestamp_enabled () : false;
}

inline void 
Logger::
set_timezone (int zone_)
{
    if (m_impl) {
		m_impl->set_timezone (zone_);
    }
}

inline void 
Logger::
log_resync (void) 
{
    if (m_impl) {
		m_impl->log_resync ();
    }
}

inline void 
Logger::
sign_on (const string& func_name_)
{
    m_context.push (func_name_);
}

inline void 
Logger::
sign_off (void)
{
    if (!m_context.empty ()) {
		m_context.pop ();
    }
}

/*******************************************************************************
  Macro definition shortcuts
*******************************************************************************/

/** @def DL(X)
	A macro for writing debug message to the Logger.
*/

#if defined (ASSA_NLOGGING)
#  define DL(X) do {} while (0)
#else
#  define DL(X) \
     do { \
       LOGGER->log_msg X; \
     } while (0)
#endif

/** @def EL(X)
	A macro for writing error message to the Logger.
*/
#if defined (ASSA_NLOGGING)
#  define EL(X) do {} while (0)
#else
#  define EL(X) \
     do { \
       LOGGER->log_msg X; \
       LOGGER->log_msg(ASSA::ERROR,"errno: %d \"%s\"\n", errno, strerror(errno)); \
     } while (0)
#endif

/*******************************************************************************
  Namespace Log provides only the most plausable interface.
  Other public functions can be reached via LOGGER
*******************************************************************************/

namespace Log {					// @start namespace Log

	/**
	 * Set application name. Call this function before any other
	 * to have application name set properly. This is especially
	 * important if you plan to call open_log_server() to send messages
	 * to the log server, assa-logd.
	 */
	inline void set_app_name (const std::string& appname_)
	{
		LOGGER->set_app_name (appname_);
	}

    /**
	 * Open log file.
	 *
	 * @param logfname_ File name
	 * @param groups_ ORed bitmask of logging groups as defined in
	 *                assa/LogMask.h. Default is to log all groups.
	 * @param maxsize_ Maximum size log file can reach before renamed to
	 *                 logfilename_0. Default is 10 megabytes.
	 */
    inline int open_log_file (const char* logfname_, 
							  u_long groups_ = ALL, 
							  u_long maxsize_ = 10485760) 
    {
		return LOGGER->log_open (logfname_, groups_, maxsize_);
    }

    /// Write log message to standard output
    inline int open_log_stdout (u_long groups_ = ALL)
    {
		return LOGGER->log_open (groups_);
    }

    /** 
	 * Open connection with and write log message to the log server.
	 *
	 * @param logsvraddr_ Address of the logserver, assa-logd in form
	 *                    port@host
	 * @param logfname_   Log file path
	 * @param reactor_    Reactor to use for communications.
	 * @param groups_     ORed bitmask of logging groups as defined in
	 *                    LogMask.h. Default is to log all groups.
	 * @param maxsize_    Maximum size log file can reach before renamed to
	 *                    logfilename_0. Default is 10Mb.
	 */
	inline int open_log_server (const std::string& logsvraddr_,
								const char*          logfname_, 
								Reactor*              reactor_,
								u_long     groups_ = ASSA::ALL,
								u_long     maxsize_ = 10485760)
	{
		int ret = LOGGER->log_open (logsvraddr_, logfname_, groups_, 
									maxsize_, reactor_);
		return ret;
	}

    /// Resynchronize logging stream after Fork
    inline void log_resync (void) {	LOGGER->log_resync (); }
    
    /// Close logging stream
    inline int log_close (void) { return LOGGER->log_close (); }

	/// Set timezone to GMT
    inline void set_gmt_timezone (void) { LOGGER->set_timezone (0); }

	/// Enable timestamp logging
    inline void enable_timestamp (void) { LOGGER->enable_timestamp (); }

	/// Disable timestamp logging
    inline void disable_timestamp (void) { LOGGER->disable_timestamp (); }	

} // @end namespace Log

//------------------------------------------------------------------------------
// DiagnosticContext
//------------------------------------------------------------------------------

/** Class DiagnosticContext tracks who deep a function is in the calling stack.
 */

class DiagnosticContext
{
public: 
	DiagnosticContext (const char* fn_, u_long mask_ = TRACE);
	~DiagnosticContext ();

private:
	DiagnosticContext (const DiagnosticContext&);
	DiagnosticContext& operator= (const DiagnosticContext&);

private:
	const char*  m_fname;
	u_long       m_mask;
};

inline
DiagnosticContext::
DiagnosticContext (const char* fn_, u_long mask_)
	: m_fname (fn_), m_mask (mask_)
{
	if (LOGGER->group_enabled ((ASSA::Group) m_mask)) {
		LOGGER->sign_on (m_fname);
		LOGGER->log_func (m_mask, FUNC_ENTRY);
	}
}

inline 
DiagnosticContext::
~DiagnosticContext ()
{
	if (LOGGER->group_enabled ((ASSA::Group) m_mask)) {
		LOGGER->log_func (m_mask, FUNC_EXIT);
		LOGGER->sign_off ();
	}
}

/** @def trace(s)

    trace() is used to trace function call chain in C++ program.

    trace() logs a message of type [function_name]  --v-- with log
    level TRACE, where 'function_name' is the argument supplied.
    Upon exiting from the function, record
    [function_name] --^-- is written to the log file.
    
    Application user can easily turn trace on/off by supplying
    different debug mask with {'-m', '--mask'} command-line argument 
    and masking out highest bit in debug mask: TRACE
*/

#define trace(s) ASSA::DiagnosticContext tRaCeR(s);

/** @def trace_with_mask(s, m)

    trace_with_mask() is used to trace function call chain in C++ program.
    Mask is used to limit the scope of the messages.
*/

#define trace_with_mask(s, m) ASSA::DiagnosticContext tRaCeR(s, m);

} // @end namespace ASSA

#endif /* LOGGER_H */
