#include "target.h"
/*#include <comdef.h>*/
#include "x509v3.h"

#include "tl_softsim_logic.h"
#include "tl_utils.h"
#include "tl_fs.h"
#include "tl_errno.h"
#include "tl_heap.h"
#include "tl_log.h"
#include "target.h"

#define USIM_INFO_FILE_NAME        "/efs/softsim/usim.dat"
#define SYMMETRIC_KEY_FILE_NAME    "/efs/softsim/symkey.dat"
#define RSA_PRIVATE_KEY_FILE_NAME  "/efs/softsim/rsa_priv_key.dat"
#define SERVICE_CERT_FILE_NAME     "/efs/softsim/service_cert.dat"
#define DEVICE_CERT_FILE_NAME      "/efs/softsim/device_cert.dat"
#define RSA_CERT_CHAIN_FILE_NAME   "/efs/softsim/rsa_cert_chain.dat"
#define ACTIVE_IMSI_INFO_FILE_NAME "/efs/softsim/active_imsi_info.dat"
#define USIM_MAX_NUM 131


#define FREE_USIM_RES()                        \
    SOFTSIM_LOGD(" =====Ignore Memory Release ========");

#define mk_get_size_of_usim(_name)   \
    len += strlen((char*)usim->_name) + 4;

#define plmn_mk_get_size_of_usim(_name)      \
    len += 4;                                \
    if(NULL != usim->_name) {                \
        len += strlen((char*)usim->_name);   \
    }
static uint32_t get_size_of_usim(usim_info_t *usim){
    /*SOFTSIM_LOGD("file: %s, func: %s", __FILE_NAME__, __func__);*/

    // over 8 bytes occupied by pointer;
    uint32_t len = 0;

    if( NULL == usim){
        return 0;
    }

    mk_functions_line(mk_get_size_of_usim);

    /*dump_usim_info(usim);*/

    SOFTSIM_LOGD("%s, length of usim %d", __func__, len);

    return len;
}

void dump_usim_header(usim_header_t *header) {
    SOFTSIM_LOGD("magic: 0x%08x", header->magic);
    SOFTSIM_LOGD("offset: 0x%08x", header->offset);
    SOFTSIM_LOGD("number: 0x%08x", header->number);
    SOFTSIM_LOGD("length: 0x%08x", header->length);
}

/*
 * usim_group[] should be filled
 * usim_header is assigned
 */
static int _tl_retrieve_all_usim_from_file(usim_header_t ** usim_header_p,
                                           usim_info_t **usim_group_p) {

    /*SOFTSIM_LOGD("file: %s, func: %s", __FILE_NAME__, __func__);*/

    int rc, i = 0, ret = NO_ERROR;
    uint32_t num = 0;
    uint32_t size_of_file, size = 0;

    rc = target_get_file_size(USIM_INFO_FILE_NAME, &size_of_file);
    if(rc != 0 ) {
        SOFTSIM_LOGE("get file size error");
        return -ERR_SFS_GET_FILE_SIZE_FAILED;
    }

    if (size_of_file == 0) {
        SOFTSIM_LOGD("no usim data saved...");
        return -ERR_USIM_NOT_EXIST;
    }

    char *data = tl_malloc(size_of_file);
    if (data == NULL) {
        rc = -ERR_EXCEED_MAX_SIZE;
        goto exit;
    }
    char *tmp = data;
    rc = target_read_file(USIM_INFO_FILE_NAME, (uint8_t *)data, &size_of_file);
    if (rc != 0) {
        SOFTSIM_LOGE(" fail to read usim data...");
        ret = -ERR_SFS_FILE_READ_FAILED;
        goto exit;
    } else {
        /*SOFTSIM_LOGD(" read usim data...");*/
        usim_header_t *usim_header = tl_malloc(sizeof(usim_header_t));
        if (usim_header == NULL) {
            rc = -ERR_EXCEED_MAX_SIZE;
            goto exit;
        }
        *usim_header_p = usim_header;
        memcpy(usim_header, data, sizeof(usim_header_t));
        data += usim_header->offset;
        num = usim_header->number;
        dump_usim_header(usim_header);
        usim_info_t *usim_group = tl_malloc(sizeof(usim_info_t) * num);
        if (usim_group == NULL) {
            rc = -ERR_EXCEED_MAX_SIZE;
            goto exit;
        }
        memset(usim_group, 0, sizeof(usim_info_t) * num);
        *usim_group_p = usim_group;
        for(i = 0; i < num; i++) {
            tl_restore_usim_from_buffer(&usim_group[i], (const char*)data, &size);
            data += size;
            /*dump_usim_info(&usim_group[i]);*/
        }
    }

exit:
    if(NULL != tmp){
        tl_free(tmp);
    }

    return ret;
}

/*
 * return index of imsi in usim_group,
 * if it does not exist, return negative number
 */
static int _tl_find_usim(usim_info_t *usim_group,
                  uint32_t num,
                  uint8_t *imsi) {
    const char *name = (const char *)imsi;

    for(int i = 0; i < num; i++) {
        if( 0 == strcmp(name, (const char*)(usim_group[i].imsi))) {
            return i;
        }
    }

    return -ERR_USIM_NOT_EXIST;
}

int tl_fs_save_usim(usim_info_t *usim) {
    /*SOFTSIM_LOGD("file: %s, func: %s", __FILE_NAME__, __func__);*/

    int rc, i = 0;
    uint8_t *data = NULL;
    uint8_t *tmp;
    int usim_size, file_size;
    int old_usim_size;
    int old_index = INVALID_INDEX;
    uint32_t old_number;
    usim_header_t *usim_header;
    usim_info_t *usim_group;
    rc = _tl_retrieve_all_usim_from_file(&usim_header, &usim_group);
    if (rc == -ERR_USIM_NOT_EXIST) {
        /* no usim exists. create a new file header*/
        usim_header = tl_malloc(sizeof(usim_header_t));
        if (usim_header == NULL) {
            rc = -ERR_EXCEED_MAX_SIZE;
            goto exit;
        }
        usim_header->magic = 0x12345678;
        usim_header->offset = sizeof(usim_header_t);
        usim_header->number = 0;
        usim_header->length = 0; //length of payload(usim)
    } else if (rc != NO_ERROR) {
        goto exit;
    }

    /*SOFTSIM_LOGD("usim data saved...");*/
    for(i = 0; i < usim_header->number; i++) {
        if (0 == strcmp((const char *)usim_group[i].imsi, (const char*)usim->imsi)) {
            SOFTSIM_LOGD("Imsi is same, check plmn");
            if (0 == strcmp((const char *)usim_group[i].plmn, (const char*)usim->plmn)) {
                SOFTSIM_LOGD("Usim info is duplicated..., ignore it");
                goto exit;
            } else {
                SOFTSIM_LOGD("Update usim info");
                old_usim_size = get_size_of_usim(&usim_group[i]);
                old_index = i;
                break;
            }
         }
    }

    if (usim_header->number >= USIM_MAX_NUM) {
        rc = -ERR_EXCEED_MAX_NUM;
        goto exit;
    }

    old_number = usim_header->number;
    usim_size = get_size_of_usim(usim);
    usim_header->length += usim_size;
    if (old_index != INVALID_INDEX) {
        usim_header->length -= old_usim_size;
    } else {
        usim_header->number += 1;
    }
    file_size = sizeof(usim_header_t) + usim_header->length;

    data = tl_malloc(file_size);
    if (data == NULL) {
        rc = -ERR_EXCEED_MAX_SIZE;
        goto exit;
    }
    tmp = data;
    memcpy(tmp, usim_header, sizeof(usim_header_t));
    tmp += sizeof(usim_header_t);
    uint32_t size;
    for(i = 0; i < old_number; i++) {
        if (old_index == i) {
            SOFTSIM_LOGD("Ignore old Usim info");
            continue;
        } else {
            tl_pack_usim_to_buffer(&usim_group[i], (uint8_t*)tmp, &size);
            tmp +=size;
        }
    }
    tl_pack_usim_to_buffer(usim, tmp, &size);
    tmp += size;

    rc = target_write_file(USIM_INFO_FILE_NAME, data, tmp - data);

exit:
    FREE_USIM_RES();

    /*if(NULL != data){*/
        /*tl_free(data);*/
    /*}*/

    return rc;
}

int tl_fs_retrieve_usim_all_be(uint8_t *buffer, uint32_t *len,
                               void mask_func(usim_info_t*)) {
    int rc = 0, i, num;
    uint8_t *tmp;
    usim_header_t *usim_header;
    usim_info_t *usim_group;

    rc = _tl_retrieve_all_usim_from_file(&usim_header, &usim_group);

    if (rc != NO_ERROR) {
        return rc;
    }

    if (usim_header->length + sizeof(usim_header_t) > *len) {
       SOFTSIM_LOGE("output buffer is too small. required %d Bytes, actual %d Bytes", usim_header->length + sizeof(usim_header_t), *len);
       return -1;
    }

    num = usim_header->number;
    // usim_group is ready
    for(i = 0; i < num; i++) {
        if (NULL != mask_func) {
            mask_func(&usim_group[i]);
        }
        /*dump_usim_info(&usim_group[i]);*/
    }

    // encapsulate
    // header
    usim_header_t usim_header_be;
    usim_header_be.magic = CHANGE_UINT32T_ENDIAN(usim_header->magic);
    usim_header_be.offset = CHANGE_UINT32T_ENDIAN(usim_header->offset);
    usim_header_be.number = CHANGE_UINT32T_ENDIAN(usim_header->number);
    usim_header_be.length = CHANGE_UINT32T_ENDIAN(usim_header->length);
    tmp = buffer;
    memcpy(tmp, &usim_header_be, sizeof(usim_header_t));
    tmp += sizeof(usim_header_t);
    uint32_t _size = 0;
    for(i = 0; i < num; i++) {
        tl_pack_usim_to_buffer_be(&usim_group[i], tmp, &_size);
        tmp += _size;
    }

    *len = tmp - buffer;

    FREE_USIM_RES();

    return NO_ERROR;
}


int tl_fs_retrieve_usim(uint8_t *imsi, usim_info_t **usim,  void mask_func(usim_info_t*)) {
    int rc = NO_ERROR;
    usim_header_t *usim_header;
    usim_info_t *usim_group;
    int idx;

    rc = _tl_retrieve_all_usim_from_file(&usim_header, &usim_group);
    if(rc != NO_ERROR) {
        SOFTSIM_LOGE("_tl_retrieve_all_usim_from_file error, rc = %d", rc);
        goto exit;
    }

    idx = _tl_find_usim(usim_group, usim_header->number, imsi);
    if(idx < 0 ) {
        SOFTSIM_LOGE("_tl_find_usim error");
        *usim=NULL;
        return idx;
    }

    *usim = tl_malloc(sizeof(usim_info_t));
    if (usim == NULL) {
        rc = -ERR_EXCEED_MAX_SIZE;
        goto exit;
    }
    memcpy(*usim, &usim_group[idx], sizeof(usim_info_t));

    if(mask_func != NULL) {
        mask_func(*usim);
    }

exit:

    FREE_USIM_RES();

    return rc;
}

static int _tl_remove_usim(usim_header_t *usim_header,
                           usim_info_t *usim_group,
                           int idx) {

    int i, ret = NO_ERROR, num, rc;

    rc = target_remove_file(USIM_INFO_FILE_NAME);

    if( rc != NO_ERROR ) {
        return -ERR_SFS_FILE_NOT_EXIST_FAILED;
    }

    num = usim_header->number;
    if( num == 1 ) {
        return NO_ERROR;
    }

    //remove idx item in group and update header;
    uint32_t _len = get_size_of_usim(&usim_group[idx]);
    /*SOFTSIM_LOGD("get_size_of_usim + get_size_of_meta_data = %d", _len);*/
    usim_header->number -= 1;
    usim_header->length -= _len;
    dump_usim_header(usim_header);

    uint8_t *buffer = tl_malloc((usim_header->length) + sizeof(usim_header_t));
    if (buffer == NULL) {
        return -ERR_EXCEED_MAX_SIZE;
    }
    uint8_t *tmp = buffer;

    memcpy(tmp, usim_header, sizeof(usim_header_t));
    tmp += sizeof(usim_header_t);

    uint32_t size;
    for(i = 0; i < num; i++ ) {
        if(i == idx) {
            SOFTSIM_LOGD("skip %s", usim_group[i].imsi);
            continue;
        }
        tl_pack_usim_to_buffer(&usim_group[i], tmp, &size);
        /*SOFTSIM_LOGD("tl_pack_usim_to_buffer size = %d", size);*/
        tmp +=size;
    }

    rc = target_write_file(USIM_INFO_FILE_NAME, buffer, tmp - buffer);

    if(NULL != buffer) {
        tl_free(buffer);
    }

    return ret;
}

int tl_fs_remove_usim(uint8_t *imsi) {
    int rc, ret = NO_ERROR;
    usim_header_t *usim_header = NULL;
    usim_info_t *usim_group = NULL;
    int idx;
    rc = _tl_retrieve_all_usim_from_file(&usim_header, &usim_group);
    if( rc != NO_ERROR) {
        ret = rc;
        goto exit;
    }

    idx = _tl_find_usim(usim_group, usim_header->number, imsi);
    if (idx >= 0) {
        /*dump_usim_info_in_bytes(usim);*/
        SOFTSIM_LOGE("usim found");
    } else {
        SOFTSIM_LOGE("usim not found");
        ret = -ERR_USIM_NOT_EXIST;
        goto exit;
    }

    ret = _tl_remove_usim(usim_header, usim_group, idx);
    /*memcpy(&usim_header_updated, &usim_header, sizeof(usim_header_t));*/
    /*usim_header_updated.number -=1;*/

exit:
    FREE_USIM_RES();

    return ret;
}

int tl_fs_get_saved_imsi_number(uint32_t *num) {

    /*SOFTSIM_LOGD(" file: %s, func: %s",__FILE_NAME__, __func__);*/
    usim_header_t *usim_header;
    usim_info_t *usim_group;
    int rc = _tl_retrieve_all_usim_from_file(&usim_header, &usim_group);
    // ignore return value
    /*SOFTSIM_LOGD("_tl_retrieve_all_usim_from_file: rc = %d", rc);*/
    if (rc < 0)
        return rc;

    /*SOFTSIM_LOGD("number of usim is %d", usim_header->number);*/
    *num = usim_header->number;

    FREE_USIM_RES();

    return NO_ERROR;
}

int tl_fs_get_all_saved_imsi(char *output) {

    /*SOFTSIM_LOGD(" file: %s, func: %s",__FILE_NAME__, __func__);*/
    int i = 0;
    usim_header_t *usim_header;
    usim_info_t *usim_group;
    int rc = _tl_retrieve_all_usim_from_file(&usim_header, &usim_group);
    if (rc < 0)
        return rc;

    char *tmp = output;

    for(i = 0; i < usim_header->number; i++){
        // imsi should be always not NULL
        strcpy(tmp, (char*)usim_group[i].imsi);
        tmp += strlen((char*)usim_group[i].imsi);
        strcpy(tmp, ",");
        tmp +=1;
    }

    *(tmp-1) = 0;
    dump_strings("dump all imsi", output);

    FREE_USIM_RES();

    return NO_ERROR;
}

int tl_fs_get_apn_by_imsi(uint8_t *imsi, char *output) {
    int rc, ret = NO_ERROR;
    usim_header_t *usim_header;
    usim_info_t *usim_group;

    rc = _tl_retrieve_all_usim_from_file(&usim_header, &usim_group);
    if( rc != NO_ERROR) {
        return rc;
    }

    int idx = _tl_find_usim(usim_group, usim_header->number, imsi);
    if ( idx >= 0) {
        /*dump_usim_info_in_bytes(usim);*/
        SOFTSIM_LOGE("usim found");
    } else {
        SOFTSIM_LOGE("usim not found");
        return -ERR_USIM_NOT_EXIST;
    }

    char *json_string = NULL;
    rc = tl_json_build_apn_info(0, (char*)usim_group[idx].imsi, (char*)usim_group[idx].apn, (char*)usim_group[idx].plmn, &json_string);
    if( rc != 0 ) {
        ret = -2;
    }

    strcpy(output, json_string);

    FREE_USIM_RES();

    if(NULL != json_string){
        tl_free(json_string);
    }
    return ret;
}

int tl_fs_remove_usim_all() {
    SOFTSIM_LOGD(" file: %s, func: %s",__FILE_NAME__, __func__);
    int rc = NO_ERROR;

    rc = target_remove_file(USIM_INFO_FILE_NAME);
    SOFTSIM_LOGD("remove file rc = %d", rc);

    if(rc != 0 ) {
        rc = -ERR_SFS_FILE_NOT_EXIST_FAILED;
    }

    return rc;
}

int tl_fs_save_symmetric_key(uint8_t *key, uint32_t key_size) {
    SOFTSIM_LOGD(" file: %s, func: %s, key size: %d",__FILE_NAME__, __func__, key_size);
    return target_write_file(SYMMETRIC_KEY_FILE_NAME, key, key_size);
}

int tl_fs_restore_symmetric_key(uint8_t *key, uint32_t *key_size) {
    SOFTSIM_LOGD(" file: %s, func: %s",__FILE_NAME__, __func__);
    return target_read_file(SYMMETRIC_KEY_FILE_NAME, key, key_size);
}

int tl_fs_save_rsa_certs_chain(uint8_t *chain, uint32_t chain_size){
    SOFTSIM_LOGD(" file: %s, func: %s",__FILE_NAME__, __func__);
    return target_write_file(RSA_CERT_CHAIN_FILE_NAME, chain, chain_size);
}

int tl_fs_restore_rsa_certs_chain(uint8_t *chain, uint32_t *chain_size){
    SOFTSIM_LOGD(" file: %s, func: %s",__FILE_NAME__, __func__);
    return target_read_file(RSA_CERT_CHAIN_FILE_NAME, chain, chain_size);
}

int tl_fs_save_service_cert(uint8_t *cert, uint32_t cert_size) {
    SOFTSIM_LOGD(" file: %s, func: %s",__FILE_NAME__, __func__);
    return target_write_file(SERVICE_CERT_FILE_NAME, cert, cert_size);
}

int tl_fs_getsize_of_service_cert(uint32_t *cert_size) {
    SOFTSIM_LOGD(" file: %s, func: %s",__FILE_NAME__, __func__);
    return target_get_file_size(SERVICE_CERT_FILE_NAME, cert_size);
}

int tl_fs_restore_service_cert(uint8_t *cert, uint32_t *cert_size) {
    SOFTSIM_LOGD(" file: %s, func: %s",__FILE_NAME__, __func__);
    return target_read_file(SERVICE_CERT_FILE_NAME, cert, cert_size);
}

int tl_fs_save_rsa_private_key(uint8_t *key, uint32_t key_size) {
    SOFTSIM_LOGD(" file: %s, func: %s",__FILE_NAME__, __func__);
    return target_write_file(RSA_PRIVATE_KEY_FILE_NAME, key, key_size);
}

int tl_fs_getsize_of_rsa_private_key(uint32_t *key_size) {
    SOFTSIM_LOGD(" file: %s, func: %s",__FILE_NAME__, __func__);
    return target_get_file_size(RSA_PRIVATE_KEY_FILE_NAME, key_size);
}

int tl_fs_restore_rsa_private_key(uint8_t *key, uint32_t *key_size) {
    SOFTSIM_LOGD(" file: %s, func: %s",__FILE_NAME__, __func__);
    return target_read_file(RSA_PRIVATE_KEY_FILE_NAME, key, key_size);
}

int tl_fs_save_using_imsi_info(uint8_t *input, uint32_t len) {
    SOFTSIM_LOGD(" file: %s, func: %s",__FILE_NAME__, __func__);
    return target_write_file(ACTIVE_IMSI_INFO_FILE_NAME, input, len);
}

int tl_fs_restore_using_imsi_info(uint8_t *output, uint32_t *len) {
    /*SOFTSIM_LOGD(" file: %s, func: %s",__FILE_NAME__, __func__);*/
    return target_read_file(ACTIVE_IMSI_INFO_FILE_NAME, output, len);
}

int tl_fs_remove_using_imsi_info() {
    SOFTSIM_LOGD("file: %s, func: %s", __FILE_NAME__, __func__);

    int rc;
    rc = target_remove_file(ACTIVE_IMSI_INFO_FILE_NAME);

    SOFTSIM_LOGD("remove file, %s, ret = %d",  ACTIVE_IMSI_INFO_FILE_NAME, rc);

    return rc;
}

int tl_fs_save_device_cert(uint8_t *key, uint32_t key_size) {
    SOFTSIM_LOGD(" file: %s, func: %s", __FILE_NAME__, __func__);
    return target_write_file(DEVICE_CERT_FILE_NAME, key, key_size);
}

int tl_fs_restore_device_cert(uint8_t *key, uint32_t *key_size) {
    SOFTSIM_LOGD(" file: %s, func: %s", __FILE_NAME__, __func__);
    return target_read_file(DEVICE_CERT_FILE_NAME, key, key_size);
}

int tl_fs_create_file(uint8_t *buffer, uint32_t buffer_size) {
    SOFTSIM_LOGD(" file: %s, func: %s", __FILE_NAME__, __func__);

    /*dump_bytes("buffer", buffer, buffer_size);*/

    uint32_t size = 0;
    char *filename  = (char*)get_buffer(buffer, &size);
    /*SOFTSIM_LOGD("file name : %s, size : %d", filename, size);*/

    if (size + 8 >= SOFTSIM_INPUT_LEN) {
        SOFTSIM_LOGE("create file error");
        return INVALID_INDEX;
    }

    uint8_t *payload  = get_buffer(buffer + size + 8, &size);

    /*dump_bytes(" payload", payload, size);*/

    return target_write_file(filename, payload, size);
}

int tl_fs_remove_file(char *file){
    return target_remove_file(file);
}

int tl_fs_remove_cached_files(){
    /*target_remove_file(USIM_INFO_FILE_NAME);*/
    int rc = NO_ERROR;
    int final_rc = NO_ERROR;
    rc = target_remove_file(SYMMETRIC_KEY_FILE_NAME);
    if (rc != NO_ERROR) {
        SOFTSIM_LOGE("remove symmetric key file error, rc = %d", rc);
        final_rc = rc;
    }
    rc = target_remove_file(RSA_PRIVATE_KEY_FILE_NAME);
    if (rc != NO_ERROR) {
        SOFTSIM_LOGE("remove rsa private key file error, rc = %d", rc);
        final_rc = rc;
    }
    rc = target_remove_file(SERVICE_CERT_FILE_NAME);
    if (rc != NO_ERROR) {
        SOFTSIM_LOGE("remove service cert file error, rc = %d", rc);
        final_rc = rc;
    }
    rc = target_remove_file(DEVICE_CERT_FILE_NAME);
    if (rc != NO_ERROR) {
        SOFTSIM_LOGE("remove device cert file error, rc = %d", rc);
        final_rc = rc;
    }
    rc = target_remove_file(RSA_CERT_CHAIN_FILE_NAME);
    if (rc != NO_ERROR) {
        SOFTSIM_LOGE("remove rsa cert chain file error, rc = %d", rc);
        final_rc = rc;
    }
    /*target_remove_file(ACTIVE_IMSI_INFO_FILE_NAME);*/

    return final_rc;
}
