/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef _LOG4CXX_HELPERS_LOG_LOG_H
#define _LOG4CXX_HELPERS_LOG_LOG_H

#include <log4cxx/logstring.h>
#include <log4cxx/logger.h>
#include <log4cxx/helpers/widelife.h>
#include <exception>
#include <mutex>

namespace LOG4CXX_NS
{
namespace helpers
{
/**
This class used to output log statements from within the log4cxx package.

<p>Log4cxx components cannot make log4cxx logging calls. However, it is
sometimes useful for the user to learn about what log4cxx is
doing. You can enable log4cxx internal debug logging by calling the
<b>#setInternalDebugging</b> method.

<p>All LogLog messages are written to SystemErrWriter
prepended with the string "log4cxx: ".
*/
class LOG4CXX_EXPORT LogLog
{
	private:
		LOG4CXX_DECLARE_PRIVATE_MEMBER_PTR(LogLogPrivate, m_priv)

		friend WideLife<LogLog>;
		LogLog();
		LogLog(const LogLog&);
		LogLog& operator=(const LogLog&);
		static LogLog& getInstance();

	public:
		~LogLog();

		/**
		 *  Are debug messages sent to SystemErrWriter?
		 **/
		static bool isDebugEnabled();

		/**
		 *  Are \c category debug messages sent to SystemErrWriter?
		 **/
		static bool isDebugEnabledFor(const LoggerPtr& category);

		/**
		 *  Are \c category trace messages sent to SystemErrWriter?
		 **/
		static bool isTraceEnabledFor(const LoggerPtr& category);

		/**
		Start/stop outputing debug messages if \c newValue is true/false respectively.
		*/
		static void setInternalDebugging(bool newValue);

		/**
		 *  Are messages output in color?
		 **/
		static bool isColorEnabled();

		/**
		Start/stop coloring message text if \c newValue is true/false respectively.
		*/
		static void setColorEnabled(bool newValue);

		/**
		Output \c msg to SystemErrWriter if internal debug logging is enabled.
		*/
		static void debug(const LogString& msg);
		/**
		Output \c msg and <code>ex.what()</code> to SystemErrWriter if internal debug logging is enabled.
		*/
		static void debug(const LogString& msg, const std::exception& e);


		/**
		Output \c msg to SystemErrWriter unconditionally.
		*/
		static void error(const LogString& msg);
		/**
		Output \c msg and <code>ex.what()</code> to SystemErrWriter unconditionally.
		*/
		static void error(const LogString& msg, const std::exception& ex);

		/**
		Output \c msg to SystemErrWriter if internal debug logging is enabled.
		*/
		static void trace(const LoggerPtr& category, const LogString& msg);

#if !LOG4CXX_LOGCHAR_IS_UTF8
		/**
		Output \c msg to SystemErrWriter if internal debug logging is enabled.
		*/
		static void trace(const LoggerPtr& category, const std::string& msg);
#endif
		/**
		Change quiet mode to \c newValue.

		In quiet mode LogLog generates strictly no output, not even
		for errors.

		@param newValue <code>true</code> for no output.
		*/
		static void setQuietMode(bool newValue);

		/**
		Output \c msg to SystemErrWriter unconditionally.
		*/
		static void warn(const LogString&  msg);
		/**
		Output \c msg and <code>ex.what()</code> to SystemErrWriter unconditionally.
		*/
		static void warn(const LogString&  msg, const std::exception& ex);

	private:
		static void emit_log(const LogString& prefix, const LogString& msg, const LogString& suffix);
		static void emit_log(const LogString& prefix, const std::exception& ex, const LogString& suffix);
};
}  // namespace helpers
} // namespace log4cxx

#if defined(LOGLOG_THRESHOLD) && LOGLOG_THRESHOLD <= 10000
/**
Send \c message to helpers::SystemErrWriter if \c logger is enabled for <code>DEBUG</code> events.

\usage
~~~{.cpp}
LOGLOG_DEBUG(m_log, "setRemote:"
	<< " hostAddress " << address->getHostAddress()
	<< " port " << port
	<< " inetType " << m_inetType
	<< " ipVersion " << m_inetFamily
	);
~~~

@param logger the logger that has the enabled status.
@param message a valid parameter to an <code>operator<<(std::ostream&. ...)</code> overload.
*/
#define LOGLOG_DEBUG(logger, message) { \
		if (LOG4CXX_NS::helpers::LogLog::isDebugEnabledFor(logger)) { \
			LOG4CXX_NS::helpers::MessageBuffer buf; \
			LOG4CXX_NS::helpers::LogLog::trace(logger, buf.str(buf << message)); } }
#else // !defined(LOGLOG_THRESHOLD) || 10000 < LOGLOG_THRESHOLD
#define LOGLOG_DEBUG(logger, message)
#endif

#if defined(LOGLOG_THRESHOLD) && LOGLOG_THRESHOLD <= 5000
/**
Send \c message to helpers::SystemErrWriter if \c logger is enabled for <code>TRACE</code> events.

\usage
~~~{.cpp}
LOGLOG_TRACE(m_log, "write:"
	<< " byteCount " << byteCount
	<< " to " << *m_remoteAddress
	<< "\n" << MessageData(pMessage, byteCount)
	);
~~~

@param logger the logger that has the enabled status.
@param message a valid parameter to an <code>operator<<(std::ostream&. ...)</code> overload.
*/
#define LOGLOG_TRACE(logger, message) { \
		if (LOG4CXX_NS::helpers::LogLog::isTraceEnabledFor(logger)) { \
			LOG4CXX_NS::helpers::MessageBuffer buf; \
			LOG4CXX_NS::helpers::LogLog::trace(logger, buf.str(buf << message)); } }
#else // !defined(LOGLOG_THRESHOLD) || 5000 < LOGLOG_THRESHOLD
#define LOGLOG_TRACE(logger, message)
#endif

/**
Send \c message to helpers::SystemErrWriter using the configured warning output style.
@param message a valid parameter to an <code>operator<<(std::ostream&. ...)</code> overload.
*/
#define LOGLOG_WARN(message) { \
		LOG4CXX_NS::helpers::MessageBuffer buf; \
		LOG4CXX_NS::helpers::LogLog::warn(buf.str(buf << message)); }

/**
Send \c message to helpers::SystemErrWriter using the configured error output style.
@param message a valid parameter to an <code>operator<<(std::ostream&. ...)</code> overload.
*/
#define LOGLOG_ERROR(message) { \
		LOG4CXX_NS::helpers::MessageBuffer buf; \
		LOG4CXX_NS::helpers::LogLog::error(buf.str(buf << message)); }

#endif //_LOG4CXX_HELPERS_LOG_LOG_H
