#ifndef __TA_LOGGER_H__
#define __TA_LOGGER_H__

#include <stdio.h>
#include <inttypes.h>

#include "ifaa_ta_config.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) /*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

#elif defined(TZ_MODEL_BLOWFISH) /*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

    #define __LOG_P__(xx_tag, xx_fmt, ...)  {    \
        printf("[TEEgris:" TA_LOG_TAG "] <" #xx_tag ">: [ %s@%3d ]: " xx_fmt EOL, __func__, __LINE__, ##__VA_ARGS__); \
    }

    #define __LOG_Q__(xx_tag, xx_fmt, ...)  {    \
        printf("[TEEgris:" TA_LOG_TAG "] <" #xx_tag ">: [%s - %s@%3d ]: " xx_fmt EOL, __FILE__, __func__, __LINE__, ##__VA_ARGS__); \
    }

#ifdef TRANSMIT_LOG_TO_CA
    #define __LOG__(xx_tag, xx_fmt, ...)   { \
        __LOG_P__(xx_tag, xx_fmt, ##__VA_ARGS__)  \
        char str4ca[LOG_BUFF_ERR_MAX_LEN]; \
        int strlen = snprintf(str4ca, LOG_BUFF_ERR_MAX_LEN, "[TEEgris:" TA_LOG_TAG "] <" #xx_tag ">: [ %s@%3d ]: " xx_fmt EOL, __func__, __LINE__, ##__VA_ARGS__); \
        if(strlen >  LOG_BUFF_ERR_MAX_LEN - LOG_BUFF_GAP_LEN) { \
            LOG_Raw("***MAX size(%d) of log buffer is too small !!!***", LOG_BUFF_MAX_LEN); \
        }   \
        pushStackLog(str4ca, strlen); \
    }

    #define LOG_F(xx_fmt, ...)   \
        __LOG__(F, xx_fmt, ##__VA_ARGS__);

    #define LOG_E(xx_fmt, ...)   \
        __LOG__(E, xx_fmt, ##__VA_ARGS__);

    #define LOG_I(xx_fmt, ...)   \
        __LOG__(I, xx_fmt, ##__VA_ARGS__);

#ifndef TA_RELEASE

    #define __LOG_D__(xx_tag, xx_fmt, ...) { \
        __LOG_P__(xx_tag, xx_fmt, ##__VA_ARGS__)  \
        char str4ca[LOG_BUFF_MAX_LEN]; \
        int strlen = snprintf(str4ca, LOG_BUFF_MAX_LEN, "[TEEgris:" TA_LOG_TAG "] <" #xx_tag ">: " xx_fmt EOL, ##__VA_ARGS__); \
        if(strlen >  LOG_BUFF_MAX_LEN - LOG_BUFF_GAP_LEN) { \
            LOG_Raw("***MAX size(%d) of log buffer is too small !!!***", LOG_BUFF_MAX_LEN); \
        }   \
        pushStackLog(str4ca, strlen); \
    }

    #define LOG_W(xx_fmt, ...)  \
        __LOG_D__(W, xx_fmt, ##__VA_ARGS__)

    #define LOG_D(xx_fmt, ...)  \
        __LOG_D__(D, xx_fmt, ##__VA_ARGS__)

#else /*TA_RELEASE*/
    #define LOG_W(...)
    #define LOG_D(...)
#endif /* !TA_RELEASE */

#else /*TRANSMIT_LOG_TO_CA*/
    #ifndef TA_RELEASE
        #define LOG_D(xx_fmt, ...)  \
            __LOG_P__(D, xx_fmt, ##VA_ARGS__)
    #else
        #define LOG_D(xx_fmt,...)
    #endif

    #define LOG_F(xx_fmt, ...)  \
            __LOG_P__(F, xx_fmt, ##VA_ARGS__)

    #define LOG_E(xx_fmt, ...)  \
            __LOG_P__(E, xx_fmt, ##VA_ARGS__)

    #define LOG_W(xx_fmt, ...)  \
            __LOG_P__(W, xx_fmt, ##VA_ARGS__)

    #define LOG_I(xx_fmt, ...)  \
            __LOG_P__(I, xx_fmt, ##VA_ARGS__)

#endif /* !TRANSMIT_LOG_TO_CA */

#elif defined(TZ_MODEL_Kinibi) /*TZ_MODEL_Kinibi*/

    #define LOG_Raw(xx_fmt, ...) TEE_LogPrintf(TA_LOG_TAG "|R:%s:%3d|" xx_fmt EOL, __func__, __LINE__, ##__VA_ARGS__)

    #ifndef EOL
        #define EOL "\n"
    #endif

    #define __LOG_P__(xx_tag, xx_fmt, ...)  {    \
        TEE_LogPrintf("[Kinibi:" TA_LOG_TAG "] <" #xx_tag ">: [ %s@%3d ]: " xx_fmt EOL, __func__, __LINE__, ##__VA_ARGS__); \
    }

    #define __LOG_Q__(xx_tag, xx_fmt, ...)  {    \
        TEE_LogPrintf("[Kinibi:" TA_LOG_TAG "] <" #xx_tag ">: [%s - %s@%3d ]: " xx_fmt EOL, __FILE__, __func__, __LINE__, ##__VA_ARGS__); \
    }

#ifdef TRANSMIT_LOG_TO_CA
    #define __LOG__(xx_tag, xx_fmt, ...)   { \
        __LOG_P__(xx_tag, xx_fmt, ##__VA_ARGS__)  \
        char str4ca[LOG_BUFF_ERR_MAX_LEN]; \
        int strlen = snprintf(str4ca, LOG_BUFF_ERR_MAX_LEN, "[Kinibi:" TA_LOG_TAG "] <" #xx_tag ">: [ %s@%3d ]: " xx_fmt EOL, __func__, __LINE__, ##__VA_ARGS__); \
        if(strlen >  LOG_BUFF_ERR_MAX_LEN - LOG_BUFF_GAP_LEN) { \
            LOG_Raw("***MAX size(%d) of log buffer is too small !!!***", LOG_BUFF_MAX_LEN); \
        }   \
        pushStackLog(str4ca, strlen); \
    }

    #define LOG_F(xx_fmt, ...)   \
        __LOG__(F, xx_fmt, ##__VA_ARGS__);

    #define LOG_E(xx_fmt, ...)   \
        __LOG__(E, xx_fmt, ##__VA_ARGS__);

    #define LOG_I(xx_fmt, ...)   \
        __LOG__(I, xx_fmt, ##__VA_ARGS__);

#ifndef TA_RELEASE

    #define __LOG_D__(xx_tag, xx_fmt, ...) { \
        __LOG_P__(xx_tag, xx_fmt, ##__VA_ARGS__)  \
        char str4ca[LOG_BUFF_MAX_LEN]; \
        int strlen = snprintf(str4ca, LOG_BUFF_MAX_LEN, "[Kinibi:" TA_LOG_TAG "] <" #xx_tag ">: " xx_fmt EOL, ##__VA_ARGS__); \
        if(strlen >  LOG_BUFF_MAX_LEN - LOG_BUFF_GAP_LEN) { \
            LOG_Raw("***MAX size(%d) of log buffer is too small !!!***", LOG_BUFF_MAX_LEN); \
        }   \
        pushStackLog(str4ca, strlen); \
    }

    #define LOG_W(xx_fmt, ...)  \
        __LOG_D__(W, xx_fmt, ##__VA_ARGS__)

    #define LOG_D(xx_fmt, ...)  \
        __LOG_D__(D, xx_fmt, ##__VA_ARGS__)

#else /*TA_RELEASE*/
    #define LOG_W(...)
    #define LOG_D(...)
#endif /* !TA_RELEASE */

#else /*TRANSMIT_LOG_TO_CA*/
    #ifndef TA_RELEASE
        #define LOG_D(xx_fmt, ...)  \
            __LOG_P__(D, xx_fmt, ##VA_ARGS__)
    #else
        #define LOG_D(xx_fmt,...)
    #endif

    #define LOG_F(xx_fmt, ...)  \
            __LOG_P__(F, xx_fmt, ##VA_ARGS__)

    #define LOG_E(xx_fmt, ...)  \
            __LOG_P__(E, xx_fmt, ##VA_ARGS__)

    #define LOG_W(xx_fmt, ...)  \
            __LOG_P__(W, xx_fmt, ##VA_ARGS__)

    #define LOG_I(xx_fmt, ...)  \
            __LOG_P__(I, xx_fmt, ##VA_ARGS__)

#endif /* !TRANSMIT_LOG_TO_CA */

#else
#error("TEE is not defined yet")
#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__
