#include "ta_logger.h"
#include "ifaa_ta_commandId.h"

#include "tzWrappers/TzwMemory.h"
#include "tzWrappers/TzwString.h"
#include "tzWrappers/TzwCommon.h"

#include <stdio.h>


typedef struct TeeError {
    TEE_Result code;
    const char* text;
} TeeError_t;

static const TeeError_t TEE_RESULT_ERROR_LIST[] = {
    { TEE_SUCCESS,                          "TEE_SUCCESS" },
    { TEE_ERROR_CORRUPT_OBJECT,             "TEE_ERROR_CORRUPT_OBJECT" },
    { TEE_ERROR_CORRUPT_OBJECT_2,           "TEE_ERROR_CORRUPT_OBJECT_2" },
    { TEE_ERROR_STORAGE_NOT_AVAILABLE,      "TEE_ERROR_STORAGE_NOT_AVAILABLE" },
    { TEE_ERROR_STORAGE_NOT_AVAILABLE_2,    "TEE_ERROR_STORAGE_NOT_AVAILABLE_2" },
    { TEE_ERROR_GENERIC,                    "TEE_ERROR_GENERIC" },
    { TEE_ERROR_ACCESS_DENIED,              "TEE_ERROR_ACCESS_DENIED" },
    { TEE_ERROR_CANCEL,                     "TEE_ERROR_CANCEL" },
    { TEE_ERROR_ACCESS_CONFLICT,            "TEE_ERROR_ACCESS_CONFLICT" },
    { TEE_ERROR_EXCESS_DATA,                "TEE_ERROR_EXCESS_DATA" },
    { TEE_ERROR_BAD_FORMAT,                 "TEE_ERROR_BAD_FORMAT" },
    { TEE_ERROR_BAD_PARAMETERS,             "TEE_ERROR_BAD_PARAMETERS" },
    { TEE_ERROR_BAD_STATE,                  "TEE_ERROR_BAD_STATE" },
    { TEE_ERROR_ITEM_NOT_FOUND,             "TEE_ERROR_ITEM_NOT_FOUND" },
    { TEE_ERROR_NOT_IMPLEMENTED,            "TEE_ERROR_NOT_IMPLEMENTED" },
    { TEE_ERROR_NOT_SUPPORTED,              "TEE_ERROR_NOT_SUPPORTED" },
    { TEE_ERROR_NO_DATA,                    "TEE_ERROR_NO_DATA" },
    { TEE_ERROR_OUT_OF_MEMORY,              "TEE_ERROR_OUT_OF_MEMORY" },
    { TEE_ERROR_BUSY,                       "TEE_ERROR_BUSY" },
    { TEE_ERROR_COMMUNICATION,              "TEE_ERROR_COMMUNICATION" },
    { TEE_ERROR_SECURITY,                   "TEE_ERROR_SECURITY" },
    { TEE_ERROR_SHORT_BUFFER,               "TEE_ERROR_SHORT_BUFFER" },
    { TEE_PENDING,                          "TEE_PENDING" },
    { TEE_ERROR_TIMEOUT,                    "TEE_ERROR_TIMEOUT" },
    { TEE_ERROR_OVERFLOW,                   "TEE_ERROR_OVERFLOW" },
    { TEE_ERROR_TARGET_DEAD,                "TEE_ERROR_TARGET_DEAD" },
    { TEE_ERROR_STORAGE_NO_SPACE,           "TEE_ERROR_STORAGE_NO_SPACE" },
    { TEE_ERROR_MAC_INVALID,                "TEE_ERROR_MAC_INVALID" },
    { TEE_ERROR_SIGNATURE_INVALID,          "TEE_ERROR_SIGNATURE_INVALID" },
    { TEE_ERROR_TIME_NOT_SET,               "TEE_ERROR_TIME_NOT_SET" },
    { TEE_ERROR_TIME_NEEDS_RESET,           "TEE_ERROR_TIME_NEEDS_RESET" },
    #ifdef TEEC_ERROR_TARGET_DEAD
    { TEE_ERROR_TARGET_DEAD,                "TEEC_ERROR_TARGET_DEAD" },
    #endif
    // WARNING!!!
    // Add new elements BEFORE this comment!!!
    { 0, NULL } // End of the list.
};

// Check object type is transient
const char* getTeeErrorText(TEE_Result status);

void logRawByteArrayHex(const uint8_t* ba, size_t length, const char* label) {
    const unsigned char* datai = (const unsigned char*) ba;
    char endLine[60];
    char* pEndLine = &endLine[0];
    int one_line = 0;

    if (!label) {
        label = "";
    }

    if (!datai) {
        LOG_Raw("%s @<null> [%d]", label, length);
        length = 0;
    } else if (!length) {
        LOG_Raw("%s @%p [%d]", label, datai, length);
    } else if (length > 16) {
        LOG_Raw("%s @%p [%d]:", label, datai, length);
    } else {
        one_line = 1;
    }

    while (length > 16) {
        LOG_Raw("%05x: %02x %02x %02x %02x | %02x %02x %02x %02x | %02x %02x %02x %02x | %02x %02x %02x %02x",
                (unsigned) (datai - (const unsigned char*) ba), datai[0], datai[1], datai[2], datai[3], datai[4],
                datai[5], datai[6], datai[7], datai[8], datai[9], datai[10], datai[11], datai[12], datai[13], datai[14],
                datai[15]);
        length -= 16;
        datai += 16;
    }

    if (length) {
        int pEndLine_filled = 0;
        *pEndLine = 0;

        for (size_t i = 0; i < length; ++i) {
            if (i && !(i & 3)) {
                pEndLine_filled += snprintf(pEndLine + pEndLine_filled, sizeof(endLine) - pEndLine_filled, " |");
            }
            pEndLine_filled += snprintf(pEndLine + pEndLine_filled, sizeof(endLine) - pEndLine_filled, " %02x",
                    datai[i]);
            endLine[sizeof(endLine) - 1] = '\0';
        }

        if (one_line) {
            LOG_Raw("%s @%p [%d]: %s", label, datai, length, endLine);
        } else {
            LOG_Raw("%05x:%s", (unsigned) (datai - (const unsigned char*) ba), endLine);
        }
    }
}

void logByteArrayHex(const uint8_t* ba, size_t length, const char* label) {
    (void) ba;
    (void) length;
    (void) label;

#ifndef TA_RELEASE
    const unsigned char* datai = (const unsigned char*) ba;
    char endLine[60];
    char* pEndLine = &endLine[0];
    int one_line = 0;

    if (!label) {
        label = "";
    }

    if (!datai) {
        LOG_D("%s @<null> [%zu]", label, length);
        length = 0;
    } else if (!length) {
        LOG_D("%s @%p [%zu]", label, (void*)datai, length);
    } else if (length > 16) {
        LOG_D("%s @%p [%zu]:", label, (void*)datai, length);
    } else {
        one_line = 1;
    }

    if (length > IFBIO_INPUT_LEN) {
        LOG_E("buffer size exceed the Max available size");
        return;
    }

    while (length > 16) {
        LOG_D("%05x: %02x %02x %02x %02x | %02x %02x %02x %02x | %02x %02x %02x %02x | %02x %02x %02x %02x",
                (unsigned) (datai - (const unsigned char*) ba), datai[0], datai[1], datai[2], datai[3], datai[4],
                datai[5], datai[6], datai[7], datai[8], datai[9], datai[10], datai[11], datai[12], datai[13], datai[14],
                datai[15]);
        length -= 16;
        datai += 16;
    }

    if (length) {
        int pEndLine_filled = 0;
        *pEndLine = 0;

        for (size_t i = 0; i < length; ++i) {
            if (i && !(i & 3)) {
                pEndLine_filled += snprintf(pEndLine + pEndLine_filled, sizeof(endLine) - pEndLine_filled, " |");
            }
            pEndLine_filled += snprintf(pEndLine + pEndLine_filled, sizeof(endLine) - pEndLine_filled, " %02x",
                    datai[i]);
            endLine[sizeof(endLine) - 1] = '\0';
        }

        if (one_line) {
            LOG_D("%s @%p [%zu]: %s", label, (void*)datai, length, endLine);
        } else {
            LOG_D("%05x:%s", (unsigned) (datai - (const unsigned char*) ba), endLine);
        }
    }
#endif
}

void logByteArrayString(const uint8_t* ba, size_t length, const char* label) {
    (void) ba;
    (void) length;
    (void) label;

#ifndef TA_RELEASE
    const char* datai = (const char*) ba;

    LOG_D("%s [%zu]:", label, length);

    if (!datai || length == 0) {
        return;
    }

    if (!label) {
        label = "";
    }

    char buf[255];
    const size_t sizeBuf = sizeof(buf);
    const size_t lenBuf = sizeBuf - 1;
    size_t strLen = 0;
    size_t skip = 0;

    while (length > 0) {
        const char* pch = strchr(datai, '\n');
        skip = 0;
        strLen = length;
        if (pch) {
            if ((int) (pch - datai) <= 0) {
                return;
            }
            strLen = pch - datai;
            skip = 1;
        }
        if (strLen > lenBuf) {
            strLen = lenBuf;
            skip = 0;
        }
        tzwMemFill(buf, 0, sizeBuf);
        tzwMemMove((char*) buf, (char*) datai, strLen);
        LOG_D("%s", buf);
        strLen += skip;
        if ((int) (length - strLen) <= 0) {
            return;
        }
        length -= strLen;
        datai += strLen;
    }
#endif
}

void logTeeError(TEE_Result status, const char* const extraMessage) {
    if (status != TEE_SUCCESS) {
        LOG_E("%s; TEE Error: %s [0x%08x]", extraMessage, getTeeErrorText(status), status);
    }
}

const char* getTeeErrorText(TEE_Result status) {
    for (size_t i = 0; NULL != TEE_RESULT_ERROR_LIST[i].text; ++i) {
        if (TEE_RESULT_ERROR_LIST[i].code == status) {
            return TEE_RESULT_ERROR_LIST[i].text;
        }
    }
    return "Unknown TEE Error Code";
}

#ifdef TRANSMIT_LOG_TO_CA
// StackLog for CA
typedef struct TEELogForCA {
    char *str;
    int len;
    struct TEELogForCA *next;
} TEELogForCA;
static TEELogForCA *log_head = 0, *log_tail = 0;

bool isStackLogEmpty()
{
    return (log_head==0);
}

void pushStackLog(char *str, int len)
{
    char *log = tzwMalloc(len+1);

    if(log == NULL) return;

    tzwMemMove(log, str, len);
    log[len] = 0;

    TEELogForCA *log_struct = tzwMalloc(sizeof(TEELogForCA));

    if(log_struct == NULL) {tzwFree(log); return;}

    log_struct->str = log;
    log_struct->len = len+1;
    log_struct->next = 0;

    if(isStackLogEmpty()) {
        log_head = log_tail = log_struct;
    } else {
        log_tail->next = log_struct;
        log_tail = log_struct;
    }
}

char* popStackLog(int* len)
{
    if(!isStackLogEmpty()) {
        TEELogForCA *log_struct = log_head;
        char *str = (log_struct->str);
        *len = log_struct->len;
        if(log_head == log_tail) {
            log_head = log_tail = 0;
        } else {
            log_head = log_struct->next;
        }
        tzwFree(log_struct);
        return str;
    }
    return 0;
}
#endif
