/*
 * @file tl_utils.c
 * @brief Code for handling SOFTSIM commands
 * Copyright (c) 2015, Samsung Electronics Corporation. All rights reserved.
 */
/*#include <comdef.h>*/
#include "target.h"
#include <stdio.h>
#include <stdarg.h>

#include "tl_utils.h"
#include "tl_softsim_logic.h"
#include "tl_heap.h"
#include "tl_log.h"

#define l1 "%02x"
#define l2 l1 l1
#define l4 l2 l2
#define l8 l4 l4
#define l16 l8 l8
#define l32 l16 l16

#define v1(i) tmp[i]
#define v2(i) v1(i), v1(i+1)
#define v4(i) v2(i), v2(i+2)
#define v8(i) v4(i), v4(i+4)
#define v16(i) v8(i), v8(i+8)
#define v32(i) v16(i), v16(i+16)

void dump_bytes(const char *prompt, uint8_t *buff, uint32_t size){
    /*SOFTSIM_LOGD("file: %s, func: %s", __FILE_NAME__, __func__);*/
    uint8_t *tmp = buff;

    if(NULL == tmp ) {
        SOFTSIM_LOGD("========= buff is null=======");
        return;
    }

    if(size > SOFTSIM_INPUT_LEN) {
        SOFTSIM_LOGD("dump_bytes size is too large");
        return;
    }

    SOFTSIM_LOGD("=========START: %s=======", prompt);

    uint32_t i;
    for(i = 0; i + 31 < size; i += 32) {
        SOFTSIM_LOGD(l32, v32(i));
    }

    if (i < size) {
        uint8_t buf[32] = {0};
        for (uint32_t j = i; j < size; j++) {
            buf[j - i] = tmp[j];
        }

        tmp = buf;
        SOFTSIM_LOGD(l32, v32(0));
    }

    SOFTSIM_LOGD("=========END(%d): %s=======", size, prompt);
}

void dump_strings(const char *prompt, const char *string){
    /*SOFTSIM_LOGD("file: %s, func: %s", __FILE_NAME__, __func__);*/
    const uint32_t span = 0x6f;
    if(NULL == string) {
        SOFTSIM_LOGD("========= string is null=======");
        return;
    }

    char *tmp = (char*)string;
    char ptr[span + 1];
    uint32_t off = 0;

    SOFTSIM_LOGD("=========START: %s=======", prompt);
    while(off < strnlen(string, ITEM_SIZE)){
        memcpy((uint8_t*) ptr, (uint8_t*)(tmp + off), span);
        ptr[span] = 0;
        off += span;
        SOFTSIM_LOGD("%s", ptr);
    }
    SOFTSIM_LOGD("=========END(%d): %s=======", strnlen(string, ITEM_SIZE), prompt);
}

#define mk_pack_usim_line(_name)     \
    ptr = (char *)usim->_name;       \
    /*SOFTSIM_LOGD(#_name": %s, len: %d", ptr, strlen(ptr));*/ \
    if(NULL != ptr ){                \
        size_t len = strlen(ptr);        \
        *((uint32_t*)(buffer + pos)) = (uint32_t)len; \
        pos += 4;                        \
        memcpy((char*)(buffer + pos), ptr, len); \
        pos += len;              \
    } else {                     \
        *((uint32_t*)(buffer + pos)) = 0;  \
        pos += 4;   \
    }

#define plmn_mk_pack_usim_line(_name) mk_pack_usim_line(_name)
void tl_pack_usim_to_buffer(usim_info_t *usim, uint8_t *buffer, uint32_t *buf_size){
    /*SOFTSIM_LOGD("file: %s, func: %s", __FILE_NAME__, __func__);*/
    uint32_t pos = 0;
    char * ptr = NULL;

    mk_functions_line(mk_pack_usim_line);

    *buf_size = pos;
    /*dump_bytes("dump usim:", buffer, pos);*/
}

#define mk_pack_usim_le_line(_name)     \
    ptr = (char *)usim->_name;       \
    if (NULL != ptr) {               \
        size_t len = strlen(ptr);        \
        /*SOFTSIM_LOGD(#_name": %s, len: %d", ptr, strlen(ptr)); */ \
        *((uint32_t*)(buffer + pos)) = CHANGE_UINT32T_ENDIAN(((uint32_t)len)); \
        pos += 4;                          \
        memcpy((char*)(buffer + pos), ptr, len); \
        pos += len;                        \
    } else {                               \
        *((uint32_t*)(buffer + pos)) = 0;  \
        pos += 4;                          \
    }
#define plmn_mk_pack_usim_le_line(_name) mk_pack_usim_le_line(_name)
void tl_pack_usim_to_buffer_be(usim_info_t *usim, uint8_t *buffer, uint32_t *buf_size){
    /*SOFTSIM_LOGD("file: %s, func: %s", __FILE_NAME__, __func__);*/
    uint32_t pos = 0;
    char * ptr = NULL;

    mk_functions_line(mk_pack_usim_le_line);

    *buf_size = pos;
    /*dump_bytes("dump usim:", buffer, pos);*/
}

#define mk_restore_usim_line(_name)           \
    /*SOFTSIM_LOGD(">>>>start of "#_name);  */\
    len = *(uint32_t*)buffer;                 \
    buffer += 4;                              \
    *size += 4;                               \
    memcpy((char*)usim->_name, buffer, len);  \
    usim->_name[len] = 0;                     \
    buffer += len;                            \
    *size += len;                             \
    /*SOFTSIM_LOGD("<<<<endf of "#_name);*/

#define plmn_mk_restore_usim_line(_name)      \
    /*SOFTSIM_LOGD(">>>>start of "#_name);  */\
    len = *(uint32_t*)buffer;                 \
    buffer += 4;                              \
    *size += 4;                               \
    usim->_name = tl_malloc(len + 1);         \
    if (usim->_name == NULL) {                \
        SOFTSIM_LOGE("plmn_mk_restore_usim_line tl_malloc error"); \
        return;                               \
    }                                         \
    memcpy((char*)usim->_name, buffer, len);  \
    usim->_name[len] = 0;                     \
    buffer += len;                            \
    *size += len;                             \
    /*SOFTSIM_LOGD("<<<<endf of "#_name);*/

void tl_restore_usim_from_buffer(usim_info_t *usim,const char *buffer, uint32_t *size){
    /*SOFTSIM_LOGD("file: %s, func: %s", __FILE_NAME__, __func__);*/

    int len = 0;
    *size = 0;

    mk_functions_line(mk_restore_usim_line);
}

char *get_char_pointer(uint8_t *buff, int *start, uint32_t *_size){
    str_block_t *str = (str_block_t*)(buff + *start);

    char *pointer = (char*)str->payload;
    uint32_t size = GET_UINT32T_LE(str->size);
    uint32_t total = GET_UINT32T_LE(str->total);
    /*SOFTSIM_LOGD("total = 0x%08x, len = 0x%08x, layload = %s", total, size, pointer);*/
    if(total > SOFTSIM_INPUT_LEN) {
        total = SOFTSIM_INPUT_LEN;
    }
    *start += total;
    if(*start > SOFTSIM_INPUT_LEN) {
        *start = SOFTSIM_INPUT_LEN;
    }
    if(size < (SOFTSIM_INPUT_LEN - 8)) {
        pointer[size] = 0;
    } else {
        pointer[SOFTSIM_INPUT_LEN - 9] = 0;
    }

    if( _size != NULL)
        *_size = size;

    return pointer;
}

uint8_t *get_buffer(uint8_t *buff, uint32_t *size){
    int start = 0;

    uint8_t *ptr = (uint8_t*)get_char_pointer(buff, &start, size );

    return ptr;
}

#define mk_parse_usim_line(name) \
    char *name = get_char_pointer(buffer, &start, NULL); \
    strlcpy((char*)(usim->name), name, ITEM_SIZE); \
    SOFTSIM_LOGD(#name"= %s", name);

#define plmn_mk_parse_usim_line(name) \
    char *name = get_char_pointer(buffer, &start, NULL); \
    usim->name = tl_malloc(strlen(name)+1);           \
    if(name == NULL || usim->name == NULL) { \
        SOFTSIM_LOGE("plmn_mk_parse_usim_line error."); \
        return; \
    } \
    strcpy((char*)(usim->name), name); \
    dump_strings(#name, (const char*)usim->name);

void tl_parse_usim(uint8_t *buffer, usim_info_t *usim){
    SOFTSIM_LOGD("file: %s, func: %s", __FILE_NAME__, __func__);

    int start= 0;

    mk_functions_line(mk_parse_usim_line);
}

void tl_parse_imsi(uint8_t *buffer, uint8_t *imsi){
    /*SOFTSIM_LOGD("file: %s, func: %s", __FILE_NAME__, __func__);*/

    int start= 0;
    uint32_t size = 0;

    char *name = get_char_pointer(buffer, &start, &size); \

    /*SOFTSIM_LOGD("=====%s(%d)=======", name, size);*/
    if (size > 64) {
        SOFTSIM_LOGE("tl_parse_imsi overflow");
        return;
    }
    name[size] = 0;

    strlcpy((char*)imsi, name, ITEM_SIZE); \
}

#define mk_dump_line(name) \
    SOFTSIM_LOGD(#name"= %s, len: %d", usim->name, strlen((const char *)usim->name));
#define plmn_mk_dump_line(name) \
    dump_strings(#name, (const char*)usim->name);
void dump_usim_info(usim_info_t *usim){
    SOFTSIM_LOGD("file: %s, func: %s", __FILE_NAME__, __func__);

    mk_functions_line(mk_dump_line);
}

void dump_usim_info_in_bytes(usim_info_t *usim){
    uint8_t buff[4096];
    uint32_t size;
    tl_pack_usim_to_buffer(usim, buff, &size);
    /*dump_bytes("dump usim info in bytes : ", buff, size);*/
}

void tl_parse_encrypt_data(uint8_t *buffer,
        uint8_t *plain_text, uint32_t *text_size, uint8_t *aes_key, uint32_t *key_size,
        uint8_t *aes_iv, uint32_t *iv_size){

    int start = 0;
    uint8_t *buff = NULL;
    uint32_t size = 0;

    buff= (uint8_t*)get_char_pointer(buffer, &start, &size);
    if (size > 1024) {
        SOFTSIM_LOGE("tl_parse_encrypt_data plain_text overflow");
        return;
    }
    *text_size = size;
    memcpy(plain_text, buff, size);

    buff= (uint8_t*)get_char_pointer(buffer, &start, &size);
    if (size > 1024) {
        SOFTSIM_LOGE("tl_parse_encrypt_data aes_key overflow");
        return;
    }
    *key_size = size;
    memcpy(aes_key, buff, size);

    buff= (uint8_t*)get_char_pointer(buffer, &start, &size);
    if (size > 1024) {
        SOFTSIM_LOGE("tl_parse_encrypt_data aes_iv overflow");
        return;
    }
    *iv_size = size;
    memcpy(aes_iv, buff, size);
}

void tl_parse_decrypt_data(uint8_t *buffer,
        uint8_t *cipher_data, uint32_t *cipher_data_size, uint8_t *aes_iv, uint32_t *iv_size,
        uint8_t * auth_tag, uint32_t *tag_size){

    int start = 0;
    uint8_t *buff = NULL;
    uint32_t size = 0;

    buff= (uint8_t*)get_char_pointer(buffer, &start, &size);
    *cipher_data_size = size;
    memcpy(cipher_data, buff, size);

    buff= (uint8_t*)get_char_pointer(buffer, &start, &size);
    *iv_size = size;
    memcpy(aes_iv, buff, size);

    buff= (uint8_t*)get_char_pointer(buffer, &start, &size);
    *tag_size = size;
    memcpy(auth_tag, buff, size);
}

void tl_parse_decrypt_data_default_IV(uint8_t *buffer,
        uint8_t *cipher_data, uint32_t *cipher_data_size,
        uint8_t * auth_tag, uint32_t *tag_size){

    SOFTSIM_LOGD("func: %s", __func__);

    int start = 0;
    uint8_t *buff = NULL;
    uint32_t size = 0;

    buff= (uint8_t*)get_char_pointer(buffer, &start, &size);
    if (size - 16 > 1024) {
        size = 1024 + 16;
    }
    *cipher_data_size = size - 16;
    memcpy(cipher_data, buff, size - 16);

    /*buff= (uint8_t*)get_char_pointer(buffer, &start, &size);*/
    /**tag_size = size;*/
    memcpy(auth_tag, buff + size - 16, 16);
    *tag_size = 16;
}

int tl_json_parse_imsi(const char * json_str, char *imsi ){
    SOFTSIM_LOGD("file: %s, func: %s", __FILE_NAME__, __func__);
    /*SOFTSIM_LOGD("json_str: %s", json_str);*/

    cJSON *root;
    root = cJSON_Parse(json_str);
    cJSON *_json_imsi= cJSON_GetObjectItem(root, "imsi");
    if(!_json_imsi) {
        SOFTSIM_LOGD("no imsi ! ");
        return -1;
    }
    /*SOFTSIM_LOGD("imsi type is %d", _json_imsi->type);*/
    /*SOFTSIM_LOGD("imsi is %s", _json_imsi->valuestring);*/

    if ( NULL == imsi)
        return -1;

    strcpy(imsi, (const char*)_json_imsi->valuestring);

    return 0;
}

#ifndef _NDEBUG_
#define SOFTSIM_PRINTF_BUFF_MAX_SIZE 512
static char static_buff[SOFTSIM_PRINTF_BUFF_MAX_SIZE];
#endif

void softsim_printf(const char *fmt, ...){
#ifndef _NDEBUG_
    int n;
    va_list ap;
    int size = SOFTSIM_PRINTF_BUFF_MAX_SIZE;
    char *p;

    if(NULL == fmt){
        return;
    }

    memset(static_buff, 0, SOFTSIM_PRINTF_BUFF_MAX_SIZE);

    p = (char*)static_buff;

    va_start(ap, fmt);
    n = vsnprintf(p, size, fmt, ap);
    va_end(ap);
    if (n > -1 && n < size){
        SOFTSIM_LOGD("[ LOGIC ]%s", p);
    }
#endif
}

char *tl_basename(char *path) {
    if (path == NULL)
        return path;

    size_t size = strlen(path);
    while (size > 0) {
        if (path[size-1] == '/')
            return &path[size];
        size--;
    }
    return path;
}

char byte_to_char(uint8_t b){

    if( b <= 9){
        return  b + '0';
    }

    if( b >= 0xa && b <= 0xf){
        return b - 0xa + 'a' ;
    }

    return '0';
}


//caution
void tl_bytes_to_hexstring(uint8_t *bytes, uint32_t byte_len, char *hexstring){

    for(uint8_t i = 0; i < byte_len; i++){
        uint8_t b = bytes[i];
        uint8_t lsb = b & 0xf;
        uint8_t msb = (b>>4) & 0xf;

        hexstring[ i*2 ] = byte_to_char(msb);
        hexstring[ i*2 + 1] = byte_to_char(lsb);
    }
}
