#ifndef CONSOLE_H
#define CONSOLE_H

#include <GSTenums.h>
#include <boost/serialization/singleton.hpp>
#include <buildspec.h>
#include <string>
using boost::serialization::singleton;
#include <boost/filesystem.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/thread.hpp>

namespace GST
{
namespace Utils
{

class ILogger;
class FileLogger;
class TerminalLogger;

//------------------------------------------------------------------------------

/**
	* This class allows to print output.
	* Console is a singleton object, that means there is one global instance
	* that can be accessed through getInstance().
	*
	* The class has a set of static methods that internally gets the Console
	* instance and run the method thread safe. E.g. the following example
	* can be called from anywhere (also from a thread) to report to all
   registered
	* ILoggers a message:
	\code
	Console::SendStatus("Hello World!");
	\endcode
	*
	* By default Console manages two ILogger's internally:
	* -# A FileLogger
	* -# A TerminalLogger
	*
	* Activate them using
	* -# SetLogFilePath()
	* -# SetWriteToTerminal()
	*
	* You can attach your own custom logger using the AddLogger() method.
   Therefore
	* simply implement the ILogger interface. Remark the notes
	* \ref changelogNewLogger here.
	* <b>When dropping or deleting your custom logger call DropLogger() first,
	* to prevent NULL pointer exceptions when a log is fired after deletion.</b>
	*
	* You can control the message types that Console is logging by the use of
	* SetLogLevel(). By default Console uses LOG_PUBLIC (which is a makro for
	* <b>LOG_ERROR | LOG_WARNING | LOG_STATUS</b>, see GSTenums.h ).
	*
	* When you are using the GST API we suggest to set the LogLevel to
	* LOG_ALL and implement you own Logger forwarding DEBUG and SQL messages
	* to the resource of you prefer. (That helps to to indicated what was
	* done when debugging).
	*/
class Console : singleton<Console>
{
public:
	GST_API_EXPORT Console();
	GST_API_EXPORT ~Console();

	/** @returns (The global) instance of Console
	 *
	 * @note This method is thread safe.
	 */
	GST_API_EXPORT static Console *getInstance();

	/**
	 * Sends a message to Console (of type status)
	 *
	 * @note This method is thread safe.
	 */
	GST_API_EXPORT static void Send(const std::string &Output);

	/**
	 * Sends a message to Console (of type warning)
	 *
	 * @note This method is thread safe.
	 */
	GST_API_EXPORT static void SendWarning(const std::string &Output);

	/**
	 * Sends a message to Console (of type error)
	 *
	 * @note This method is thread safe.
	 */
	GST_API_EXPORT static void SendError(const std::string &Output);

	/**
	 * Sends a message to Console (of type status)
	 *
	 * @note This method is thread safe.
	 */
	GST_API_EXPORT static void SendStatus(const std::string &Output);

	/**
	 * Sends a message to Console (of type debug)
	 *
	 * @note This method is thread safe.
	 */
	GST_API_EXPORT static void SendDebug(const std::string &Output);

	/**
	 * Logs and shrink sql statement (of type debug)
	 *
	 * @see SetSQLLogLength()
	 * @note This method is thread safe.
	 */
	GST_API_EXPORT static void SendPID(const long &pid, const std::string &sql);

	/**
	 * Writes version and revision of build to console (in type status)
	 *
	 * @note This method is thread safe.
	 */
	GST_API_EXPORT static void SendBuildInfo();

	/**
	 * @returns the log level
	 *
	 * @note This method is _not_ thread safe.	(Take care that no other
	 *       thread is using console at the same time when calling this method.)
	 * @see GetLogLevel()
	 * @note This method is thread safe.
	 */
	GST_API_EXPORT static Utils::LogLevel GetLogLevel();

	/**
	 * Set the log log level
	 *
	 * @note This method is thread safe.
	 */
	GST_API_EXPORT static void SetLogLevel(Utils::LogLevel level);

	/**
	 * True if log level is at least Utils::LOG_DEBUG (or more detailed)
	 *
	 * @note This method is _not_ thread safe.	(Take care that no other
	 *       thread is using console at the same time when calling this
	 *       method.)
	 * @note This method is thread safe.
	 */
	GST_API_EXPORT static bool IsLoggingDebugMessages();

	/**
	 * Adds a logger. Use this to implement you own ILogger. All
	 * information send to Console are written to logger. If you
	 * delete your ILogger instance, provide disable writing to
	 * by passing DropLogger(<your pointer>) before deleting your
	 * resource.
	 *
	 * @note Console will _not_ take the owner of logger.
	 * @note This method is thread safe.
	 */
	GST_API_EXPORT static void AddLogger(ILogger *logger);

	/**
	 * When you delete your ILogger resource drop the logger
	 * with pointer to your ILogger _before_ deleting
	 * your resource.
	 * Use this method when you want to stop writing onto your logger.
	 *
	 * @note This method is thread safe.
	 */
	GST_API_EXPORT static void DropLogger(ILogger *logger);

	/**
	 * @returns number of chars in a logged sql statement (default: 1000)
	 * @note This method is thread safe.
	 */
	GST_API_EXPORT static long GetSQLLogLength();

	/**
	 * set the number of chars in a logged sql statement.
	 *
	 * A number <= 0 log the complete statements
	 * @note This method is thread safe.
	 */
	GST_API_EXPORT static void SetSQLLogLength(long val);

	/**
	 * Construct a FileLogger and append it to the console loggers.
	 * This (global) console is the owner of the FileLogger (m_fileLogger).
	 *
	 * If filePath is empty file logging is disabled.
	 * @note This method is thread safe.
	 */
	GST_API_EXPORT static void SetLogFilePath(const std::string &filePath);

	/**
	 * If this instance of Console is owning a FileLogger (set via
	 * SetLogFilePath()), then this returns the filepath.
	 * Return an empty string otherwise.
	 * @note This method is thread safe.
	 */
	GST_API_EXPORT static std::string GetLogFilePath();

	/**
	 * If writeToTerminal set to true: Construct a TerminalLogger and
	 * append it to the console loggers.
	 *
	 * If writeToTerminal set to false: Drops the TerminalLogger from
	 * list of loggers.
	 *
	 * This (global) console is the owner of the TerminalLogger (m_fileLogger).
	 * @note This method is thread safe.
	 */
	GST_API_EXPORT static void SetWriteToTerminal(bool writeToTerminal = true);

	/**
	 * Specify the terminal logger logging behavior. Use this to filter terminal
	 * logging e.g.
	 * <li> just to Debug messages: LOG_DEBUG </li>
	 * <li> just to Debug and Error messages: LOG_DEBUG | LOG_ERROR </li>
	 *
	 * To control the whole console behavior (reporting messages to all logger
	 * not just to terminal logger use SetLogLevel)
	 * @see Console::SetLogLevel()
	 */
	GST_API_EXPORT static void SetTerminalLogFilter(Utils::LogLevel logMode
													= LOG_ALL);

	/**
	 * Similar to SetTerminalLogFilter but set the filter to file loggger.
	 * @note Specify a file logger via Console::SetLogFilePath() first!
	 * @exception GSTRuntimeException is thrown when not file logger specified
	 * before.
	 */
	GST_API_EXPORT static void SetFileLogFilter(Utils::LogLevel logMode
												= LOG_ALL);

	///@name not thread safe (public interface)
	//@{
	GST_API_EXPORT void addLogger_impl(ILogger *logger);
	GST_API_EXPORT void dropLogger_impl(ILogger *logger);
	GST_API_EXPORT Utils::LogLevel getLogLevel_impl() const;
	GST_API_EXPORT void setLogLevel_impl(Utils::LogLevel val);
	GST_API_EXPORT bool isLoggingDebugMessages_impl() const;
	GST_API_EXPORT void setSQLLogLength_impl(long val);
	GST_API_EXPORT long getSQLLogLength_impl() const;
	GST_API_EXPORT void setLogFilePath_impl(const std::string &filePath);
	GST_API_EXPORT std::string getLogFilePath_impl() const;
	GST_API_EXPORT void setWriteToTerminal_impl(bool writeToTerminal);
	GST_API_EXPORT void setTerminalLogFilter_impl(Utils::LogLevel val);
	GST_API_EXPORT void setFileLogFilter_impl(Utils::LogLevel val);
	//@}

protected:
	///@name not thread safe
	//@{
	std::stringstream &stripSQL(const std::string &sql,
								std::stringstream &msg) const;
	void writeMessageToLoggers(const std::string &message, LogLevel ltype);
	void writePIDToLoggers(const long &pid, const std::string &sql);
	//@}

private:
	typedef std::vector<ILogger *> LoggerContainer;
	LoggerContainer m_logger;
	/**
	 * Optional Console can be the owner of a FileLogger
	 *
	 * @see	SetLogFilePath()
	 */
	FileLogger *m_fileLogger;
	/**
	 * Optional Console can be the owner of a TerminalLogger
	 *
	 * @see	SetLogFilePath()
	 */
	TerminalLogger *m_terminalLogger;

	static boost::mutex accessorMutex;

	///@name config members
	//@{
	Utils::LogLevel logLevels;
	long sqlLogLength;
	//@}
};

} // namespace Utils
} // namespace GST

#endif
