#ifndef NLP_SERVICE_LOGGER_H
#define NLP_SERVICE_LOGGER_H

#include <boost/shared_ptr.hpp>

#include <log4cxx/logger.h>
#include <log4cxx/level.h>
#include <log4cxx/patternlayout.h>

namespace nlp_service {

enum logging_lvl {TRACE_LVL, DEBUG_LVL, INFO_LVL, WARNING_LVL, ERROR_LVL};

/* Wrapper class for logging. As we want the logging to be easy and unified
 * for both main logs (service-wise) and task specific logs.
 * Logger level is the one provided in command line arguments (default INFO_LVL).
 * Possible levels: TRACE_LVL, DEBUG_LVL, INFO_LVL, WARNING_LVL, ERROR_LVL
 */
class ServiceLogger {
public:
	ServiceLogger(logging_lvl level, std::string main_log_path, std::string task_logs_path, bool log_to_console = false);
	~ServiceLogger();

	/* Logs message with certain level to the main log file (default ./service.log,
	 * can be changed via command line arguments).
	 */
	void log(logging_lvl level, const std::string& message);

	/* Logs exception with certain level to the main log file (default ./service.log,
	 * can be changed via command line arguments).
	 */
	// TODO: save backtrace in exceptions
	inline void log(logging_lvl level, const std::exception& exception)
	{
		this->log(level, exception.what());
	}

	/* Logs message with certain level to the task log file. Task log files are
	 * stored in the directory specified by config file (error_dir).
	 * The message is also logged in main log file for convinience.
	 */
	void task_log(std::string task_token, logging_lvl level, const std::string& message);

	/* Logs exception with certain level to the task log file. Task log files are
	 * stored in the directory specified by config file (error_dir).
	 * * The message is also logged in main log file for convinience.
	 */
	// TODO: save backtrace in exceptions
	inline void task_log(std::string task_token, logging_lvl level, const std::exception& exception)
	{
		this->task_log(task_token, level, exception.what());
	}

	// Shortcut functions
	inline void trace(const std::string& message);
	inline void debug(const std::string& message);
	inline void info(const std::string& message);
	inline void warning(const std::string& message);
	inline void error(const std::string& message);

	inline void trace(const std::exception& exception);
	inline void debug(const std::exception& exception);
	inline void info(const std::exception& exception);
	inline void warning(const std::exception& exception);
	inline void error(const std::exception& exception);

	inline void task_trace(const std::string& task_token, const std::string& message);
	inline void task_debug(const std::string& task_token, const std::string& message);
	inline void task_info(const std::string& task_token, const std::string& message);
	inline void task_warning(const std::string& task_token, const std::string& message);
	inline void task_error(const std::string& task_token, const std::string& message);

	inline void task_trace(const std::string& task_token, const std::exception& exception);
	inline void task_debug(const std::string& task_token, const std::exception& exception);
	inline void task_info(const std::string& task_token, const std::exception& exception);
	inline void task_warning(const std::string& task_token, const std::exception& exception);
	inline void task_error(const std::string& task_token, const std::exception& exception);

	/* Function for closing task-specific log files
	 * Ugly hack, but I have no idea how to tell log4cxx to close the logger */
	void close_task_log(const std::string& task_token);

	/* Functions to get logfiles paths.*/
	std::string get_main_log_path()
	{
		return this->main_log_path;
	}
	std::string get_task_logs_path()
	{
		return this->task_logs_path;
	}

	/* Utility function to translate strings into logging levels.
	 * It's used when parsing data from the command line.
	 */
	static logging_lvl str2logging_lvl(std::string str_level);

private:
	void log(log4cxx::LoggerPtr logger, logging_lvl level, const std::string& message);
	log4cxx::LoggerPtr get_main_logger(const std::string& main_log_path, bool log_to_console);
	log4cxx::LoggerPtr get_task_logger(const std::string& task_token);
	log4cxx::LoggerPtr get_logger(const std::string& name, const std::string& filename);

	bool check_logger_init(const std::string& logger_name);

	log4cxx::LevelPtr translate_level(logging_lvl level);

	const std::string main_log_path;
	const std::string task_logs_path;

	log4cxx::LoggerPtr main_logger;
	log4cxx::LevelPtr default_logging_level;
	log4cxx::LayoutPtr default_layout;

	static std::string PATTERN_STRING;
};

typedef boost::shared_ptr<ServiceLogger> ServiceLoggerPtr;

// All the shortcut functions definitions
void ServiceLogger::trace(const std::string& message)
{
	this->log(TRACE_LVL, message);
}
void ServiceLogger::debug(const std::string& message)
{
	this->log(DEBUG_LVL, message);
}
void ServiceLogger::info(const std::string& message)
{
	this->log(INFO_LVL, message);
}
void ServiceLogger::warning(const std::string& message)
{
	this->log(WARNING_LVL, message);
}
void ServiceLogger::error(const std::string& message)
{
	this->log(ERROR_LVL, message);
}

void ServiceLogger::trace(const std::exception& exception)
{
	this->log(TRACE_LVL, exception.what());
}
void ServiceLogger::debug(const std::exception& exception)
{
	this->log(DEBUG_LVL, exception.what());
}
void ServiceLogger::info(const std::exception& exception)
{
	this->log(INFO_LVL, exception.what());
}
void ServiceLogger::warning(const std::exception& exception)
{
	this->log(WARNING_LVL, exception.what());
}
void ServiceLogger::error(const std::exception& exception)
{
	this->log(ERROR_LVL, exception.what());
}

void ServiceLogger::task_trace(const std::string& task_token, const std::string& message)
{
	this->task_log(task_token, TRACE_LVL, message);
}
void ServiceLogger::task_debug(const std::string& task_token, const std::string& message)
{
	this->task_log(task_token, DEBUG_LVL, message);
}
void ServiceLogger::task_info(const std::string& task_token, const std::string& message)
{
	this->task_log(task_token, INFO_LVL, message);
}
void ServiceLogger::task_warning(const std::string& task_token, const std::string& message)
{
	this->task_log(task_token, WARNING_LVL, message);
}
void ServiceLogger::task_error(const std::string& task_token, const std::string& message)
{
	this->task_log(task_token, ERROR_LVL, message);
}

void ServiceLogger::task_trace(const std::string& task_token, const std::exception& exception)
{
	this->task_log(task_token, TRACE_LVL, exception.what());
}
void ServiceLogger::task_debug(const std::string& task_token, const std::exception& exception)
{
	this->task_log(task_token, DEBUG_LVL, exception.what());
}
void ServiceLogger::task_info(const std::string& task_token, const std::exception& exception)
{
	this->task_log(task_token, INFO_LVL, exception.what());
}
void ServiceLogger::task_warning(const std::string& task_token, const std::exception& exception)
{
	this->task_log(task_token, WARNING_LVL, exception.what());
}
void ServiceLogger::task_error(const std::string& task_token, const std::exception& exception)
{
	this->task_log(task_token, ERROR_LVL, exception.what());
}

}	// end namespace nlp_service

#endif // NLP_SERVICE_LOGGER_H
