#ifndef __TA_LOGGER_H__
#define __TA_LOGGER_H__

#include <stdio.h>

#include "tzWrappers/TzwCommon.h"

// Kernel logging tag
#define TA_LOG_TAG  __TA_LOG_TAG__

//#undef TRANSMIT_LOG_TO_CA
#define TRANSMIT_LOG_TO_CA

#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

// Macros to trace the function calls
// TODO (v.kopp): Consider using '-finstrument-functions' gcc option
//                https://balau82.wordpress.com/2010/10/06/trace-and-profile-function-calls-with-gcc/
#define LOG_FUNC_BEGIN  LOG_D("BEGIN----- %s -----", __func__)
#define LOG_FUNC_END    LOG_D("END  ----- %s -----", __func__)

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
