/**
 * 方便打印日志
 * 为了保证输出顺序 都使用stdout而不是stderr
 *
 * 可配置项(默认都是未定义)
 * LOG_LINE_END_CRLF        默认是\n结尾 添加此宏将以\r\n结尾
 * LOG_FOR_MCU              MCU项目可配置此宏 更适用于MCU环境
 * LOG_NDEBUG               关闭LOGD的输出
 * LOG_SHOW_VERBOSE         显示LOGV的输出
 * LOG_NOT_EXIT_ON_FATAL    FATAL默认退出程序 添加此宏将不退出
 */
#ifndef HW_LIB_LOG_H
#define HW_LIB_LOG_H
#pragma once

#ifdef __cplusplus
#include <cstdio>
#include <cstring>
#include <cstdlib>
#else

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#endif

/* 输出日志等级 */
#define LOG_LVL_FATAL                       0
#define LOG_LVL_ERROR                       1
#define LOG_LVL_WARN                        2
#define LOG_LVL_INFO                        3
#define LOG_LVL_DEBUG                       4
#define LOG_LVL_VERBOSE                     5

// 配置
#define LOG_TIMER
//#define LOG_FOR_MCU
#define LOG_LINE_END_CRLF
//#define LOG_WITH_RUN_TIMER
#define LOG_OUTPUT_LVL LOG_LVL_INFO
////////////////////////

#ifdef LOG_WITH_RUN_TIMER
#define LOG_RUN_TIMER_FUN GetTickCount64()
#define LOG_RUN_TIMER_FMT "-T(%lums)"
#else
#define LOG_RUN_TIMER_FUN GetTickCount64()
#define LOG_RUN_TIMER_FMT "(%llums)"
#endif

#ifdef  LOG_LINE_END_CRLF
#define LOG_LINE_END        "\r\n"
#else
#define LOG_LINE_END        "\n"
#endif

#ifdef LOG_NOT_EXIT_ON_FATAL
#define LOG_EXIT_PROGRAM()
#else
#ifdef LOG_FOR_MCU
#define LOG_EXIT_PROGRAM()      do{ for(;;); } while(0)
#else
#define LOG_EXIT_PROGRAM()      exit(EXIT_FAILURE)
#endif
#endif

#define LOG_OUT_FUNC printf


#define LOG_BASE_FILENAME \
    (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : \
    strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)

#define LOG_WITH_COLOR
#if defined(_WIN32) || defined(LOG_FOR_MCU)
#undef LOG_WITH_COLOR
#endif



#ifdef LOG_WITH_COLOR
#define LOG_COLOR_RED       "\033[31m"
#define LOG_COLOR_GREEN     "\033[32m"
#define LOG_COLOR_YELLOW    "\033[33m"
#define LOG_COLOR_BLUE      "\033[34m"
#define LOG_COLOR_CARMINE   "\033[35m"
#define LOG_COLOR_CYAN      "\033[36m"
#define LOG_COLOR_DEFAULT
#define LOG_COLOR_END       "\033[m"
#else
#define LOG_COLOR_RED
#define LOG_COLOR_GREEN
#define LOG_COLOR_YELLOW
#define LOG_COLOR_BLUE
#define LOG_COLOR_CARMINE
#define LOG_COLOR_CYAN
#define LOG_COLOR_DEFAULT
#define LOG_COLOR_END
#endif

#define LOG_END                 LOG_COLOR_END LOG_LINE_END

// 带时钟输出
#if defined(LOG_TIMER)
#ifdef LOG_WITH_RUN_TIMER
#define LOGT(tag, fmt, ...)     do{ LOG_OUT_FUNC(LOG_COLOR_BLUE "[" tag "]" LOG_RUN_TIMER_FMT ": " fmt LOG_END,LOG_RUN_TIMER_FUN, ##__VA_ARGS__); } while(0)
#else
#define LOGT(tag, fmt, ...)     do{ LOG_OUT_FUNC(LOG_COLOR_BLUE "[" tag "]: " fmt LOG_END, ##__VA_ARGS__); } while(0)
#endif
#else
#define LOGT(fmt, ...)          ((void)0)
#endif

// 等级输出
#if LOG_OUTPUT_LVL >= LOG_LVL_FATAL
#ifdef LOG_WITH_RUN_TIMER
#define LOGF(fmt, ...)          do{ LOG_OUT_FUNC(LOG_COLOR_CYAN "[F]" LOG_RUN_TIMER_FMT ": %s: %s: %d: " fmt LOG_END,LOG_RUN_TIMER_FUN, LOG_BASE_FILENAME, __func__, __LINE__, ##__VA_ARGS__); LOG_EXIT_PROGRAM(); } while(0)
#else
#define LOGF(fmt, ...)          do{ LOG_OUT_FUNC(LOG_COLOR_CYAN "[F]: %s: %s: %d: " fmt LOG_END, LOG_BASE_FILENAME, __func__, __LINE__, ##__VA_ARGS__); LOG_EXIT_PROGRAM(); } while(0)
#endif
#else
#define LOGF(fmt, ...)          ((void)0)
#endif

#if LOG_OUTPUT_LVL >= LOG_LVL_ERROR
#ifdef LOG_WITH_RUN_TIMER
#define LOGE(fmt, ...)          do{ LOG_OUT_FUNC(LOG_COLOR_RED "[E]" LOG_RUN_TIMER_FMT ": %s: %s: %d: " fmt LOG_END,LOG_RUN_TIMER_FUN, LOG_BASE_FILENAME, __func__, __LINE__, ##__VA_ARGS__); } while(0)
#else
#define LOGE(fmt, ...)          do{ LOG_OUT_FUNC(LOG_COLOR_RED "[E]: %s: %s: %d: " fmt LOG_END, LOG_BASE_FILENAME, __func__, __LINE__, ##__VA_ARGS__); } while(0)
#endif
#else
#define LOGE(fmt, ...)          ((void)0)
#endif

#if LOG_OUTPUT_LVL >= LOG_LVL_WARN
#ifdef LOG_WITH_RUN_TIMER
#define LOGW(fmt, ...)          do{ LOG_OUT_FUNC(LOG_COLOR_CARMINE "[W]" LOG_RUN_TIMER_FMT ": %s: %s: %d: " fmt LOG_END,LOG_RUN_TIMER_FUN, LOG_BASE_FILENAME, __func__, __LINE__, ##__VA_ARGS__); } while(0)
#else
#define LOGW(fmt, ...)          do{ LOG_OUT_FUNC(LOG_COLOR_CARMINE "[W]: %s: %s: %d: " fmt LOG_END, LOG_BASE_FILENAME, __func__, __LINE__, ##__VA_ARGS__); } while(0)
#endif
#else
#define LOGW(fmt, ...)          ((void)0)
#endif

#if LOG_OUTPUT_LVL >= LOG_LVL_INFO
#ifdef LOG_WITH_RUN_TIMER
#define LOGI(fmt, ...)          do{ LOG_OUT_FUNC(LOG_COLOR_YELLOW "[I]" LOG_RUN_TIMER_FMT ": %s: " fmt LOG_END,LOG_RUN_TIMER_FUN, LOG_BASE_FILENAME, ##__VA_ARGS__); } while(0)
#else
#define LOGI(fmt, ...)          do{ LOG_OUT_FUNC(LOG_COLOR_YELLOW "[I]: %s: " fmt LOG_END, LOG_BASE_FILENAME, ##__VA_ARGS__); } while(0)
#endif
#else
#define LOGI(fmt, ...)          ((void)0)
#endif

#if LOG_OUTPUT_LVL >= LOG_LVL_DEBUG
#ifdef LOG_WITH_RUN_TIMER
#define LOGD(fmt, ...)          do{ LOG_OUT_FUNC(LOG_COLOR_DEFAULT "[D]" LOG_RUN_TIMER_FMT ": %s: " fmt LOG_END,LOG_RUN_TIMER_FUN, LOG_BASE_FILENAME, ##__VA_ARGS__); } while(0)
#else
#define LOGD(fmt, ...)          do{ LOG_OUT_FUNC(LOG_COLOR_DEFAULT "[D]: %s: " fmt LOG_END, LOG_BASE_FILENAME, ##__VA_ARGS__); } while(0)
#endif
#else
#define LOGD(fmt, ...)          ((void)0)
#endif

#if LOG_OUTPUT_LVL >= LOG_LVL_VERBOSE
#ifdef LOG_WITH_RUN_TIMER
#define LOGV(fmt, ...)          do{ LOG_OUT_FUNC(LOG_COLOR_DEFAULT "[V]" LOG_RUN_TIMER_FMT ": %s: " fmt LOG_END,LOG_RUN_TIMER_FUN, LOG_BASE_FILENAME, ##__VA_ARGS__); } while(0)
#else
#define LOGV(fmt, ...)          do{ LOG_OUT_FUNC(LOG_COLOR_DEFAULT "[V]: %s: " fmt LOG_END, LOG_BASE_FILENAME, ##__VA_ARGS__); } while(0)
#endif
#else
#define LOGV(fmt, ...)          ((void)0)
#endif


#ifndef LOG_RUN_TIME
#ifndef __LOG_VA_NUM_ARGS_IMPL
#   define __LOG_VA_NUM_ARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, \
                                    _12, _13, _14, _15, _16, __N, ...)      __N
#endif

#ifndef __LOG_VA_NUM_ARGS
#define __LOG_VA_NUM_ARGS(...)                                                \
            __LOG_VA_NUM_ARGS_IMPL( 0,##__VA_ARGS__,16,15,14,13,12,11,10,9,   \
                                      8,7,6,5,4,3,2,1,0)
#endif

#ifndef __ticks_sync_barrier__

/* default implementation */
#if defined(__clang__) || __IS_COMPILER_GCC__
#   define __ticks_sync_barrier__(...)      __sync_synchronize()
#else
#   define __ticks_sync_barrier__(...)
#endif

#endif

#undef __CONNECT2
#undef __CONNECT3

#undef CONNECT2
#undef CONNECT3

#undef CONNECT

#undef __MACRO_EXPANDING
#define __MACRO_EXPANDING(...)                      __VA_ARGS__

#define __CONNECT2(__A, __B)                        __A##__B
#define __CONNECT3(__A, __B, __C)                   __A##__B##__C

#define ALT_CONNECT2(__A, __B)              __CONNECT2(__A, __B)
#define CONNECT2(__A, __B)                  __CONNECT2(__A, __B)
#define CONNECT3(__A, __B, __C)             __CONNECT3(__A, __B, __C)

#define CONNECT(...)                                                            \
            ALT_CONNECT2(CONNECT, __LOG_VA_NUM_ARGS(__VA_ARGS__))(__VA_ARGS__)

#undef __using1
#undef __using2
#undef __using3
#undef __using4
#undef using

#define __using1(__declare)                                                     \
            for (__declare, *CONNECT3(__using_, __LINE__,_ptr) = NULL;          \
                 CONNECT3(__using_, __LINE__,_ptr)++ == NULL;                   \
                )

#define __using2(__declare, __on_leave_expr)                                    \
            for (__declare, *CONNECT3(__using_, __LINE__,_ptr) = NULL;          \
                 CONNECT3(__using_, __LINE__,_ptr)++ == NULL;                   \
                 (__on_leave_expr)                                              \
                )

#define __using3(__declare, __on_enter_expr, __on_leave_expr)                   \
            for (__declare, *CONNECT3(__using_, __LINE__,_ptr) = NULL;          \
                 CONNECT3(__using_, __LINE__,_ptr)++ == NULL ?                  \
                    ((__on_enter_expr),1) : 0;                                  \
                 (__on_leave_expr)                                              \
                )

#define __using4(__dcl1, __dcl2, __on_enter_expr, __on_leave_expr)              \
            for (__dcl1, __dcl2, *CONNECT3(__using_, __LINE__,_ptr) = NULL;     \
                 CONNECT3(__using_, __LINE__,_ptr)++ == NULL ?                  \
                    ((__on_enter_expr),1) : 0;                                  \
                 (__on_leave_expr)                                              \
                )

#define using(...)                                                              \
                CONNECT2(__using, __LOG_VA_NUM_ARGS(__VA_ARGS__))(__VA_ARGS__)

#ifdef LOG_WITH_RUN_TIMER
#define LOG_RUN_TIME(__STR, ...)                                                 \
            using(uint64_t _ = LOG_RUN_TIMER_FUN, __time_count__ = _,          \
                {__ticks_sync_barrier__();},                                    \
                {                                                               \
                __ticks_sync_barrier__();                                       \
                _ = LOG_RUN_TIMER_FUN - _;                         \
                __time_count__ = _;                                            \
                if (__LOG_VA_NUM_ARGS(__VA_ARGS__) == 0) {                    \
                    LOG_OUT_FUNC("\r\n");                            \
                    LOG_OUT_FUNC("-[Run Timer]");                 \
                    LOG_OUT_FUNC(                                    \
                        "------------------------------------\r\n");            \
                    LOG_OUT_FUNC(                                    \
                        __STR " total run timer: " LOG_RUN_TIMER_FMT LOG_END,            \
                            _);                                  \
                } else {                                                        \
                    __VA_ARGS__                                                 \
                };                                                              \
            })
#else

#define LOG_RUN_TIME(__STR, ...)                                                 \
            using(uint64_t _ = LOG_RUN_TIMER_FUN, __time_count__ = _,          \
                {__ticks_sync_barrier__();},                                    \
                {                                                               \
                __ticks_sync_barrier__();                                       \
                _ = LOG_RUN_TIMER_FUN - _;                         \
                __time_count__ = _;                                            \
                if (__LOG_VA_NUM_ARGS(__VA_ARGS__) == 0) {                    \
                    LOG_OUT_FUNC("\r\n");                            \
                    LOG_OUT_FUNC("-[Run Timer]");                 \
                    LOG_OUT_FUNC(                                    \
                        "------------------------------------\r\n");            \
                    LOG_OUT_FUNC(                                    \
                        __STR " total run timer: " LOG_RUN_TIMER_FMT LOG_END,            \
                            _);                                  \
                } else {                                                        \
                    __VA_ARGS__                                                 \
                };                                                              \
            })
#endif
#endif
#endif //HW_LIB_LOG_H