/***************************************************************************
                          SIMPLY POWERFUL TOOLKIT (SPTK)
                          CBaseLog.h  -  description
                             -------------------
    begin                : Mon Jan 30 2006
    copyright            : (C) 2001-2006 by Alexey Parshin
    email                : alexeyp@gmail.com

    This module creation was sponsored by Total Knowledge (http://www.total-knowledge.com).
    Author thanks the developers of CPPSERV project (http://www.total-knowledge.com/progs/cppserv)
    for defining the requirements for this class.
 ***************************************************************************/

/***************************************************************************
   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.

   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 Library
   General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; if not, write to the Free Software Foundation, 
   Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.

   Please report all bugs and problems to "alexeyp@gmail.com"
 ***************************************************************************/

#ifndef __CBASELOG_H__
#define __CBASELOG_H__

#include <stdio.h>
#include <time.h>

#include <sptk3/sptk.h>
#include <sptk3/CDateTime.h>
#include <sptk3/CException.h>
#include <iostream>
#include <streambuf>

#ifndef _WIN32
#include <syslog.h>
#else
/* priority codes */
#define	LOG_EMERG   0	/* system is unusable */
#define	LOG_ALERT   1	/* action must be taken immediately */
#define	LOG_CRIT    2	/* critical conditions */
#define	LOG_ERR     3	/* error conditions */
#define	LOG_WARNING 4	/* warning conditions */
#define	LOG_NOTICE  5	/* normal but significant condition */
#define	LOG_INFO    6	/* informational */
#define	LOG_DEBUG   7	/* debug-level messages */

/* facility codes */
#define	LOG_KERN		(0<<3)	/* kernel messages */
#define	LOG_USER		(1<<3)	/* random user-level messages */
#define	LOG_MAIL		(2<<3)	/* mail system */
#define	LOG_DAEMON	    (3<<3)	/* system daemons */
#define	LOG_AUTH		(4<<3)	/* security/authorization messages */
#define	LOG_SYSLOG	    (5<<3)	/* messages generated internally by syslogd */
#define	LOG_LPR		    (6<<3)	/* line printer subsystem */
#define	LOG_NEWS		(7<<3)	/* network news subsystem */
#define	LOG_UUCP		(8<<3)	/* UUCP subsystem */
#define	LOG_CRON		(9<<3)	/* clock daemon */
#define	LOG_AUTHPRIV    (10<<3)	/* security/authorization messages (private) */
#define	LOG_FTP		    (11<<3)	/* ftp daemon */

#endif

namespace sptk {

   /// @addtogroup log Log Classes
   /// @{

	class CBaseLog;

   /// Log message priority
	class CLogPriority {
		unsigned 	m_id;		///< Priority id
		std::string	m_name;	///< Priority name

	private:

      /// @brief Private copy constructor
      ///
      /// We shouldn't copy the priorities.
      /// @param prt const CLogPriority&, a log priority object to copy from
		CLogPriority(const CLogPriority& prt) {}

	public:

      /// @brief Constructor
      ///
      /// Contructs a log priority object
      /// @param id unsigned, a priority id
      /// @param name const char *, a priority name
		CLogPriority(unsigned id,const char* name) {
			m_id = id;
			m_name = name;
		}

      /// @brief Returns priority id
		unsigned id() const { return m_id; }

      /// @brief Returns priority name
		std::string name() const { return m_name; }
	};

	extern const CLogPriority 
		CLP_DEBUG,     ///< Debug message priority
		CLP_INFO,      ///< Information message priority
		CLP_NOTICE,    ///< Notice message priority
		CLP_WARNING,   ///< Warning message priority
		CLP_ERROR,     ///< Error message priority
		CLP_CRITICAL,  ///< Critical message priority
		CLP_ALERT,     ///< Alert message priority
		CLP_PANIC;     ///< Panic message priority

   /// Internal buffer for the CLogStream class
	class CLogStreamBuf : public std::streambuf {
		friend class CBaseLog;
	private:
		char          *m_buffer;	      ///< Internal buffer to store the current log message 
		unsigned       m_size;		      ///< The size of the internal buffer
		unsigned       m_bytes;		      ///< The number of characters in the buffer
		CDateTime      m_date;		      ///< Message timestamp
		const CLogPriority *m_priority;	///< Current message priority, should be defined for every message or CLP_DEBUG
		CBaseLog      *m_parent;	      ///< Parent log object

	protected:
      /// @brief Assignes the parent log object
   ///
      /// @param par CBaseLog *, parent log object
		void parent(CBaseLog *par) { m_parent = par; }

      /// Overwritten virtual method for std::streambuf
      /// @param c int_type, a character sent to the stream on overflow
		virtual int_type overflow(int_type c);

	public:
      /// @brief Constructor
      ///
      /// Constructs a buffer for CBaseLog.
		CLogStreamBuf();

      /// @brief Destructor
      /// 
      /// Sends the remaining part of the message to the log,
      /// then releases the allocated memory
		~CLogStreamBuf() { flush(); free(m_buffer); }

      /// @brief Flushes message buffer
      ///
      /// Sends the remaining part of the message to the log
		void flush() { overflow(char(13)); }

      /// @brief Sets current message priority
      ///
      /// @param prt const CLogPriority&, current message priority
		void priority(const CLogPriority& prt) { m_priority = &prt; }
	};

   /// Class name alias for std::ostream, to make M$ VC-- compile the the class
	typedef std::ostream _ostream; 

   /// Class name alias for std::ios, to make M$ VC-- compile the the class
	typedef std::ios     _ios; 

	class CFunctionLogger;

   /// Base class for all log classes
	class CBaseLog : public _ostream {
		friend class CFunctionLogger;
		friend class CLogStreamBuf;
	protected:
		CLogStreamBuf m_buffer;	///< Log buffer
		int           m_indent; ///< Text indent

      /// @brief Stores or sends log message to actual destination
      ///
      /// This method should be overwritten by the actual log implementation
      /// @param date CDateTime, message timestamp
      /// @param message const char *, message text
      /// @param sz unsigned, message size
      /// @param priority CLogPriority, message priority. @see CLogPriority for more information.
		virtual void saveMessage(CDateTime date,const char *message,unsigned sz,const CLogPriority *priority) throw(CException) {}

	protected:
      /// @brief Protected constructor
      ///
      /// Creates an empty log object. You should never try to instantiate this class.
      /// CBaseLog can only be used as a base class for log classes that implement at least
      /// saveMessage() method.
		CBaseLog() : _ios(0), _ostream(&m_buffer) { m_buffer.parent(this); m_indent = 0; }
	public:

      /// @brief Destructor
      ///
      /// Flushes the log and releases any allocated resources
		virtual ~CBaseLog() { flush(); }

      /// @brief Sets current message priority
      ///
      /// @param prt const CLogPriority&, current message priority
		void priority(const CLogPriority& prt) { m_buffer.priority(prt); }

      /// @brief Writes a string to a log (deprecated)
      ///
      /// This method is provided for the backward compatibility only.
      /// Please use << operators instead
		void writeString(std::string text) throw(CException) { *this << text.c_str() << std::endl; } 

      /// @brief Restarts the log, if applicable
      ///
      /// In CBaseLog it does nothing
		virtual void reset() throw(CException) {}
	}; 

   /// @brief Function logger
   ///
   /// Designed to be placed to the beginning of function or method as an automatic (stack) object.
   /// Creates a log message 'XYZ started' upon creation.
   /// Creates another log message 'XYZ exits' upon destruction. 
	class CFunctionLogger {
		CBaseLog&    m_log;           ///< Log object to send messages to
		std::string  m_functionName;  ///< Function or method name
		CDateTime    m_started;       ///< The datetime of the start
		static bool  m_quietMode;     ///< If true, don't print start-end messages
	public:
      /// Constructor
      /// @param log CBaseLog&, Log object to send messages to
      /// @param functionName std::string, function or method name
		CFunctionLogger(CBaseLog& log,std::string functionName);

      /// Destructor
		~CFunctionLogger();

      /// Sets quiet mode (static method)
      /// @param quietMode bool, if true - don't print start-end messages
		static void quiet(bool quietMode) { m_quietMode = quietMode; }

      /// Returns current quiet mode (static method)
		static bool quiet() { return m_quietMode; }
	};

   /// @}
}

   /// @brief Sets the message priority
   ///
   /// By default, the message priority is CLP_DEBUG.
   /// Changing the priority would hold till the new log message.
   /// The new message would start with the default priority.
sptk::CBaseLog &operator<< (sptk::CBaseLog &, sptk::CLogPriority const &);

#endif
