#include <stdio.h>
#include <string.h>

#include "platform.h"
#include "ssl_errno.h"
#include "ssl_utils.h"
#include "ssl_log.h"
#include "ssl_tlv.h"
#include "ssl_file.h"
#include "ssl_usim_file.h"
#include "ssl_file.h"
#include "ssl_profile.h"

usim_profile_t usim_profile = {};

static void encode_iccid(const char *input, uint8_t **output, size_t *out_len){
    int idx = 0;
    union {
        uint8_t val;
        struct{
            uint8_t lsb:4;
            uint8_t msb:4;
        };
    } l;

    size_t len = (strlen(input)>>1) + 1;
    /*MLOG_D(" len: :%d", len);*/

    *output = plt_malloc(len);
    if(NULL == *output){
        len = 0;
        goto exit;
    }
    plt_memset(((uint8_t*)*output), 0, len);

    for(idx = 0; idx < plt_strlen(input); idx++){
        if( (idx & 0x1) == 0x0 ){
            l.lsb = input[idx] - '0';
        }else{
            l.msb = input[idx] - '0';
            (*output)[idx>>1] = l.val;
            l.val = 0;
        }
    }

    if((idx & 0x1) == 0x0){
        (*output)[idx>>1] = l.val;
    }

exit:
    *out_len = len;
}


static void encode_mcc_mnc(const char *mcc_mnc, uint8_t *encoded){
    int idx = 0;
    union {
            uint8_t val;
        struct{
            uint8_t lsb:4;
            uint8_t msb:4;
        };
    } l;

    for(idx = 0; idx <3; idx++){// agui add, fix over read and write_usim_profile
    // for(idx = 0; idx <5; idx++){
        l.lsb = mcc_mnc[2 * idx] == 'F' ? 0xF :  mcc_mnc[2 * idx] - '0';
        l.msb = mcc_mnc[2 * idx + 1] == 'F' ? 0xF :  mcc_mnc[2 * idx + 1] - '0';
        encoded[idx] = l.val;
        /*MLOG_D("%02x", l.val);*/
    }
}

static void encode_plmn(const char *plmn, int skip, uint8_t *dest_buff, size_t *pos){
    int l = strlen(plmn);
    int idx = 0;
    char buff[6 + 1]; //store '\0'

    plt_memcpy(buff + idx, plmn, 3);
    idx += 3;

    //mccmnc == 5
    if((l - (skip - 3)) == 5){
        buff[idx++] = 'F';
        plt_memcpy(buff + idx, plmn + skip, 2);
    //mccmnc == 6
    }else if((l - (skip - 3)) == 6){
        buff[idx++] = plmn[l - 1];
        plt_memcpy(buff + idx, plmn + skip, 2);
    }

    /*MLOG_D("buff: %s\n", buff);*/
    encode_mcc_mnc(buff, dest_buff);
    *pos += 3;
}

static void add_act(uint8_t *buff, size_t *pos ){
    int index = 0;
    if(NET_MODE_3G == usim_profile.net_mode){
        buff[index++] = 0x80;
        buff[index++] = 0xc0;
    }else if (NET_MODE_4G == usim_profile.net_mode){
        buff[index++] = 0xc0;
        buff[index++] = 0xc0;
    }

    *pos += index;
}

char* strip_space(const char *src){
    char *dest = plt_malloc(plt_strlen(src) + 1);
    plt_memset(dest, 0, plt_strlen(src) + 1);

    int i = 0, idx = 0;
    for(i = 0; i < plt_strlen(src); i++){
        if(src[i] == ' ')
            continue;

        dest[idx++] = src[i];
    }

    return dest;
}

int count_item(char *input, const char delim){
    int idx = 0;
    char c;
    int num = 1;

    if ( NULL == input) {
        return 0;
    }

    while( '\0' != (c = input[idx++])){
        c == delim ? num++ : num;
    }

    return num;
}

#define SIZE_OF_PLMN 5
void parse_plmn(char* plmn, const char *delim, uint8_t** dest_buff, size_t *size) {
    size_t pos = 0;
    char *token;

    char *plmns = strip_space(plmn);
    char *shadow = plmns;
    int size_of_buff = count_item(plmns, ',') * SIZE_OF_PLMN;

    *dest_buff = plt_malloc(size_of_buff);
    if( NULL == *dest_buff){
        MLOG_E("malloc failed...");
        goto exit;
    }

    /*MLOG_D("plmns: %s\n", plmns);*/

    while( NULL != ( token = plt_strsep(&plmns, delim))){
        /*MLOG_D("size: %d, %s\n", strlen(token), token);*/
        ltrim(token);
        rtrim(token);
        encode_plmn(token, 4, *dest_buff + pos, &pos);
        add_act(*dest_buff + pos, &pos);
    }

exit:
    /*MLOG_D("size_of_buff: %d, pos: %d", size_of_buff, pos);*/
    *size = pos;
    plt_free(shadow);
}

#define SIZE_OF_FPLMN 4
void parse_fplmn(char* fplmn, const char *delim, uint8_t** dest_buff, size_t *size) {
    size_t pos = 0;
    char *token;

    char *fplmns = strip_space(fplmn);
    char *shadow = fplmns;
    int size_of_buff = count_item(fplmns, ',') * SIZE_OF_FPLMN;

    /*MLOG_D("fplmns size: %d, %s\n", plt_strlen(fplmns), fplmns);*/

    *dest_buff = plt_malloc(size_of_buff);
    if( NULL == *dest_buff){
        MLOG_E("malloc failed...");
        goto exit;
    }

    /*MLOG_D("fplmns: %s", fplmns);*/

    while( NULL != ( token = plt_strsep(&fplmns, delim))){
        ltrim(token);
        rtrim(token);
        /*MLOG_D("size: %d, %s", strlen(token), token);*/
        encode_plmn(token, 4, (*dest_buff) + pos, &pos);
    }

exit:
    *size = pos;

    plt_free(shadow);
}


static const uint8_t parity_table_16[16] =
{
#   define P2(n) n, n^1, n^1, n
#   define P4(n) P2(n), P2(n^1), P2(n^1), P2(n)
    P4(0),
};

uint8_t check_digital_parity(const char *str){
    int i = 0;
    uint8_t ecc = 0;

    for(i = 0; i < strlen(str); i++){
        ecc ^= (str[i] - '0');
    }

    return parity_table_16[ecc];
}

void parse_imsi(const char *imsi, uint8_t *output){
    lv_ptr lv = plt_malloc(9);;
    int idx = 0;

    if(NULL == lv || NULL == imsi || plt_strlen(imsi) > 15){
        MLOG_E("imsi parse error...");
        return;
    }
    plt_memset(lv->value, 0xFF, 8);
    lv->len = 8;
    /*lv->value[0] = char_to_hex(imsi[0]) <<4 |  0x01 | check_digital_parity(imsi) << 3;*/
    lv->value[0] = char_to_hex(imsi[0]) <<4 |  0x01 | 0x01<< 3;
    idx = 1;

    for(idx = 1; idx < plt_strlen(imsi) ; idx++) {
        if(idx & 0x1) {
            lv->value[(idx + 1)>>1] = (imsi[idx] - '0') + (0xF<<4);

        }else{
            lv->value[(idx + 1)>>1] &= 0x0F;
            lv->value[(idx + 1)>>1] |= (imsi[idx] - '0')<<4;
        }
    }

    memcpy(output, (uint8_t*)lv, 9);

    plt_free(lv);
}

#define INIT_PROFILE_CHAR(__name)   \
     file = get_ef_file_by_fid(g_ADF_usim, EF_##__name); \
    usim_profile.__name = file;       \
    if(NULL != file && NULL != __name && plt_strlen(__name) > 0) {   \
        save_string_of_file(file, __name); \
        MLOG_D("%s: %s", #__name, __name); \
    }else{                                                 \
        MLOG_E("%s is NULL", #__name); \
    }

void write_usim_profile(uint8_t slot, char* imsi, char* EXT_KI, char* EXT_OPC, char* ICCID, char* ACC, char* EXT_APN, 
                            char* SPN, char* MSISDN, char* OPLMNwAcT,char* FPLMN){
    file_ptr file;
    uint8_t IMSI[9];
    uint8_t encoded_rplmn[4];
    uint8_t infor[EF_LOCI_SIZE + 1]; 
    size_t file_size = 0;
    size_t size = 0;

    // for debug multi plmn tiems issue
    //char * testStr="454-03,454-03,454-04,455-01,455-04";
    //OPLMNwAcT = testStr;

    usim_profile.slot = slot;

    plt_memset(IMSI,0xFF, 9);

     parse_imsi(imsi, IMSI);
    file = get_ef_file_by_fid(g_ADF_usim, EF_IMSI);
    save_data_of_file(file, IMSI, 9);
    dump_EF_file(file);

    INIT_PROFILE_CHAR(EXT_KI);
    INIT_PROFILE_CHAR(EXT_OPC);

    INIT_PROFILE_CHAR(ACC);
    INIT_PROFILE_CHAR(EXT_APN);

    file = get_ef_file_by_fid(g_ADF_usim, EF_SPN); \
    if(NULL != file && NULL != SPN && plt_strlen(SPN) > 0) {
        uint8_t _spn[17];
        plt_memset(_spn + 1, 0xff, 16);
        size = plt_strlen(SPN);
        plt_memcpy(_spn + 1, SPN, MIN(size, 16));
        save_data_of_file(file, _spn, 17);
    }
    /*INIT_PROFILE_CHAR(SPN);*/
    /*INIT_PROFILE_CHAR(OPLMNwAcT);*/
    /*INIT_PROFILE_CHAR(FPLMN);*/

    /*MLOG_E("%s: %s", "EF_OPLMNwAcT\n", OPLMNwAcT);*/
    uint8_t *plmn;
    char *tmp = plt_strdup(OPLMNwAcT);
    parse_plmn(OPLMNwAcT, ",", &plmn, &size);
    dump_bytes("encoded plmn", plmn, size);
    file = get_ef_file_by_fid(g_ADF_usim, EF_OPLMNwAcT);
    save_data_of_file(file, plmn, size);
    // save rplmn for EPSLOCI and LOCI. rplmn is the first item of oplmn list
    plt_memcpy(encoded_rplmn, plmn, 3);
    plt_free(tmp);
    plt_free(plmn);

    // LOCI
    plt_memset(infor,0xFF, EF_LOCI_SIZE);
    file = get_ef_file_by_fid(g_ADF_usim, EF_LOCI);
    if (NULL != file)
    {
        file_size = 0;
        load_data_of_file(file, infor, &file_size);
        if (EF_LOCI_SIZE != file_size)
        {
            // length error, maybe overwrite
            MLOG_E("warning. check size of EF_LOCI, size: %d", file_size);
        }
        else
        {
            // modify LAI
            plt_memcpy(&infor[LOCI_LAI_OFFSET], encoded_rplmn, 3);
        }
        save_data_of_file(file, infor, EF_LOCI_SIZE);
    }

    /*MLOG_D("%s: %s", "EF_FPLMN", FPLMN);*/
    uint8_t *fplmn;
    tmp = plt_strdup(FPLMN);
    parse_fplmn(FPLMN, ",", &fplmn, &size);
    dump_bytes("encoded fplmn", fplmn, size);
    file = get_ef_file_by_fid(g_ADF_usim, EF_FPLMN);
    save_data_of_file(file, fplmn, size);
    plt_free(tmp);
    plt_free(fplmn);

    uint8_t *iccid;
    size_t iccid_sz;
    encode_iccid(ICCID, &iccid, &iccid_sz);
    file = get_file_by_fid(g_MF, EF_ICCID);
    save_data_of_file(file, iccid, iccid_sz);
    /*dump_bytes("iccid", iccid, iccid_sz);*/
    plt_free(iccid);
}
