#ifndef __SR_UTEST_H__
#define __SR_UTEST_H__


#include <sr_stddef.h>
#include <sr_config.h>


#ifdef __cplusplus
extern "C" {
#endif


uint32_t os_tick_get_value(void);



#ifdef SR_UTEST_USING_DLOG

#define UTEST_LOG_E LOG_E
#define UTEST_LOG_W LOG_W
#define UTEST_LOG_I LOG_I
#define UTEST_LOG_D LOG_D

#define UTEST_LOG_RAW LOG_RAW
#define UTEST_LOG_HEX LOG_HEX

#else /* Not define SR_UTEST_USING_DLOG */
//#include <os_clock.h>
#define UTEST_LOG_E(tag, fmt, ...)                                                                                     \
    SR_PRINTF("\033[31m[%d]" fmt " [%s][%d]\033[0m\r\n", os_tick_get_value(), ##__VA_ARGS__, __FUNCTION__, __LINE__)

#define UTEST_LOG_W(tag, fmt, ...)                                                                                     \
    SR_PRINTF("\033[33m[%d]" fmt " [%s][%d]\033[0m\r\n", os_tick_get_value(), ##__VA_ARGS__, __FUNCTION__, __LINE__)

#define UTEST_LOG_I(tag, fmt, ...)                                                                                     \
    SR_PRINTF("\033[32m[%d]" fmt " [%s][%d]\033[0m\r\n", os_tick_get_value(), ##__VA_ARGS__, __FUNCTION__, __LINE__)

#define UTEST_LOG_D(tag, fmt, ...)                                                                                     \
    SR_PRINTF("[%d]" fmt " [%s][%d]\r\n", os_tick_get_value(), ##__VA_ARGS__, __FUNCTION__, __LINE__)

#endif /* SR_UTEST_USING_DLOG */

/**
 ***********************************************************************************************************************
 * @enum        utest_tc_priority
 *
 * @brief       Testcase priorities.
 ***********************************************************************************************************************
 */
enum utest_tc_priority
{
    TC_PRIORITY_HIGH = 0, /* High priority testcase. */
    TC_PRIORITY_MIDDLE,   /* Middle priority testcase. */
    TC_PRIORITY_LOW,      /* Low priority testcase. */
    TC_PRIORITY_CNT_MAX
};

/**
 ***********************************************************************************************************************
 * @struct      utest_stats
 *
 * @brief       Statistical results of executing multiple testcases.
 ***********************************************************************************************************************
 */
struct utest_stats
{
    uint16_t tc_passed_num; /* Total number of testcases passed. */
    uint16_t tc_failed_num; /* Total number of testcases failed. */
    uint16_t tu_passed_num; /* Total number of test units passed. */
    uint16_t tu_failed_num; /* Total number of test units failed. */
    uint16_t tp_passed_num; /* Total number of test points passed. */
    uint16_t tp_failed_num; /* Total number of test points failed. */
};
typedef struct utest_stats utest_stats_t;

/**
 ***********************************************************************************************************************
 * @struct      utest_tp_stats
 *
 * @brief       Statistical results of test points in a single testcase.
 ***********************************************************************************************************************
 */
struct utest_tp_stats
{
    uint16_t tp_passed_num; /* Total number of test points passed in a single testcase. */
    uint16_t tp_failed_num; /* Total number of test points failed in a single testcase. */
};
typedef struct utest_tp_stats utest_tp_stats_t;

/**
 ***********************************************************************************************************************
 * @struct      utest_tc_entry
 *
 * @brief       utest testcase entry data structure. Will export the data to 'utestTcTab' section in flash.
 ***********************************************************************************************************************
 */
struct utest_tc_entry
{
    const char *name;                /* Testcase name. */
    int (*init)(void);          /* Initialization before executing the testcase function. */
    void (*tc)(void);                /* Execute the testcase function. */
    int (*cleanup)(void);       /* Cleanup after executing the testcase function.  */
    enum utest_tc_priority priority; /* The priority of testcase */
};
typedef struct utest_tc_entry utest_tc_entry_t;

/* Unit test handler function pointer */
typedef void (*utest_unit_func_t)(void);

/**
 ***********************************************************************************************************************
 * @def         UTEST_TC_EXPORT
 *
 * @brief       Export testcase entry to '.sunrise.testcase.1' section in flash. Used in application layer.
 *
 * @param       name            The testcase name.
 * @param       testcase        The execute function of the testcase.
 * @param       init            The initialization function of the testcase.
 * @param       cleanup         The cleanup function of the testcase.
 * @param       priority        The testcase priority.
 ***********************************************************************************************************************
 */
 #if  0
#define UTEST_TC_EXPORT(name, testcase, init, cleanup, priority)                                                       \
    SR_USED static const utest_tc_entry_t gs_utest##testcase SR_SECTION(                                               \
        "utestTcTab") = {#name, init, testcase, cleanup, priority};

#endif
//合宙平台不要加static
#define UTEST_TC_EXPORT(name, testcase, init, cleanup, priority)                                                       \
    SR_USED  const utest_tc_entry_t _sr_utest_##testcase SR_SECTION(                                               \
        ".sunrise.testcase.1") = {#name, init, testcase, cleanup, priority};



/**
 ***********************************************************************************************************************
 * @def         UTEST_UNIT_RUN
 *
 * @brief       Test unit function executor. Used in 'testcase' function in application.
 *
 * @param       test_unit_func  Unit test function.
 ***********************************************************************************************************************
 */
#define UTEST_UNIT_RUN(test_unit_func)                                                                                 \
    do                                                                                                                 \
    {                                                                                                                  \
        utest_unit_run(test_unit_func, #test_unit_func);                                                               \
        if (utest_tp_stats_get()->tp_failed_num != 0)                                                                  \
        {                                                                                                              \
            return;                                                                                                    \
        }                                                                                                              \
    } while (0)

#define __utest_assert(value, msg) utest_assert(value, __FILE__, __LINE__, __func__, msg)

/* If @value is true, not assert, means passing. */
#define tp_assert_true(value) __utest_assert(value, "(" #value ") is false")

/* If @value is false, not assert, means passing. */
#define tp_assert_false(value) __utest_assert(!(value), "(" #value ") is true")

/* If @value is null, not assert, means passing. */
#define tp_assert_null(value) __utest_assert(SR_NULL == (const char *)(value), "(" #value ") is not null")

/* If @value is not null, not assert, means passing. */
#define tp_assert_not_null(value) __utest_assert(SR_NULL != (const char *)(value), "(" #value ") is null")

/* If @a equal to @b, not assert, means passing. Integer type test. */
#define tp_assert_integer_equal(a, b) __utest_assert((a) == (b), "(" #a ") not equal to (" #b ")")

/* If @a not equal to @b, not assert, means passing. Integer type test. */
#define tp_assert_integer_not_equal(a, b) __utest_assert((a) != (b), "(" #a ") equal to (" #b ")")

/* if @a equal to @b, not assert, means passing. String type test. */
#define tp_assert_str_equal(a, b)                                                                                      \
    utest_assert_string((const char *)(a), (const char *)(b), SR_TRUE, __FILE__, __LINE__, __func__, "string not equal")

/* If @a not equal to @b, not assert, means passing. String type test. */
#define tp_assert_str_not_equal(a, b)                                                                                  \
    utest_assert_string((const char *)(a), (const char *)(b), SR_FALSE, __FILE__, __LINE__, __func__, "string equal")

/* If @a equal to @b, not assert, means passing. Buffer type test. */
#define tp_assert_buf_equal(a, b, size)                                                                                \
    utest_assert_buf((const uint8_t *)(a),                                                                             \
                     (const uint8_t *)(b),                                                                             \
                     (size),                                                                                           \
                     SR_TRUE,                                                                                          \
                     __FILE__,                                                                                         \
                     __LINE__,                                                                                         \
                     __func__,                                                                                         \
                     "buf not equal")

/* If @a not equal to @b, not assert, means passing. Buffer type test. */
#define tp_assert_buf_not_equal(a, b, size)                                                                            \
    utest_assert_buf((const uint8_t *)(a),                                                                             \
                     (const uint8_t *)(b),                                                                             \
                     (size),                                                                                           \
                     SR_FALSE,                                                                                         \
                     __FILE__,                                                                                         \
                     __LINE__,                                                                                         \
                     __func__,                                                                                         \
                     "buf equal")

/* If @value is in range of @min and @max, not assert, means passing. */
#define tp_assert_in_range(value, min, max)                                                                            \
    __utest_assert(((value >= min) && (value <= max)), "(" #value ") not in range(" #min "," #max ")")

/* If @value is not in range of @min and @max, not assert, means passing. */
#define tp_assert_not_in_range(value, min, max)                                                                        \
    __utest_assert(!((value >= min) && (value <= max)), "(" #value ") in range(" #min "," #max ")")

extern utest_tp_stats_t *utest_tp_stats_get(void);
extern void              utest_unit_run(utest_unit_func_t func, const char *unit_func_name);

extern void utest_assert(srBool_t condition, const char *file, int32_t line, const char *func, const char *msg);

extern void utest_assert_string(const char *str_a,
                                const char *str_b,
                                srBool_t   equal,
                                const char *file,
                                int32_t     line,
                                const char *func,
                                const char *msg);

extern void utest_assert_buf(const uint8_t *buff_a,
                             const uint8_t *buff_b,
                             srSize_t      size,
                             srBool_t      equal,
                             const char    *file,
                             int32_t        line,
                             const char    *func,
                             const char    *msg);


//获取命令行传参
void UtestGetArgs(int *argc,char **argv);

#ifdef __cplusplus
}
#endif

#endif /* __UTEST_H__ */
