#ifndef __OMW_DBG_H__
#define __OMW_DBG_H__

#include <stdio.h>
#include <stdint.h>
#include <stdarg.h>

#include "omw_config.h"

#ifdef OMW_HAS_LOG_UART
#define FUNCTION_CONTROL_DEBUG_ENABLE
#endif
#define FUNCTION_CONTROL_DEBUG_PRINTF_ONLY_ENABLE
#define FUNCTION_CONTROL_DEBUG_POINT_PRINT_ENABLE 1

#define LOG_PROTECT_OVERFLOW

// Macro Protocol
#define DEBUG_PROTOCL_TYPE_LOG           (0x00)   // The basic log type
#define DEBUG_PROTOCL_TYPE_ROUND_BUFFER  (0x01)   //
#define DEBUG_PROTOCL_TYPE_STRING        (0x02)   //
#define DEBUG_PROTOCL_TYPE_PROTOCOL      (0x03)   //
#define DEBUG_PROTOCL_TYPE_HCI_H4        (0x04)   //

#define DEBUG_PROTOCL_PAYLOAD_START_OFFSET   (4)
#define DEBUG_PROTOCL_HEADER_SIZE            (5)

#define DEBUG_STRING_LOG_SINGLE_LINE_MAX_SIZE   (200)

// Macro POINT
#define POINT_BUFFER_CNT (200)

typedef struct
{
    uint8_t wptr;
    uint8_t rptr;
    uint32_t buf[POINT_BUFFER_CNT];
}OMW_DBG_POINT_CACHE_INFO;

void omw_dbg_uart_protocol(uint8_t type, uint8_t* buf, uint16_t len, uint8_t* sub_buf, uint16_t sub_len);
void omw_dbg_print_round_buffer(uint8_t index, uint32_t start_addr, uint32_t end_addr, uint32_t write_addr, uint32_t read_addr);
void omw_dbg_print_point(uint32_t value);

void omw_dbg_print_string_raw(const char *format, va_list vaArgP);
void omw_dbg_print_string(const char *format, ...);
void omw_dbg_print_protocol(uint16_t log_level, uint16_t log_type, uint8_t* buf, int len);
void omw_dbg_print_hci_h4_raw(uint8_t isRcv, uint8_t type, uint8_t* buf, int len);
void omw_dbg_print_hci_h4(uint8_t isRcv, uint8_t* buf, int len);
void omw_dbg_init(void);
void omw_dbg_point_polling(void);

#ifdef FUNCTION_CONTROL_DEBUG_ENABLE
#define DEBUG_INIT()    omw_dbg_init()
#ifdef FUNCTION_CONTROL_DEBUG_PRINTF_ONLY_ENABLE
#define DEBUG_STRING(...)
#define DEBUG_STRING_RAW(...)
#else
#define DEBUG_STRING(...)  omw_dbg_print_string(__VA_ARGS__)
#define DEBUG_STRING_RAW(...)  omw_dbg_print_string_raw(__VA_ARGS__)
#endif
#else
#define DEBUG_INIT(callback)
#define DEBUG_STRING(...)
#undef printf
#define printf(...)
#endif

#if defined(FUNCTION_CONTROL_DEBUG_ENABLE) && !defined(FUNCTION_CONTROL_DEBUG_PRINTF_ONLY_ENABLE)

#define DEBUG_POINT_RAW(VALUE)  omw_dbg_print_point(VALUE)
#define DEBUG_POINT(LOG_LEVEL,TYPE_STR,STR,FLAG,VALUE)  omw_dbg_print_point(((FLAG) << 16) | ((uint16_t)VALUE))
#define DEBUG_POINT_POLLING_PRINT()

#define DEBUG_POINT_2(LOG_LEVEL,TYPE_STR,STR,FLAG,VALUE_0,VALUE_1)  \
    DEBUG_POINT(LOG_LEVEL,TYPE_STR,STR,FLAG,(((uint8_t)(VALUE_0) & 0xff)<<8)|(((uint8_t)(VALUE_1) & 0xff)<<0))
#define DEBUG_POINT_4(LOG_LEVEL,TYPE_STR,STR,FLAG,VALUE_0,VALUE_1,VALUE_2,VALUE_3) \
    DEBUG_POINT(LOG_LEVEL,TYPE_STR,STR,FLAG,(((uint8_t)(VALUE_0) & 0x0f)<<12)|(((uint8_t)(VALUE_1) & 0x0f)<<8)|(((uint8_t)(VALUE_2) & 0x0f)<<4)|(((uint8_t)(VALUE_3) & 0x0f)<<0))
#define DEBUG_POINT_8(LOG_LEVEL,TYPE_STR,STR,FLAG,VALUE_0,VALUE_1,VALUE_2,VALUE_3,VALUE_4,VALUE_5,VALUE_6,VALUE_7)\
    DEBUG_POINT(LOG_LEVEL,TYPE_STR,STR,FLAG,(((uint8_t)(VALUE_0) & 0x03)<<14)|(((uint8_t)(VALUE_1) & 0x03)<<12)|(((uint8_t)(VALUE_2) & 0x03)<<10)|(((uint8_t)(VALUE_3) & 0x03)<<8)|(((uint8_t)(VALUE_4) & 0x03)<<6)|(((uint8_t)(VALUE_5) & 0x03)<<4)|(((uint8_t)(VALUE_6) & 0x03)<<2)|(((uint8_t)(VALUE_7) & 0x07)<<0))

#define DEBUG_ROUND_BUFFER(INDEX, START, END, WRITE, READ) (omw_dbg_print_round_buffer(INDEX, START, END, WRITE, READ))
#define DEBUG_PROTOCOL(LEVEL, TYPE, BUFFER, LEN) (omw_dbg_print_protocol(LEVEL, TYPE, BUFFER, LEN))
#define DEBUG_HCI_H4(RCV, BUFFER, LEN) (omw_dbg_print_hci_h4(RCV, BUFFER, LEN))
#define DEBUG_HCI_H4_RAW(RCV, TYPE, BUFFER, LEN) (omw_dbg_print_hci_h4_raw(RCV, TYPE, BUFFER, LEN))
#else
#define DEBUG_POINT_RAW(VALUE)
#define DEBUG_POINT(LOG_LEVEL,TYPE_STR,STR,FLAG,VALUE)
#define DEBUG_POINT_2(LOG_LEVEL,TYPE_STR,STR,FLAG,VALUE_0,VALUE_1)
#define DEBUG_POINT_4(LOG_LEVEL,TYPE_STR,STR,FLAG,VALUE_0,VALUE_1,VALUE_2,VALUE_3)
#define DEBUG_POINT_8(LOG_LEVEL,TYPE_STR,STR,FLAG,VALUE_0,VALUE_1,VALUE_2,VALUE_3,VALUE_4,VALUE_5,VALUE_6,VALUE_7)
#define DEBUG_POINT_POLLING_PRINT()
#define DEBUG_ROUND_BUFFER(INDEX, START, END, WRITE, READ)
#define DEBUG_PROTOCOL(LEVEL, TYPE, BUFFER, LEN)
#define DEBUG_HCI_H4(RCV, BUFFER, LEN)
#define DEBUG_HCI_H4_RAW(RCV, TYPE, BUFFER, LEN)
#endif

#define LOG_NONE               0
#define LOG_ERROR              10
#define LOG_WARNING            20
#define LOG_INFO            30
#define LOG_DEBUG           40

#ifndef DEBUG_POINT_LEVEL
#define DEBUG_POINT_LEVEL            LOG_DEBUG
#endif

#if DEBUG_POINT_LEVEL >= LOG_INFO
#define DEBUG_STRING_INFO(...) DEBUG_STRING(__VA_ARGS__)
#else
#define DEBUG_STRING_INFO(...)
#endif

#if DEBUG_POINT_LEVEL >= LOG_ERROR
#define DEBUG_STRING_ERROR(...) DEBUG_STRING(__VA_ARGS__)
#else
#define DEBUG_STRING_ERROR(...)
#endif

#if DEBUG_POINT_LEVEL >= LOG_WARNING
#define DEBUG_STRING_WARNING(...) DEBUG_STRING(__VA_ARGS__)
#else
#define DEBUG_STRING_WARNING(...)
#endif

#if DEBUG_POINT_LEVEL >= LOG_DEBUG
#define DEBUG_STRING_DEBUG(...) DEBUG_STRING(__VA_ARGS__)
#else
#define DEBUG_STRING_DEBUG(...)
#endif


#ifdef  OMW_USE_FULL_ASSERT
    #define assert_param(expr) ((expr) ? (void)0U : assert_failed((uint8_t *)__FILE__, __LINE__))
    void assert_failed(uint8_t* file, uint32_t line);
#else
    #define assert_param(expr) ((void)0U)
#endif

// Debug Round Buffer Index Define, one buffer should only use one
enum
{
    DEBUG_ROUND_BUFFER_INDEX_00 = 0x00,
    DEBUG_ROUND_BUFFER_INDEX_01 = 0x01,
    DEBUG_ROUND_BUFFER_INDEX_02 = 0x02,
    DEBUG_ROUND_BUFFER_INDEX_03 = 0x03,
    DEBUG_ROUND_BUFFER_INDEX_04 = 0x04,
    DEBUG_ROUND_BUFFER_INDEX_05 = 0x05,
    DEBUG_ROUND_BUFFER_INDEX_06 = 0x06,
    DEBUG_ROUND_BUFFER_INDEX_07 = 0x07,
    DEBUG_ROUND_BUFFER_INDEX_08 = 0x08,
    DEBUG_ROUND_BUFFER_INDEX_09 = 0x09,
    DEBUG_ROUND_BUFFER_INDEX_0A = 0x0A,
    DEBUG_ROUND_BUFFER_INDEX_0B = 0x0B,
};

#endif

#ifdef assert
#undef assert
#endif
#define assert(x)
