#ifndef ACL_MSG_INCLUDE_H
#define ACL_MSG_INCLUDE_H

#ifdef  __cplusplus
extern "C" {
#endif

#include "acl_define.h"
#include <stdarg.h>
#include "acl_vstream.h"

#undef	USE_PRINTF_MACRO

/**
 * 在将写日志至日志文件前回调用户自定义的函数，且将日志信息传递给该函数，
 * 只有当用户通过 acl_msg_pre_write 进行设置后才生效
 * @param ctx {void*} 用户的自定义参数
 * @param fmt {const char*} 格式参数
 * @param ap {va_list} 格式参数列表
 */
typedef void (*ACL_MSG_PRE_WRITE_FN)(void *ctx, const char *fmt, va_list ap);

/**
 * 应用通过此函数类型可以自定义日志打开函数，当应用在打开日志前调用
 * acl_msg_register 注册了自定义打开函数，则当应用调用 acl_msg_open
 * 时会调用此定义打开日志函数打开日志，否则则用缺省的方法打开日志文件
 * @param file_name {const char*} 回传给自定义日志打开函数的参数，即
 *  将日志文件回传
 * @param ctx {void*} 应用传递进去的参数
 * @return {int} 如果自定义打开日志函数返回 -1 则调用缺省的日志打开函数
 */
typedef int (*ACL_MSG_OPEN_FN) (const char *file_name, void *ctx);

/**
 * 应用通过此函数类型可以自定义日志关闭函数，当应用在打开日志前调用
 * acl_msg_register 注册了自定义打开函数，则当应用调用 acl_msg_close
 * 时会调用此定义关闭日志函数关闭日志，否则则用缺省的方法关闭日志文件
 * @param ctx {void*} 应用传递进去的参数
 */
typedef void (*ACL_MSG_CLOSE_FN) (void *ctx);

/**
 * 应用通过此函数类型可以自定义日志记录函数，当应用在打开日志前调用
 * acl_msg_register 注册了自定义记录函数，则当应用写日志时便用此自定义
 * 函数记录日志，否则用缺省的日志记录函数
 * @param ctx {void*} 应用传递进去的参数
 * @param fmt {const char*} 格式参数
 * @param ap {va_list} 参数列表
 */
typedef void (*ACL_MSG_WRITE_FN) (void *ctx, const char *fmt, va_list ap);

/**
 * 在打开日志前调用此函数注册应用自己的日志打开函数、日志关闭函数、日志记录函数
 * @param open_fn {ACL_MSG_OPEN_FN} 自定义日志打开函数
 * @param close_fn {ACL_MSG_CLOSE_FN} 自定义日志关闭函数
 * @param write_fn {ACL_MSG_WRITE_FN} 自定义日志记录函数
 * @param ctx {void*} 自定义参数
 */
ACL_API void acl_msg_register(ACL_MSG_OPEN_FN open_fn, ACL_MSG_CLOSE_FN close_fn,
        ACL_MSG_WRITE_FN write_fn, void *ctx);

/**
 * 将 acl_msg_register 注册自定义函数清除，采用缺省的日志函数集
 */
ACL_API void acl_msg_unregister(void);

/**
 * 在打开日志前调用此函数注册应用的私有函数，在记录日志前会先记录信息通过
 * 此注册的函数传递给应用
 * @param pre_write {ACL_MSG_PRE_WRITE_FN} 日志记录前调用的函数
 * @param ctx {void*} 自定义参数
 */
ACL_API void acl_msg_pre_write(ACL_MSG_PRE_WRITE_FN pre_write, void *ctx);

/**
 * 全局变量，表示调试级别
 * @deprecated 将来该参数将只会内部使用，外部应用不应用它
 */
extern ACL_API int acl_msg_verbose;

/**
 * 当未调用 acl_msg_open 方式打开日志时，调用了 acl_msg_info/error/fatal/warn
 * 的操作，是否允许信息输出至标准输出屏幕上，通过此函数来设置该开关，该开关
 * 仅影响是否需要将信息输出至终端屏幕而不影响是否输出至文件中
 * @param onoff {int} 非 0 表示允许输出至屏幕
 */
ACL_API void acl_msg_stdout_enable(int onoff);

/**
 * 当调用 acl_msg_error_xxx/acl_msg_warn_xxx 等函数记录出错或警告类型的日志时
 * 是否需要记录调用堆栈，可由该函数进行设置
 * @param onoff {int} 非 0 表示允许记录调用出错/警告日志的堆栈，缺省不记录
 */
ACL_API void acl_msg_trace_enable(int onoff);

/**
 * 日志打开函数
 * @param log_file {const char*} 日志接收者集合，由 "|" 分隔，接收器
 *  可以是本地文件或远程套接口，如:
 *  /tmp/test.log|UDP:127.0.0.1:12345|TCP:127.0.0.1:12345|UNIX:/tmp/test.sock
 *  该配置要求将所有日志同时发给 /tmp/test.log, UDP:127.0.0.1:12345,
 *  TCP:127.0.0.1:12345 和 UNIX:/tmp/test.sock 四个日志接收器对象
 * @param plog_pre {const char*} 日志记录信息前的提示信息，建议用进程
 * @param info_pre {const char*} 日志记录信息前的提示信息
 */
ACL_API void acl_msg_open(const char *log_file, const char *info_pre);

/**
 * 日志打开函数
 * @param fp {ACL_VSTREAM *} 日志文件流句柄
 * @param info_pre {const char*} 日志记录信息前的提示信息
 */
ACL_API void acl_msg_open2(ACL_VSTREAM *fp, const char *info_pre);

/**
 * 关闭日志函数
 */
ACL_API void acl_msg_close(void);

/**
 * 当记录日志信息至日志文件时，需要调用如下的日志记录函数
 */

#ifndef	USE_PRINTF_MACRO

/**
 * 一般级别日志信息记录函数
 * @param fmt {const char*} 参数格式
 * @param ... 变参序列
 */
ACL_API void ACL_PRINTF(1, 2) acl_msg_info(const char *fmt,...);

/**
 * 警告级别日志信息记录函数
 * @param fmt {const char*} 参数格式
 * @param ... 变参序列
 */
ACL_API void ACL_PRINTF(1, 2) acl_msg_warn(const char *fmt,...);

/**
 * 错误级别日志信息记录函数
 * @param fmt {const char*} 参数格式
 * @param ... 变参序列
 */
ACL_API void ACL_PRINTF(1, 2) acl_msg_error(const char *fmt,...);

/**
 * 致命级别日志信息记录函数
 * @param fmt {const char*} 参数格式
 * @param ... 变参序列
 */
ACL_API void ACL_PRINTF(1, 2) acl_msg_fatal(const char *fmt,...);

/**
 * 致命级别日志信息记录函数
 * @param status {int} 当前未用
 * @param fmt {const char*} 参数格式
 * @param ... 变参序列
 */
ACL_API void ACL_PRINTF(2, 3)
	acl_msg_fatal_status(int status, const char *fmt,...);

/**
 * 恐慌级别日志信息记录函数
 * @param fmt {const char*} 参数格式
 * @param ... 变参序列
 */
ACL_API void ACL_PRINTF(1, 2) acl_msg_panic(const char *fmt,...);

/**
 * 一般级别日志信息记录函数
 * @param fmt {const char*} 参数格式
 * @param ap {va_list} 变参列表
 */
ACL_API void acl_msg_info2(const char *fmt, va_list ap);


/**
 * 警告级别日志信息记录函数
 * @param fmt {const char*} 参数格式
 * @param ap {va_list} 变参列表
 */
ACL_API void acl_msg_warn2(const char *fmt, va_list ap);

/**
 * 错误级别日志信息记录函数
 * @param fmt {const char*} 参数格式
 * @param ap {va_list} 变参列表
 */
ACL_API void acl_msg_error2(const char *fmt, va_list ap);


/**
 * 致命级别日志信息记录函数
 * @param fmt {const char*} 参数格式
 * @param ap {va_list} 变参列表
 */
ACL_API void acl_msg_fatal2(const char *fmt, va_list ap);

/**
 * 致命级别日志信息记录函数
 * @param status {int} 当前未用
 * @param fmt {const char*} 参数格式
 * @param ap {va_list} 变参列表
 */
ACL_API void acl_msg_fatal_status2(int status, const char *fmt, va_list ap);

/**
 * 恐慌级别日志信息记录函数
 * @param fmt {const char*} 参数格式
 * @param ap {va_list} 变参列表
 */
ACL_API void acl_msg_panic2(const char *fmt, va_list ap);
#else

/**
 * 当记录日志信息至标准输出时，需要调用如下的日志记录函数
 */

#include <stdio.h>

#undef	acl_msg_info
#undef	acl_msg_warn
#undef	acl_msg_error
#undef	acl_msg_fatal
#undef	acl_msg_panic

#define	acl_msg_info	acl_msg_printf
#define	acl_msg_warn	acl_msg_printf
#define	acl_msg_error	acl_msg_printf
#define	acl_msg_fatal	acl_msg_printf
#define	acl_msg_panic	acl_msg_printf

#endif

/**
 * 类似于标准C的 strerror, 但该函数是跨平台且是线程安全的，获得对应某个错误
 * 号的错误描述信息
 * @param errnum {unsigned int} 错误号
 * @param buffer {char*} 存储错误描述信息的内存缓冲区
 * @param size {int} buffer 缓冲区的大小
 * @return {const char*} 返回的地址应与 buffer 相同
 */
ACL_API const char *acl_strerror(unsigned int errnum, char *buffer, int size);

/**
 * 获得上次系统调用出错时的错误描述信息
 * @param buffer {char*} 存储错误描述信息的内存缓冲区
 * @param size {int} buffer 的空间大小
 * @return {const char*} 返回的地址应与 buffer 相同
 */
ACL_API const char *acl_last_strerror(char *buffer, int size);

/**
 * 获得上次系统调用出错时的错误描述信息，该函数内部采用了线程局部变量，所以是线程
 * 安全的，但使用起来更简单些
 * @return {const char *} 返回错误提示信息 
 */
ACL_API const char *acl_last_serror(void);

/**
 * 获得上次系统调用出错时的错误号
 * @return {int} 错误号
 */
ACL_API int acl_last_error(void);

/**
 * 手工设置错误号
 * @param errnum {int} 错误号
 */
ACL_API void acl_set_error(int errnum);

/**
 * 输出信息至标准输出
 * @param fmt {const char*} 格式参数
 * @param ... 变参序列
 */
ACL_API void ACL_PRINTF(1, 2) acl_msg_printf(const char *fmt,...);

#ifdef  __cplusplus
}
#endif

#endif

