#pragma once

/*! \defgroup logging Osmocom logging framework
 *  @{
 */

/*! \file logging.h */

#include <stdio.h>
#include <stdint.h>
#include <stdarg.h>
#include <osmocom/core/linuxlist.h>

/*! \brief Maximum number of logging contexts */
#define LOG_MAX_CTX		8
/*! \brief Maximum number of logging filters */
#define LOG_MAX_FILTERS	8

#define DEBUG

#ifdef DEBUG
#define DEBUGP(ss, fmt, args...) logp(ss, __FILE__, __LINE__, 0, fmt, ## args)
#define DEBUGPC(ss, fmt, args...) logp(ss, __FILE__, __LINE__, 1, fmt, ## args)
#else
#define DEBUGP(xss, fmt, args...)
#define DEBUGPC(ss, fmt, args...)
#endif


void osmo_vlogp(int subsys, int level, const char *file, int line,
		int cont, const char *format, va_list ap);

void logp(int subsys, const char *file, int line, int cont, const char *format, ...) __attribute__ ((format (printf, 5, 6)));

/*! \brief Log a new message through the Osmocom logging framework
 *  \param[in] ss logging subsystem (e.g. \ref DLGLOBAL)
 *  \param[in] level logging level (e.g. \ref LOGL_NOTICE)
 *  \param[in] fmt format string
 *  \param[in] args variable argument list
 */
#define LOGP(ss, level, fmt, args...) \
	logp2(ss, level, __FILE__, __LINE__, 0, fmt, ##args)

/*! \brief Continue a log message through the Osmocom logging framework
 *  \param[in] ss logging subsystem (e.g. \ref DLGLOBAL)
 *  \param[in] level logging level (e.g. \ref LOGL_NOTICE)
 *  \param[in] fmt format string
 *  \param[in] args variable argument list
 */
#define LOGPC(ss, level, fmt, args...) \
	logp2(ss, level, __FILE__, __LINE__, 1, fmt, ##args)

/*! \brief different log levels */
#define LOGL_DEBUG	1	/*!< \brief debugging information */
#define LOGL_INFO	3
#define LOGL_NOTICE	5	/*!< \brief abnormal/unexpected condition */
#define LOGL_ERROR	7	/*!< \brief error condition, requires user action */
#define LOGL_FATAL	8	/*!< \brief fatal, program aborted */

#define LOG_FILTER_ALL	0x0001

/* logging levels defined by the library itself */
#define DLGLOBAL	-1
#define DLLAPD		-2
#define DLINP		-3
#define DLMUX		-4
#define DLMI		-5
#define DLMIB		-6
#define DLSMS		-7
#define DLCTRL		-8
#define DLGTP		-9
#define DLSTATS		-10
#define OSMO_NUM_DLIB	10

struct log_category {
	uint8_t loglevel;
	uint8_t enabled;
};

/*! \brief Information regarding one logging category */
struct log_info_cat {
	const char *name;		/*!< name of category */
	const char *color;		/*!< color string for cateyory */
	const char *description;	/*!< description text */
	uint8_t loglevel;		/*!< currently selected log-level */
	uint8_t enabled;		/*!< is this category enabled or not */
};

/*! \brief Log context information, passed to filter */
struct log_context {
	void *ctx[LOG_MAX_CTX+1];
};

struct log_target;

/*! \brief Log filter function */
typedef int log_filter(const struct log_context *ctx,
		       struct log_target *target);

struct log_info;
struct vty;

typedef void log_print_filters(struct vty *vty,
			       const struct log_info *info,
			       const struct log_target *tgt);

typedef void log_save_filters(struct vty *vty,
			      const struct log_info *info,
			      const struct log_target *tgt);

/*! \brief Logging configuration, passed to \ref log_init */
struct log_info {
	/* \brief filter callback function */
	log_filter *filter_fn;

	/*! \brief per-category information */
	const struct log_info_cat *cat;
	/*! \brief total number of categories */
	unsigned int num_cat;
	/*! \brief total number of user categories (not library) */
	unsigned int num_cat_user;

	/* \brief filter saving function */
	log_save_filters *save_fn;
	/* \brief filter saving function */
	log_print_filters *print_fn;
};

/*! \brief Type of logging target */
enum log_target_type {
	LOG_TGT_TYPE_VTY,	/*!< \brief VTY logging */
	LOG_TGT_TYPE_SYSLOG,	/*!< \brief syslog based logging */
	LOG_TGT_TYPE_FILE,	/*!< \brief text file logging */
	LOG_TGT_TYPE_STDERR,	/*!< \brief stderr logging */
	LOG_TGT_TYPE_STRRB,	/*!< \brief osmo_strrb-backed logging */
};

/*! \brief structure representing a logging target */
struct log_target {
        struct llist_head entry;		/*!< \brief linked list */

	/*! \brief Internal data for filtering */
	int filter_map;
	/*! \brief Internal data for filtering */
	void *filter_data[LOG_MAX_FILTERS+1];

	/*! \brief logging categories */
	struct log_category *categories;

	/*! \brief global log level */
	uint8_t loglevel;
	/*! \brief should color be used when printing log messages? */
	unsigned int use_color:1;
	/*! \brief should log messages be prefixed with a timestamp? */
	unsigned int print_timestamp:1;
	/*! \brief should log messages be prefixed with a filename? */
	unsigned int print_filename:1;
	/*! \brief should log messages be prefixed with a category name? */
	unsigned int print_category:1;
	/*! \brief should log messages be prefixed with an extended timestamp? */
	unsigned int print_ext_timestamp:1;

	/*! \brief the type of this log taget */
	enum log_target_type type;

	union {
		struct {
			FILE *out;
			const char *fname;
		} tgt_file;

		struct {
			int priority;
			int facility;
		} tgt_syslog;

		struct {
			void *vty;
		} tgt_vty;

		struct {
			void *rb;
		} tgt_rb;
	};

	/*! \brief call-back function to be called when the logging framework
	 *	   wants to log somethnig.
	 *  \param[in] target logging target
	 *  \param[in] level log level of currnet message
	 *  \param[in] string the string that is to be written to the log
	 */
        void (*output) (struct log_target *target, unsigned int level,
			const char *string);
};

/* use the above macros */
void logp2(int subsys, unsigned int level, const char *file,
	   int line, int cont, const char *format, ...)
				__attribute__ ((format (printf, 6, 7)));
int log_init(const struct log_info *inf, void *talloc_ctx);

/* context management */
void log_reset_context(void);
int log_set_context(uint8_t ctx, void *value);

/* filter on the targets */
void log_set_all_filter(struct log_target *target, int);

void log_set_use_color(struct log_target *target, int);
void log_set_print_extended_timestamp(struct log_target *target, int);
void log_set_print_timestamp(struct log_target *target, int);
void log_set_print_filename(struct log_target *target, int);
void log_set_print_category(struct log_target *target, int);
void log_set_log_level(struct log_target *target, int log_level);
void log_parse_category_mask(struct log_target *target, const char* mask);
int log_parse_level(const char *lvl);
const char *log_level_str(unsigned int lvl);
int log_parse_category(const char *category);
void log_set_category_filter(struct log_target *target, int category,
			       int enable, int level);

/* management of the targets */
struct log_target *log_target_create(void);
void log_target_destroy(struct log_target *target);
struct log_target *log_target_create_stderr(void);
struct log_target *log_target_create_file(const char *fname);
struct log_target *log_target_create_syslog(const char *ident, int option,
					    int facility);
int log_target_file_reopen(struct log_target *tgt);
int log_targets_reopen(void);

void log_add_target(struct log_target *target);
void log_del_target(struct log_target *target);

/* Generate command string for VTY use */
const char *log_vty_command_string(const struct log_info *info);
const char *log_vty_command_description(const struct log_info *info);

struct log_target *log_target_find(int type, const char *fname);
extern struct llist_head osmo_log_target_list;

/*! @} */
