#include <stdio.h>
#include <stdarg.h>

#include "platform.h"
#include "ssl_errno.h"
#include "ssl_utils.h"
#include "ssl_log.h"
#include "ssl_file_list.h"
#include "ssl_file.h"
#include "ssl_usim_file.h"

file_ptr malloc_file_t(){
    /*FUNC_ENTER();*/

    file_ptr file = plt_malloc(sizeof(file_t));

    if (NULL == file ) {
        MLOG_E("malloc failed");
        return NULL;
    }

    plt_memset(file, 0, sizeof(file_t));

    return file;
}

void free_file_t(file_ptr file){
    /*FUNC_ENTER();*/
    if( NULL == file ){
        return;
    }

    if(NULL != file->data){
        plt_free(file->data);
        file->data = NULL;
    }

    // agui add, for debug memory issue
    //plt_memset(file, 0xaa, sizeof(file_t));

    plt_free(file);

    file = NULL;
}

static void print_with_indent(size_t tab, const char* fmt, ...){
#ifdef __X86_64__
    size_t t = 0;

    printf("|");
    for(t = 0; t < tab; t++){
        printf("--->");
    }

    va_list ap;
    va_start(ap, fmt);
    vprintf(fmt, ap);
    va_end(ap);
#endif
}


fid_t get_fid(uint8_t *bytes){
    return ((bytes[0] << 8) + bytes[1]);
}

static void file_list_init(file_list_t *list){
    /*FUNC_ENTER();*/

    plt_memset((uint8_t*)list, 0, sizeof(file_list_t));
}

file_ptr get_file_by_child_ef(file_list_ptr head){
    file_ptr ptr = NULL;

    size_t offset_head  = offsetof(file_t, node);

    ptr = (file_ptr)((uint8_t*)head - offset_head);

    return ptr;
}

file_ptr get_file_by_child_df(file_list_ptr head){
    file_ptr ptr = NULL;

    size_t offset_head  = offsetof(file_t, node);

    ptr = (file_ptr)((uint8_t*)head - offset_head);

    return ptr;
}

file_ptr get_file_by_node(file_list_ptr head){
    file_ptr ptr = NULL;

    size_t offset_head  = offsetof(file_t, node);

    ptr = (file_ptr)((uint8_t*)head - offset_head);

    return ptr;
}

file_ptr create_DF(file_ptr parent, fid_t fid, uint8_t ft){
    /*FUNC_ENTER();*/

    file_ptr df = malloc_file_t();

    df->fid = fid;
    df->parent = parent;
    df->file_type = ft;

    file_list_init(&df->node);
    file_list_init(&df->child_ef);
    file_list_init(&df->child_df);
    if(NULL != parent){
        file_list_add(&parent->child_df, &df->node);
    }

    return df;
}

file_ptr create_EF(file_ptr parent, fid_t fid, sfi_t sfi, ef_struct_t ef_struct){
    FUNC_ENTER();

    file_ptr ef = malloc_file_t();

    ef->fid = fid;
    ef->sfi = sfi;
    ef->parent = parent;

    ef->file_type = FILE_TYPE_WORKING_EF;
    ef->ef_struct = ef_struct;

    file_list_init(&ef->node);
    file_list_init(&ef->child_df); //ignore
    file_list_init(&ef->child_ef); //ignore

    /*FUNC_ENTER("name:%s, parent: %p, child_ef:%p",parent->name,  parent, parent->child_ef);*/
    if(NULL != parent){
        file_list_add(&parent->child_ef, &ef->node);
    }

    return ef;
}

void free_file(file_ptr file){
    if(NULL == file) {
        return;
    }

    FUNC_ENTER("name: %s", file->name);


    free_file_t(file);
}

void free_EF(file_ptr ef){
    free_file(ef);
}

void free_DF(file_ptr file, int tab){

    FUNC_ENTER("name of DF: %s", file->name);
    tab++;
    if (NULL == file) {
        return;
    }

    print_with_indent(tab, "free DF(%s)\n", file->name);

#if 1 // fix memory issue
    file_list_ptr head, cur, next;

    // free EFs on child_ef
    head = &file->child_ef;
    if (head != NULL)
    {
        cur = head->next;	
    }
    else
    {
        cur = NULL;
    }
    while (cur != NULL) {
        next = cur->next;
        file_ptr ptr = get_file_by_node(cur);
        if(NULL != ptr){
            free_EF(ptr);
        }
        cur = next;
    }

    // free DFs on child_df
    head = &file->child_df;
    if (head != NULL)
    {
        cur = head->next;   
    }
    else
    {
        cur = NULL;
    }
    while (cur != NULL)
    {
        next = cur->next;

        file_ptr ptr = get_file_by_node(cur);
        dump_EF_file(ptr);
        free_DF(ptr, tab);
        free_file(ptr); 

        cur = next;
    }

#else

    // free EFs on child_ef
    file_list_ptr pos,head = &file->child_ef;
    list_for_each(pos, head) { // 2. head will be free at below code, but here still use it, so it is a not safe for multi thread system
        file_ptr ptr = get_file_by_node(head);
        if(NULL != ptr){
            free_EF(ptr);  // 1. free head
        }
    }

    // free DFs on child_df
    head = &file->child_df;
    list_for_each(pos, head){
        file_ptr ptr = get_file_by_node(head);
        dump_EF_file(ptr);
        free_DF(ptr, tab);
        free_file(ptr);
    }
#endif

}

void dectroy_file_system(file_ptr root){
    free_DF(root, 1);
}

//////////////////////////////////////////////////////////////////////////////
static void print_file_with_indent(file_ptr file, size_t tab){

    if(NULL == file){
        return;
    }

    print_with_indent(tab, "[name:%s, fid: 0x%04x, sfi:0x%02x, self: %p, next:%p]\n",
            file->name, file->fid, file->sfi, &file->node, file->node);

    if(NULL!= file->data && file->file_len > 0 ) {
        /*dump_bytes("dump file data", file->data, file->file_len);*/

        char* string = plt_malloc(file->file_len * 2 + 1);
        bytes_to_string(file->data, file->file_len, string);
        print_with_indent(tab, "%s\n",string );
        plt_free(string);
    }
}

static char *get_file_type_name(file_ptr file){
    switch(file->file_type){
        case FILE_TYPE_MF:
            return "MF";
        case FILE_TYPE_DF_ADF:
            return "DF_ADF";
        case FILE_TYPE_WORKING_EF:
            return "EF";
    }

    return "NULL";
}

static char *get_ef_struct_name(file_ptr file){
    switch(file->ef_struct){
        case TRANSPARENT:
            return "TRANSPARENT";
        case LINEAR:
            return "LINEAR";
        case CYCLIC:
            return "CYCLIC";
    }

    return "NULL";
}

static char *get_shareable_name(file_ptr file){

    switch(file->shareable){
        case SHAREABLE:
            return "SHAREABLE";

        case UNSHAREABLE:
            return "UNSHAREABLE";
    }

    return "NULL";
}

void dump_EF_file(file_ptr ef){

    FUNC_ENTER("=========START==================");
    FUNC_ENTER("ef_name: %s", ef->name);
    FUNC_ENTER("fid: %04X", ef->fid);
    FUNC_ENTER("sfi: %02X", ef->sfi);
    FUNC_ENTER("file_desc: %02X [ SH: %s, FT: %s, EF_STRUCT: %s ]",
            ef->file_desc,
            get_shareable_name(ef),
            get_file_type_name(ef),
            get_ef_struct_name(ef));
    FUNC_ENTER("record_len: %d", ef->record_len);
    FUNC_ENTER("record_num: %d", ef->record_num);
    FUNC_ENTER("record_pointer: %d", ef->record_pointer);
    FUNC_ENTER("file len: %d", ef->file_len);
    FUNC_ENTER("file data: %p", ef->data);
    if(NULL != ef->data && ef->file_len > 0) {
        /*print_byte_line("data:", ef->data, ef->file_len);*/
#ifdef __X86_64__
        char *string = NULL;
        string = plt_malloc(ef->file_len * 2 + 1);
        bytes_to_string(ef->data, ef->file_len, string);
        print_with_indent(0, "%s\n",string );
        plt_free(string);
#elif defined(__ARM__)
        dump_bytes("file data", ef->data, ef->file_len);
#endif
    }

    FUNC_ENTER("=========END==================");
}

void traverse_root_file(const file_ptr file, int tab){
    int indent = tab + 1;

    print_file_with_indent(file, tab);

    file_list_ptr pos, head = &file->child_df;
    list_for_each(pos, head){
        file_ptr ptr = get_file_by_node(head);
        traverse_root_file(ptr, indent);
    }

    head = &file->child_ef;
    list_for_each(pos, head){
        file_ptr ptr = get_file_by_node(head);
        if(NULL != ptr){
            print_file_with_indent(ptr, indent);
        }
    }
}

void dump_file_system(file_ptr file, int tab){
    traverse_root_file(file, tab);
}

void dump_DF_file(file_ptr file){

    if(NULL == file) {
        return;
    }

    FUNC_ENTER("name: %s, file_desc: %02X[%02x, %02x, %02x]", file->name, file->file_desc, file->shareable, file->file_type, file->ef_struct);

    file_list_ptr pos, head = &file->child_df;
    list_for_each(pos, head){
        file_ptr ptr = get_file_by_node(head);
        dump_DF_file(ptr);
    }

    head = &file->child_ef;
    list_for_each(pos, head){
        file_ptr ptr = get_file_by_node(head);
        if(NULL != ptr){
            dump_EF_file(ptr);
        }
    }
}

file_ptr get_ef_file_by_fid(file_ptr parent, fid_t fid){

    file_list_ptr pos, head = &parent->child_ef;
    list_for_each(pos, head){
        file_ptr ptr = get_file_by_child_ef(head);
        if((NULL != ptr) && ptr->fid == fid){
            return ptr;
        }
    }

    return NULL;
}

file_ptr get_df_file_by_fid(file_ptr parent, fid_t fid){

    file_list_ptr pos, head = &parent->child_df;
    list_for_each(pos, head){
        file_ptr ptr = get_file_by_child_df(head);
        if((NULL != ptr) && ptr->fid == fid){
            return ptr;
        }
    }

    return NULL;
}

file_ptr get_file_by_sfi(file_ptr df, sfi_t sfi){

    file_list_ptr pos, head = &df->child_ef;
    list_for_each(pos, head){
        file_ptr ptr = get_file_by_child_ef(head);
        if((NULL != ptr) && ptr->sfi == sfi){
            return ptr;
        }
    }

    return NULL;
}

file_ptr get_file_by_fid(file_ptr df, fid_t fid){
    /*FUNC_ENTER(" df ->name: %s, fid: %04x", df->name, fid );*/

    file_ptr file = NULL;
    file = get_df_file_by_fid(df, fid);

    if( NULL == file ) {
        file = get_ef_file_by_fid(df, fid);
    }

    return file;
}

file_ptr get_file_by_path(file_ptr parent, uint8_t *path, size_t sz){
    file_ptr file;

    if(sz == 0 ) {
        MLOG_E("not found fid by path");
        return NULL;
    }

    fid_t fid = get_fid(path);
    if(0x7fff == fid){
        fid = ADF_USIM ;
    }

    /*dump_EF_file(parent);*/

    file = get_file_by_fid(parent, fid);

    if(file == NULL || sz == 2 ) {
        return file;
    }

    return get_file_by_path(file, path + 2, sz - 2);
}

int save_data_of_file(file_ptr file, uint8_t *data, size_t sz){

    plt_memcpy(file->data, data, sz > file->file_len ? file->file_len : sz);

    return 0;
}

int load_data_of_file(file_ptr file, uint8_t *data, size_t *sz){

    plt_memcpy(data, file->data, file->file_len);
    *sz = file->file_len;

    return 0;
}

int save_string_of_file(file_ptr file, char *value){
    size_t sz;
    uint8_t *buff = string_to_bytes(value, &sz);

    /*plt_memcpy(file->data, data, sz > file->file_len ? file->file_len : sz);*/
    save_data_of_file(file, buff, sz);

    plt_free(buff);

    return 0;
}

