#ifndef __TA_LOGGER_H__
#define __TA_LOGGER_H__

#include <stdio.h>
#include <inttypes.h>

#include "TigerConfig.h"

#include "tzWrappers/TzwCommon.h"
#include "tzWrappers/TzwTimer.h"

#ifdef TRANSMIT_LOG_TO_CA
#define LOG_BUFF_ERR_MAX_LEN    0x200
#define LOG_BUFF_MAX_LEN    0x100
#define LOG_BUFF_GAP_LEN    0x20
#endif

#if defined(TZ_MODEL_QCOM)
    #ifdef TA_RELEASE
        #define LOG_MASK (QSEE_LOG_MSG_ERROR | QSEE_LOG_MSG_FATAL | QSEE_LOG_MSG_HIGH)
    #else
        #define LOG_MASK (QSEE_LOG_MSG_ERROR | QSEE_LOG_MSG_FATAL | QSEE_LOG_MSG_DEBUG | QSEE_LOG_MSG_HIGH)
    #endif

    #define LOG_Raw(xx_fmt, ...)  \
        qsee_log(QSEE_LOG_MSG_FATAL, "|R:%s:%3d| " xx_fmt, __func__, __LINE__, ##__VA_ARGS__);

    #ifdef TRANSMIT_LOG_TO_CA
        #define LOG_F(xx_fmt, ...) { \
            qsee_log(QSEE_LOG_MSG_FATAL, "|F:%s:%3d| " xx_fmt, __func__, __LINE__, ##__VA_ARGS__); \
            char str4ca[1024]; \
            int strlen = snprintf(str4ca, 1024, "|F:%s:%3d| " xx_fmt, __func__, __LINE__, ##__VA_ARGS__); \
            pushStackLog(str4ca, strlen); \
        }
        #define LOG_E(xx_fmt, ...) { \
            qsee_log(QSEE_LOG_MSG_ERROR, "|E:%s:%3d| " xx_fmt, __func__, __LINE__, ##__VA_ARGS__); \
            char str4ca[1024]; \
            int strlen = snprintf(str4ca, 1024, "|E:%s:%3d| " xx_fmt, __func__, __LINE__, ##__VA_ARGS__); \
            pushStackLog(str4ca, strlen); \
        }

        #ifndef TA_RELEASE
            // Warning message
            #define LOG_W(xx_fmt, ...)   { \
                qsee_log(QSEE_LOG_MSG_HIGH,  "|W| "        xx_fmt, ##__VA_ARGS__); \
                char str4ca[1024]; \
                int strlen = snprintf(str4ca, 1024, "|W:%s:%3d| " xx_fmt, __func__, __LINE__, ##__VA_ARGS__); \
                pushStackLog(str4ca, strlen); \
            }

            // Debug (data) log message
            #define LOG_D(xx_fmt, ...) {  \
                qsee_log(QSEE_LOG_MSG_DEBUG, "|D| "        xx_fmt, ##__VA_ARGS__); \
                char str4ca[1024]; \
                int strlen = snprintf(str4ca, 1024, "|D:%s:%3d| " xx_fmt, __func__, __LINE__, ##__VA_ARGS__); \
                pushStackLog(str4ca, strlen); \
            }

            // Information (status) message
            #define LOG_I(xx_fmt, ...) {   \
                qsee_log(QSEE_LOG_MSG_HIGH,  "|I| "        xx_fmt, ##__VA_ARGS__); \
                char str4ca[1024]; \
                int strlen = snprintf(str4ca, 1024, "|I:%s:%3d| " xx_fmt, __func__, __LINE__, ##__VA_ARGS__); \
                pushStackLog(str4ca, strlen); \
            }
         #else
            // Warning message
            #define LOG_W(xx_fmt, ...) qsee_log(QSEE_LOG_MSG_HIGH,  "|W| "        xx_fmt, ##__VA_ARGS__)
            // Debug (data) log message
            #define LOG_D(xx_fmt, ...) qsee_log(QSEE_LOG_MSG_DEBUG, "|D| "        xx_fmt, ##__VA_ARGS__)
            // Information (status) message
            #define LOG_I(xx_fmt, ...) qsee_log(QSEE_LOG_MSG_HIGH,  "|I| "        xx_fmt, ##__VA_ARGS__)

         #endif

    #else
        // Fatal error (crash) log message
        #define LOG_F(xx_fmt, ...) qsee_log(QSEE_LOG_MSG_FATAL, "|F:%s:%3d| " xx_fmt, __func__, __LINE__, ##__VA_ARGS__)
        // Error log message
        #define LOG_E(xx_fmt, ...) qsee_log(QSEE_LOG_MSG_ERROR, "|E:%s:%3d| " xx_fmt, __func__, __LINE__, ##__VA_ARGS__)

        // Warning message
        #define LOG_W(xx_fmt, ...) qsee_log(QSEE_LOG_MSG_HIGH,  "|W| "        xx_fmt, ##__VA_ARGS__)
        // Debug (data) log message
        #define LOG_D(xx_fmt, ...) qsee_log(QSEE_LOG_MSG_DEBUG, "|D| "        xx_fmt, ##__VA_ARGS__)
        // Information (status) message
        #define LOG_I(xx_fmt, ...) qsee_log(QSEE_LOG_MSG_HIGH,  "|I| "        xx_fmt, ##__VA_ARGS__)
    #endif
#endif

#if defined(TZ_MODEL_BLOWFISH)

    #define LOG_Raw(xx_fmt, ...) printf(TA_LOG_TAG "|R:%s:%3d|" xx_fmt EOL, __func__, __LINE__, ##__VA_ARGS__)

    #ifndef EOL
        #define EOL "\n"
    #endif

    #ifdef TA_RELEASE
        #define LOG_D(xx_fmt,...)
    #else
        #define LOG_D(xx_fmt, ...) printf(TA_LOG_TAG "|D|" xx_fmt EOL, ##__VA_ARGS__)
    #endif

    #define LOG_F(xx_fmt, ...) printf(TA_LOG_TAG "|F:%s:%3d|" xx_fmt EOL, __func__, __LINE__, ##__VA_ARGS__)
    #define LOG_E(xx_fmt, ...) printf(TA_LOG_TAG "|E:%s:%3d|" xx_fmt EOL, __func__, __LINE__, ##__VA_ARGS__)
    #define LOG_W(xx_fmt, ...) printf(TA_LOG_TAG "|W|" xx_fmt EOL, ##__VA_ARGS__)
    #define LOG_I(xx_fmt, ...) printf(TA_LOG_TAG "|I|" xx_fmt EOL, ##__VA_ARGS__)
#endif

#ifdef __DEV_DEBUG__
#define LOG_DEV(xx_fmt, ...)  \
        __LOG_Q__(DEV, xx_fmt, ##__VA_ARGS__)
#else /* __DEV_DEBUG__ */
#define LOG_DEV(xx_fmt, ...)
#endif /* !__DEV_DEBUG__ */

extern uint64_t perf_time_begin;
extern uint64_t perf_time_end;

#define LOG_I_PERF_BEGIN                       \
        do{                                    \
            perf_time_begin = systemTime();    \
        }while(0);

#define LOG_I_PERF_END                         \
        do{                                    \
            perf_time_end = systemTime();      \
            LOG_I("elapsed time: %" PRIu64 "ms", perf_time_end - perf_time_begin); \
        }while(0);


#ifdef __DEV_DEBUG__
#define LOG_FUNC_BEGIN  LOG_DEV("BEGIN----- %s -----", __func__);
#define LOG_FUNC_END    LOG_DEV("END  ----- %s -----", __func__);
#define LOG_I_AM_HERE   LOG_DEV(" ----- [ %s : %s : %d ] -----", __FILE__, __func__, __LINE__);

#define LOG_PERF_BEGIN                         \
        do{                                    \
            perf_time_begin = systemTime();    \
        }while(0);

#define LOG_PERF_END                           \
        do{                                    \
            perf_time_end = systemTime();      \
            LOG_DEV("elapsed time: %" PRIu64 "ms", perf_time_end - perf_time_begin); \
        }while(0);

#else
#define LOG_FUNC_BEGIN
#define LOG_FUNC_END
#define LOG_I_AM_HERE
#define LOG_PERF_BEGIN
#define LOG_PERF_END
#endif

const char* getTeeErrorText(TEE_Result status);

/**
 * @brief Prints byte-array in hex to kernel log with DEBUG tag.
 * @param[in] ba - pointer to byte-array.
 * @param[in] length - size of byte-array.
 * @param[in] label - label message or NULL.
 */
void logRawByteArrayHex(const uint8_t* ba, size_t length, const char* label);

/**
 * @brief Prints byte-array in hex to kernel log with DEBUG tag.
 * @param[in] ba - pointer to byte-array.
 * @param[in] length - size of byte-array.
 * @param[in] label - label message or NULL.
 */
void logByteArrayHex(const uint8_t* ba, size_t length, const char* label);

/**
 * @brief Prints byte-array to kernel log with DEBUG tag.
 * @param[in] ba - pointer to byte-array.
 * @param[in] length - size of byte-array.
 * @param[in] label - label message or NULL.
 */
void logByteArrayString(const uint8_t* ba, size_t length, const char* label);

/**
 * @brief Prints GP TEE error to log as a test message.
 * @param[in] status - TEE error code.
 * @param[in] extraMessage - extra message, it's useful to pass function name.
 */
void logTeeError(TEE_Result status, const char* const extraMessage);

#ifdef TRANSMIT_LOG_TO_CA
void pushStackLog(char *str, int len);
bool isStackLogEmpty();
char* popStackLog(int*);
#endif

#endif // __TA_LOGGER_H__ 
