#ifndef RSPAMD_LOGGER_H
#define RSPAMD_LOGGER_H

#include "config.h"
#include "radix.h"
#include "util.h"

#ifdef __cplusplus
extern "C" {
#endif

#ifndef G_LOG_LEVEL_USER_SHIFT
#define G_LOG_LEVEL_USER_SHIFT 8
#endif

#define RSPAMD_LOG_ID_LEN 6

struct rspamd_config;

enum rspamd_log_flags {
	RSPAMD_LOG_FORCED = (1 << G_LOG_LEVEL_USER_SHIFT),
	RSPAMD_LOG_ENCRYPTED = (1 << (G_LOG_LEVEL_USER_SHIFT + 1)),
	RSPAMD_LOG_LEVEL_MASK = ~(RSPAMD_LOG_FORCED | RSPAMD_LOG_ENCRYPTED)
};

typedef struct rspamd_logger_s rspamd_logger_t;
typedef bool (*rspamd_log_func_t)(const char *module, const char *id,
								  const char *function,
								  int level_flags,
								  const char *message,
								  gsize mlen,
								  rspamd_logger_t *logger,
								  gpointer arg);
typedef void *(*rspamd_log_init_func)(rspamd_logger_t *logger,
									  struct rspamd_config *cfg,
									  uid_t uid, gid_t gid,
									  GError **err);
typedef bool (*rspamd_log_on_fork_func)(rspamd_logger_t *logger,
										struct rspamd_config *cfg,
										gpointer arg,
										GError **err);
typedef void *(*rspamd_log_reload_func)(rspamd_logger_t *logger,
										struct rspamd_config *cfg,
										gpointer arg,
										uid_t uid, gid_t gid,
										GError **err);
typedef void (*rspamd_log_dtor_func)(rspamd_logger_t *logger,
									 gpointer arg);

struct rspamd_logger_funcs {
	rspamd_log_init_func init;
	rspamd_log_reload_func reload;
	rspamd_log_dtor_func dtor;
	rspamd_log_func_t log;
	rspamd_log_on_fork_func on_fork;
	gpointer specific;
};

#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || defined(_M_X64)
#define RSPAMD_LOGBUF_SIZE 8192
#else
/* Use a smaller buffer */
#define RSPAMD_LOGBUF_SIZE 2048
#endif

/**
 * Opens a new (initial) logger with console type
 * This logger is also used as an emergency logger
 * @return new rspamd logger object
 */
rspamd_logger_t *rspamd_log_open_emergency(rspamd_mempool_t *pool, int flags);

/**
 * Open specific (configured logging)
 * @param pool
 * @param config
 * @param uid
 * @param gid
 * @return
 */
rspamd_logger_t *rspamd_log_open_specific(rspamd_mempool_t *pool,
										  struct rspamd_config *config,
										  const char *ptype,
										  uid_t uid, gid_t gid);

/**
 * Set log level (from GLogLevelFlags)
 * @param logger
 * @param level
 */
void rspamd_log_set_log_level(rspamd_logger_t *logger, int level);
int rspamd_log_get_log_level(rspamd_logger_t *logger);
const char *rspamd_get_log_severity_string(int level_flags);
/**
 * Set log flags (from enum rspamd_log_flags)
 * @param logger
 * @param flags
 */
void rspamd_log_set_log_flags(rspamd_logger_t *logger, int flags);

/**
 * Close log file or destroy other structures
 */
void rspamd_log_close(rspamd_logger_t *logger);


rspamd_logger_t *rspamd_log_default_logger(void);
rspamd_logger_t *rspamd_log_emergency_logger(void);

/**
 * Close and open log again for privileged processes
 */
bool rspamd_log_reopen(rspamd_logger_t *logger, struct rspamd_config *cfg,
					   uid_t uid, gid_t gid);

/**
 * Set log pid
 */
void rspamd_log_on_fork(GQuark ptype, struct rspamd_config *cfg,
						rspamd_logger_t *logger);

/**
 * Log function that is compatible for glib messages
 */
void rspamd_glib_log_function(const char *log_domain,
							  GLogLevelFlags log_level,
							  const char *message,
							  gpointer arg);

/**
 * Log function for printing glib assertions
 */
void rspamd_glib_printerr_function(const char *message);

/**
 * Function with variable number of arguments support
 */
bool rspamd_common_log_function(rspamd_logger_t *logger,
								int level_flags,
								const char *module, const char *id,
								const char *function, const char *fmt, ...);

bool rspamd_common_logv(rspamd_logger_t *logger, int level_flags,
						const char *module, const char *id, const char *function,
						const char *fmt, va_list args);

/**
 * Add new logging module, returns module ID
 * @param mod
 * @return
 */
int rspamd_logger_add_debug_module(const char *mod);

/*
 * Macro to use for faster debug modules
 */
#define INIT_LOG_MODULE(mname)                                            \
	static int rspamd_##mname##_log_id = -1;                              \
	RSPAMD_CONSTRUCTOR(rspamd_##mname##_log_init)                         \
	{                                                                     \
		rspamd_##mname##_log_id = rspamd_logger_add_debug_module(#mname); \
	}


#define INIT_LOG_MODULE_PUBLIC(mname)                                     \
	int rspamd_##mname##_log_id = -1;                                     \
	RSPAMD_CONSTRUCTOR(rspamd_##mname##_log_init)                         \
	{                                                                     \
		rspamd_##mname##_log_id = rspamd_logger_add_debug_module(#mname); \
	}

#define EXTERN_LOG_MODULE_DEF(mname) \
	extern int rspamd_##mname##_log_id

void rspamd_logger_configure_modules(GHashTable *mods_enabled);

/**
 * Conditional debug function
 */
bool rspamd_conditional_debug(rspamd_logger_t *logger,
							  rspamd_inet_addr_t *addr, const char *module, const char *id,
							  const char *function, const char *fmt, ...);

bool rspamd_conditional_debug_fast(rspamd_logger_t *logger,
								   rspamd_inet_addr_t *addr,
								   int mod_id,
								   const char *module, const char *id,
								   const char *function, const char *fmt, ...);
bool rspamd_conditional_debug_fast_num_id(rspamd_logger_t *logger,
										  rspamd_inet_addr_t *addr,
										  int mod_id,
										  const char *module, uint64_t id,
										  const char *function, const char *fmt, ...);
gboolean rspamd_logger_need_log(rspamd_logger_t *rspamd_log,
								GLogLevelFlags log_level,
								int module_id);

/**
 * Function with variable number of arguments support that uses static default logger
 */
bool rspamd_default_log_function(int level_flags,
								 const char *module, const char *id,
								 const char *function,
								 const char *fmt,
								 ...);

/**
 * Varargs version of default log function
 * @param log_level
 * @param function
 * @param fmt
 * @param args
 */
bool rspamd_default_logv(int level_flags,
						 const char *module, const char *id,
						 const char *function,
						 const char *fmt,
						 va_list args);

/**
 * Temporary turn on debug
 */
void rspamd_log_debug(rspamd_logger_t *logger);

/**
 * Turn off debug
 */
void rspamd_log_nodebug(rspamd_logger_t *logger);

/**
 * Return array of counters (4 numbers):
 * 0 - errors
 * 1 - warnings
 * 2 - info messages
 * 3 - debug messages
 */
const uint64_t *rspamd_log_counters(rspamd_logger_t *logger);

/**
 * Returns errors ring buffer as ucl array
 * @param logger
 * @return
 */
ucl_object_t *rspamd_log_errorbuf_export(const rspamd_logger_t *logger);

/**
 * Sets new logger functions and initialise logging if needed
 * @param logger
 * @param nfuncs
 * @return static pointer to the old functions (so this function is not reentrant)
 */
struct rspamd_logger_funcs *rspamd_logger_set_log_function(rspamd_logger_t *logger,
														   struct rspamd_logger_funcs *nfuncs);

/* Typical functions */

extern unsigned int rspamd_task_log_id;
#ifdef __cplusplus
#define RSPAMD_LOG_FUNC __func__
#else
#define RSPAMD_LOG_FUNC G_STRFUNC
#endif

/* Logging in postfix style */
#define msg_err(...) rspamd_default_log_function(G_LOG_LEVEL_CRITICAL, \
												 NULL, NULL,           \
												 RSPAMD_LOG_FUNC,      \
												 __VA_ARGS__)
#define msg_warn(...) rspamd_default_log_function(G_LOG_LEVEL_WARNING, \
												  NULL, NULL,          \
												  RSPAMD_LOG_FUNC,     \
												  __VA_ARGS__)
#define msg_info(...) rspamd_default_log_function(G_LOG_LEVEL_INFO, \
												  NULL, NULL,       \
												  RSPAMD_LOG_FUNC,  \
												  __VA_ARGS__)
#define msg_notice(...) rspamd_default_log_function(G_LOG_LEVEL_MESSAGE, \
													NULL, NULL,          \
													RSPAMD_LOG_FUNC,     \
													__VA_ARGS__)
#define msg_debug(...) rspamd_default_log_function(G_LOG_LEVEL_DEBUG, \
												   NULL, NULL,        \
												   RSPAMD_LOG_FUNC,   \
												   __VA_ARGS__)

#define debug_task(...) rspamd_conditional_debug_fast(NULL,                                                 \
													  task->from_addr,                                      \
													  rspamd_task_log_id, "task", task->task_pool->tag.uid, \
													  RSPAMD_LOG_FUNC,                                      \
													  __VA_ARGS__)

/* Use the following macros if you have `task` in the function */
#define msg_err_task(...) rspamd_default_log_function(G_LOG_LEVEL_CRITICAL,                                   \
													  task->task_pool->tag.tagname, task->task_pool->tag.uid, \
													  RSPAMD_LOG_FUNC,                                        \
													  __VA_ARGS__)
#define msg_err_task_lambda(...) rspamd_default_log_function(G_LOG_LEVEL_CRITICAL,                                   \
															 task->task_pool->tag.tagname, task->task_pool->tag.uid, \
															 log_func,                                               \
															 __VA_ARGS__)
#define msg_warn_task(...) rspamd_default_log_function(G_LOG_LEVEL_WARNING,                                    \
													   task->task_pool->tag.tagname, task->task_pool->tag.uid, \
													   RSPAMD_LOG_FUNC,                                        \
													   __VA_ARGS__)
#define msg_notice_task(...) rspamd_default_log_function(G_LOG_LEVEL_MESSAGE,                                    \
														 task->task_pool->tag.tagname, task->task_pool->tag.uid, \
														 RSPAMD_LOG_FUNC,                                        \
														 __VA_ARGS__)
#define msg_info_task(...) rspamd_default_log_function(G_LOG_LEVEL_INFO,                                       \
													   task->task_pool->tag.tagname, task->task_pool->tag.uid, \
													   RSPAMD_LOG_FUNC,                                        \
													   __VA_ARGS__)
#define msg_info_task_lambda(...) rspamd_default_log_function(G_LOG_LEVEL_INFO,                                       \
															  task->task_pool->tag.tagname, task->task_pool->tag.uid, \
															  log_func,                                               \
															  __VA_ARGS__)
#define msg_debug_task(...) rspamd_conditional_debug_fast(NULL, task->from_addr,                                \
														  rspamd_task_log_id, "task", task->task_pool->tag.uid, \
														  RSPAMD_LOG_FUNC,                                      \
														  __VA_ARGS__)
#define msg_debug_task_lambda(...) rspamd_conditional_debug_fast(NULL, task->from_addr,                                \
																 rspamd_task_log_id, "task", task->task_pool->tag.uid, \
																 log_func,                                             \
																 __VA_ARGS__)
#define msg_err_task_encrypted(...) rspamd_default_log_function(G_LOG_LEVEL_CRITICAL | RSPAMD_LOG_ENCRYPTED,            \
																task->task_pool->tag.tagname, task->task_pool->tag.uid, \
																RSPAMD_LOG_FUNC,                                        \
																__VA_ARGS__)
#define msg_warn_task_encrypted(...) rspamd_default_log_function(G_LOG_LEVEL_WARNING | RSPAMD_LOG_ENCRYPTED,             \
																 task->task_pool->tag.tagname, task->task_pool->tag.uid, \
																 RSPAMD_LOG_FUNC,                                        \
																 __VA_ARGS__)
#define msg_notice_task_encrypted(...) rspamd_default_log_function(G_LOG_LEVEL_MESSAGE | RSPAMD_LOG_ENCRYPTED,             \
																   task->task_pool->tag.tagname, task->task_pool->tag.uid, \
																   RSPAMD_LOG_FUNC,                                        \
																   __VA_ARGS__)
#define msg_info_task_encrypted(...) rspamd_default_log_function(G_LOG_LEVEL_INFO | RSPAMD_LOG_ENCRYPTED,                \
																 task->task_pool->tag.tagname, task->task_pool->tag.uid, \
																 RSPAMD_LOG_FUNC,                                        \
																 __VA_ARGS__)
/* Check for NULL pointer first */
#define msg_err_task_check(...) rspamd_default_log_function(G_LOG_LEVEL_CRITICAL,                                                               \
															task ? task->task_pool->tag.tagname : NULL, task ? task->task_pool->tag.uid : NULL, \
															RSPAMD_LOG_FUNC,                                                                    \
															__VA_ARGS__)
#define msg_warn_task_check(...) rspamd_default_log_function(G_LOG_LEVEL_WARNING,                                                                \
															 task ? task->task_pool->tag.tagname : NULL, task ? task->task_pool->tag.uid : NULL, \
															 RSPAMD_LOG_FUNC,                                                                    \
															 __VA_ARGS__)
#define msg_info_task_check(...) rspamd_default_log_function(G_LOG_LEVEL_MESSAGE,                                                                \
															 task ? task->task_pool->tag.tagname : NULL, task ? task->task_pool->tag.uid : NULL, \
															 RSPAMD_LOG_FUNC,                                                                    \
															 __VA_ARGS__)
#define msg_notice_task_check(...) rspamd_default_log_function(G_LOG_LEVEL_INFO,                                                                   \
															   task ? task->task_pool->tag.tagname : NULL, task ? task->task_pool->tag.uid : NULL, \
															   RSPAMD_LOG_FUNC,                                                                    \
															   __VA_ARGS__)
#define msg_debug_task_check(...) rspamd_conditional_debug_fast(NULL,                                                               \
																task ? task->from_addr : NULL,                                      \
																rspamd_task_log_id, "task", task ? task->task_pool->tag.uid : NULL, \
																RSPAMD_LOG_FUNC,                                                    \
																__VA_ARGS__)

/* Use the following macros if you have `pool` in the function */
#define msg_err_pool(...) rspamd_default_log_function(G_LOG_LEVEL_CRITICAL,             \
													  pool->tag.tagname, pool->tag.uid, \
													  RSPAMD_LOG_FUNC,                  \
													  __VA_ARGS__)
#define msg_warn_pool(...) rspamd_default_log_function(G_LOG_LEVEL_WARNING,              \
													   pool->tag.tagname, pool->tag.uid, \
													   RSPAMD_LOG_FUNC,                  \
													   __VA_ARGS__)
#define msg_info_pool(...) rspamd_default_log_function(G_LOG_LEVEL_INFO,                 \
													   pool->tag.tagname, pool->tag.uid, \
													   RSPAMD_LOG_FUNC,                  \
													   __VA_ARGS__)
#define msg_debug_pool(...) rspamd_conditional_debug(NULL, NULL,                       \
													 pool->tag.tagname, pool->tag.uid, \
													 RSPAMD_LOG_FUNC,                  \
													 __VA_ARGS__)
/* Check for NULL pointer first */
#define msg_err_pool_check(...) rspamd_default_log_function(G_LOG_LEVEL_CRITICAL,                                         \
															pool ? pool->tag.tagname : NULL, pool ? pool->tag.uid : NULL, \
															RSPAMD_LOG_FUNC,                                              \
															__VA_ARGS__)
#define msg_warn_pool_check(...) rspamd_default_log_function(G_LOG_LEVEL_WARNING,                                          \
															 pool ? pool->tag.tagname : NULL, pool ? pool->tag.uid : NULL, \
															 RSPAMD_LOG_FUNC,                                              \
															 __VA_ARGS__)
#define msg_info_pool_check(...) rspamd_default_log_function(G_LOG_LEVEL_INFO,                                             \
															 pool ? pool->tag.tagname : NULL, pool ? pool->tag.uid : NULL, \
															 RSPAMD_LOG_FUNC,                                              \
															 __VA_ARGS__)
#define msg_debug_pool_check(...) rspamd_conditional_debug(NULL, NULL,                                                   \
														   pool ? pool->tag.tagname : NULL, pool ? pool->tag.uid : NULL, \
														   RSPAMD_LOG_FUNC,                                              \
														   __VA_ARGS__)

#ifdef __cplusplus
}
#endif

#endif
