#include <stdarg.h>
#include <ctype.h>
#include <swd_log.h>
#include <cmd_serializer.h>
#include <hwvault_utils.h>


#define MAX_STR_LEN 2048
#define MAX_GET_PRINT 5

#ifdef DEBUG_FNAME
#define KM_LOG_FMT "%s[%s] (%s:%d)[%s] "
#else /* ifdef DEBUG_FNAME */
#define KM_LOG_FMT "%s[%s] (%s:%d) "
#endif /* ifdef DEBUG_FNAME */

#define KM_LOG_NAME "hwvault "

char _swd_str[256];

#ifdef NW_LOGGING
static item_list_t *log_list;
// assume single thread
static uint8_t logging;
#define START_LOGGING   {\
                            if (logging) return;\
                            else logging = 1;\
                        }

#define FINISH_LOGGING   {\
                            logging = 0;\
                        }


static void swd_logs_new(void) {
    if (log_list)
        swd_logs_free();
    log_list = create_item_list(10);
}

void swd_logs_free(void) {
    free_item_list(log_list);
    log_list = NULL;
}

void *swd_get_logs(void) {
    return log_list;
}

void swd_push_log(const char *str) {
    item_t *m = NULL;
    BOOL r = FALSE;

    START_LOGGING;

    if (!log_list)
        swd_logs_new();
    CHECK_NOT_NULL_L(log_list, exit);

    r = add_bytes_item(log_list, HV_TAG_LOG, (const uint8_t *)str, MIN(strlen(str) + 1, MAX_STR_LEN));
    if (log_list->size > 100) {
        int r;
        r = shrink_items(log_list, 60, 50);
        LOGW("shrink items %d", r);
    }

    CHECK_EQ_L(r, TRUE, exit);

exit:
    FINISH_LOGGING;
}
#else
void swd_logs_free(void) {
}

void *swd_get_logs(void) {
    return NULL;
}

void swd_push_log(const char *str) {
}
#endif

char *km_get_log_fmt(const int lvl, __attribute__((unused)) const char *file, const char *func, const int l)
{
    int ret;
    static char log_fmt[256];
    static char err_str[] = { "\n*** Logging ERORROR ***\n" };
    static char *lvl_str[] = {
#ifdef EMU_X86
        "\e[1mINF\e[0m", /* bold */
        "\e[97mDBG\e[0m", /* white */
        "\e[93mWRN\e[0m", /* light yellow */
        "\e[91mERR\e[0m", /* light red */
#else /* ifdef EMU_X86 */
        "INF",
        "DBG",
        "WRN",
        "ERR",
#endif /* ifdef EMU_X86 */
    };

#ifdef DEBUG_FNAME
    ret = snprintf(log_fmt, sizeof(log_fmt), KM_LOG_FMT, KM_LOG_NAME, lvl_str[lvl], file, l, func);
#else /* ifdef DEBUG_FNAME */
    ret = snprintf(log_fmt, sizeof(log_fmt), KM_LOG_FMT, KM_LOG_NAME, lvl_str[lvl], func, l);
#endif /* ifdef DEBUG_FNAME */

    if ((ret < 0))
        return err_str;

    if ((size_t)ret > sizeof(log_fmt))
        return err_str;

    log_fmt[sizeof(log_fmt) - 1] = 0;

    return log_fmt;
}

/* This function need to avoid "unused" warnings if TL_DEBUG is not defined */
inline void no_printf(const char *fmt, ...)
{
    (void)fmt;
}

void km_dump_hex(const char *title, const void *_data, int length)
{
    char out[512] = { 0 };
    int i, pos = 0;
    unsigned char *data = (unsigned char *)_data;
    static const char hexes[] = "0123456789abcdef";

    LOGW("%s[%d] : ", title, length);

    for (i = 0; i < length; i++)
    {
        if ((i % 16 == 0) && (i != 0))
        {
            out[pos] = 0;
            LOGW("%s", out);
            pos = 0;
        }
        out[pos++] = hexes[data[i] >> 4];
        out[pos++] = hexes[data[i] & 0xF];
        out[pos++] = ' ';
    }
    out[pos] = 0;
    LOGW("%s", out);
}


#ifdef TL_DEBUG

#define HEXDUMP_COLS 16
#define KM_PRIx64_FMT "0x%08x%08x"
#define PRI_MASK (~0ull >> (sizeof(unsigned) * 8))
#define KM_PRIx64_PRN(a) (unsigned)((a& ~PRI_MASK) >> sizeof(unsigned) * 8), (unsigned)(a& PRI_MASK)

void km_dump(const void *b, int len)
{
    char buf[512];
    int i, j, pos = 0;

    for (i = 0; i < len + ((len % HEXDUMP_COLS) ? (HEXDUMP_COLS - len % HEXDUMP_COLS) : 0); i++)
    {
        if (i % HEXDUMP_COLS == 0)
            pos += snprintf(buf + pos, sizeof(buf) - pos, "0x%06x: ", i);  /* print offset */

        if (i < len)
            pos += snprintf(buf + pos, sizeof(buf) - pos, "%02x ", 0xff & ((char *)b)[i]);  /* print hex data */
        else
            pos += snprintf(buf + pos, sizeof(buf) - pos, "   ");  /* end of block, just aligning for ASCII dump */

        if (i % HEXDUMP_COLS == (HEXDUMP_COLS - 1)) /* print ASCII dump */
        {
            while (pos < HEXDUMP_COLS * 3 + 10)
                pos += snprintf(buf + pos, sizeof(buf) - pos, " ");  /* align last line */

            for (j = i - (HEXDUMP_COLS - 1); j <= i; j++)
                if (j >= len) /* end of block, not really printing */
                    pos += snprintf(buf + pos, sizeof(buf) - pos, " ");
                else if (isprint(((char *)b)[j])) /* printable char */
                    pos += snprintf(buf + pos, sizeof(buf) - pos, "%c", 0xff & ((char *)b)[j]);
                else /* other char */
                    pos += snprintf(buf + pos, sizeof(buf) - pos, ".");

            LOGD("%s", buf);
            pos = 0;
        }
    }
}
#endif
