#include "user.h"

int ue_get_user_identifier(user_identifier_t *identifier)
{
    if (identifier == NULL)
    {
        return -1;
    }
    memcpy(identifier->buffer, (uint8_t[]) {
            0x02, 0x0F, 0x84, 0x31, 0xFF, 0xFF, 0xFF,
            0xFF, 0xFF, 0xFF, 0x1A, 0x2B, 0x3C, 0x4D,
            0x5E, 0x6F, 0x00, 0x40, 0x00, 0xFF, 0x01
    }, USER_MAX_ID_LENGTH);
    identifier->buffer_length = USER_MAX_ID_LENGTH;

    FILE *userID;

    userID = fopen("/tmp/hello-world-pc/userID.txt", "w");

    for (int i = 0; i < USER_MAX_ID_LENGTH - 1; i++) {
        fprintf(userID, "%u ", identifier->buffer[i]);
    }
    fprintf(userID, "%u", identifier->buffer[USER_MAX_ID_LENGTH - 1]);

    fclose(userID);

    return 0;
}

int ue_generate_nonce(user_nonce_t *nonce)
{
    int r;

    if (nonce == NULL)
    {
        return -1;
    }

    // random user nonce
    r = RAND_bytes(nonce->nonce, NONCE_LENGTH);
    if (r != 1)
    {
        return -1;
    }

    return 0;
}
int ue_show(user_identifier_t ue_identifier, verifier_identifier_t ve_identifier, user_nonce_t ue_nonce, verifier_nonce_t ve_nonce,
                    user_private_key_t ue_private_key, user_cipher_output_t *ue_cipher_output)
{
    uint8_t user_ukey[AES_BLOCK_SIZE];
    user_cipher_data_t user_cipher_data;

    FILE *verifierID;
    FILE *userID;
    FILE *userkey;

    int length;
    int r;

    if (ue_cipher_output == NULL)
    {
        return -1;
    }

    if( access( "/tmp/hello-world-pc/verifierID.txt", F_OK ) != 0
    || access("/tmp/hello-world-pc/userID.txt", F_OK ) != 0
    || access("/tmp/hello-world-pc/user.txt", F_OK ) != 0){
        printf("Soubory chybí!");
        return 0;
    }
    userID = fopen("/tmp/hello-world-pc/userID.txt", "r");

    //read file into array
    uint8_t user_ID[AES_BLOCK_SIZE + 1];

    for (int i = 0; i < 16; i++){
        fscanf(userID, "%u ", &user_ID[i] );
    }

    fclose(userID);

    verifierID = fopen("/tmp/hello-world-pc/verifierID.txt", "r");

    //read file into array
    uint8_t verifier_ID[AES_BLOCK_SIZE + 1];

    for (int i = 0; i < 16; i++){
        fscanf(verifierID, "%u ", &verifier_ID[i] );
    }

    fclose(verifierID);

    userkey = fopen("/tmp/hello-world-pc/user.txt", "r");

    //read file into array
    uint8_t user_key[AES_BLOCK_SIZE + 1];

    for (int i = 0; i < 16; i++){
        fscanf(userkey, "%u ", &user_key[i] );
    }

    fclose(userkey);

    /// calculate ukey
    length = AES_BLOCK_SIZE;
    r = aes_128_ecb_encrypt(user_key, user_ukey, &length, (const unsigned char *) "000000000000User", sizeof("000000000000User") - 1);
    if (r < 0)
    {
        return -3;
    }

    /// set data for AES (ukey; ID_ui, ID_vi, unonce, vnonce)
    memcpy(user_cipher_data.user_identifier, user_ID, USER_MAX_ID_LENGTH);
    memcpy(user_cipher_data.verifier_identifier, verifier_ID, VERIFIER_MAX_ID_LENGTH);
    memcpy(user_cipher_data.user_nonce, ue_nonce.nonce, NONCE_LENGTH);
    memcpy(user_cipher_data.verifier_nonce, ve_nonce.nonce, NONCE_LENGTH);

    length = sizeof(user_cipher_output_t);
    r = aes_128_ecb_encrypt(user_ukey,ue_cipher_output->cipher_output,&length,&user_cipher_data,sizeof(user_cipher_data_t));
    if (r < 0)
    {
        return -4;
    }

    uint8_t AES_cipher_user[256] = {0};
    WORD SW12;
    WORD La;
    uint8_t lc = 0;

    // AES cipher dates
    memcpy(&AES_cipher_user[lc], ue_cipher_output->cipher_output, 4 * AES_BLOCK_SIZE);
    lc += 4 * AES_BLOCK_SIZE;

    SW12 = multosSendAPDU(CLA_APPLICATION, INS_CYPHER, 0x00, 0x00, lc, 0, &La, 0x00, AES_cipher_user, 4 * AES_BLOCK_SIZE, 1000);
    if (SW12 != 0x9000)
    {
        return -1;
    }

    return 0;
}

///přidané
int ue_connect(reader_t *reader, user_identifier_t *identifier){

    uint8_t pbSendBuffer[MAX_APDU_LENGTH_T0] = {0};
    uint8_t pbRecvBuffer[MAX_APDU_LENGTH_T0] = {0};
    uint32_t dwSendLength;
    uint32_t dwRecvLength;

    int r;

    if (identifier == NULL)
    {
        return -1;
    }

    dwSendLength = sizeof(pbSendBuffer);
    r = apdu_build_command(CASE2S, CLA_APPLICATION, INS_HELLO_WORLD, 0x00, 0x00, 0, NULL, VERIFIER_MAX_ID_LENGTH, pbSendBuffer, &dwSendLength);
    if (r < 0)
    {
        return -1;
    }

    dwRecvLength = sizeof(pbRecvBuffer);
    r = sc_transmit_data(*reader, pbSendBuffer, dwSendLength, pbRecvBuffer, &dwRecvLength, NULL);
    if (r < 0)
    {
        fprintf(stderr, "Error: %s\n", sc_get_error(r));
        return r;
    }

    memcpy(identifier->buffer, pbRecvBuffer, VERIFIER_MAX_ID_LENGTH);
    identifier->buffer_length = VERIFIER_MAX_ID_LENGTH;

    FILE *userID;

    userID = fopen("/tmp/hello-world-pc/userID.txt", "w");

    for (int i = 0; i < VERIFIER_MAX_ID_LENGTH - 1; i++) {
        fprintf(userID, "%u ", identifier->buffer[i]);
    }
    fprintf(userID, "%u", identifier->buffer[VERIFIER_MAX_ID_LENGTH - 1]);

    fclose(userID);

    return 0;

}

int ue_set_user_private_key(reader_t *reader, user_private_key_t *ue_private_key){

    uint8_t pbSendBuffer[MAX_APDU_LENGTH_T0] = {0};
    uint8_t pbRecvBuffer[MAX_APDU_LENGTH_T0] = {0};
    uint32_t dwSendLength;
    uint32_t dwRecvLength;

    uint8_t ve_key[256] = {0};

    uint8_t lc = 0;

    // VERIFIER identifier
    memcpy(&ve_key[lc], ue_private_key->sk, VERIFIER_MAX_ID_LENGTH);
    lc += VERIFIER_MAX_ID_LENGTH;

    dwSendLength = sizeof(pbSendBuffer);
    int  r = apdu_build_command(CASE3S, CLA_APPLICATION, INS_KEY_VERIFIER, 0x00, 0x00, lc, ve_key, 0, pbSendBuffer, &dwSendLength);
    if (r < 0)
    {
        return -1;
    }

    dwRecvLength = sizeof(pbRecvBuffer);
    r = sc_transmit_data(*reader, pbSendBuffer, dwSendLength, pbRecvBuffer, &dwRecvLength, NULL);
    if (r < 0)
    {
        fprintf(stderr, "Error: %s\n", sc_get_error(r));
        return r;
    }

    return 0;

}

extern int ue_get_user_nonce(reader_t *reader, user_nonce_t *ue_nonce){

    uint8_t pbSendBuffer[MAX_APDU_LENGTH_T0] = {0};
    uint8_t pbRecvBuffer[MAX_APDU_LENGTH_T0] = {0};
    uint32_t dwSendLength;
    uint32_t dwRecvLength;

    int r;

    if (ue_nonce == NULL)
    {
        return -1;
    }

    dwSendLength = sizeof(pbSendBuffer);
    r = apdu_build_command(CASE2S, CLA_APPLICATION, INS_VE_NONCE, 0x00, 0x00, 0, NULL, NONCE_LENGTH, pbSendBuffer, &dwSendLength);
    if (r < 0)
    {
        return -1;
    }

    dwRecvLength = sizeof(pbRecvBuffer);
    r = sc_transmit_data(*reader, pbSendBuffer, dwSendLength, pbRecvBuffer, &dwRecvLength, NULL);
    if (r < 0)
    {
        fprintf(stderr, "Error: %s\n", sc_get_error(r));
        return r;
    }

    memcpy(ue_nonce->nonce, pbRecvBuffer, NONCE_LENGTH);
    ue_nonce->nonce_length = NONCE_LENGTH;
    return 0;
}

int ue_set_verifier_identifier(reader_t *reader, verifier_identifier_t *identifier){

    uint8_t pbSendBuffer[MAX_APDU_LENGTH_T0] = {0};
    uint8_t pbRecvBuffer[MAX_APDU_LENGTH_T0] = {0};
    uint32_t dwSendLength;
    uint32_t dwRecvLength;

    uint8_t ue_id[256] = {0};

    //toto tu musí zůstat kvuli sestavení commandu, viz odeslání lc
    uint8_t lc = 0;

    // user identifier
    memcpy(&ue_id[lc], identifier->buffer, USER_MAX_ID_LENGTH);
    lc += USER_MAX_ID_LENGTH;


    dwSendLength = sizeof(pbSendBuffer);
    int  r = apdu_build_command(CASE3S, CLA_APPLICATION, INS_ID_USER, 0x00, 0x00, lc, ue_id, 0, pbSendBuffer, &dwSendLength);
    if (r < 0)
    {
        return -1;
    }

    dwRecvLength = sizeof(pbRecvBuffer);
    r = sc_transmit_data(*reader, pbSendBuffer, dwSendLength, pbRecvBuffer, &dwRecvLength, NULL);
    if (r < 0)
    {
        fprintf(stderr, "Error: %s\n", sc_get_error(r));
        return r;
    }

    return 0;
}

int ue_set_verifier_nonce(reader_t *reader,verifier_nonce_t *nonce){

    uint8_t pbSendBuffer[MAX_APDU_LENGTH_T0] = {0};
    uint8_t pbRecvBuffer[MAX_APDU_LENGTH_T0] = {0};
    uint32_t dwSendLength;
    uint32_t dwRecvLength;

    uint8_t ue_nonce[256] = {0};

    //toto tu musí zůstat kvuli sestavení commandu, viz odeslání lc
    uint8_t lc = 0;

    // user identifier
    memcpy(&ue_nonce[lc], nonce->nonce, NONCE_LENGTH);
    lc += NONCE_LENGTH;


    dwSendLength = sizeof(pbSendBuffer);
    int  r = apdu_build_command(CASE3S, CLA_APPLICATION, INS_NONCE_USER, 0x00, 0x00, lc, ue_nonce, 0, pbSendBuffer, &dwSendLength);
    if (r < 0)
    {
        return -1;
    }

    dwRecvLength = sizeof(pbRecvBuffer);
    r = sc_transmit_data(*reader, pbSendBuffer, dwSendLength, pbRecvBuffer, &dwRecvLength, NULL);
    if (r < 0)
    {
        fprintf(stderr, "Error: %s\n", sc_get_error(r));
        return r;
    }

    return 0;
}

int ue_show_K(reader_t *reader,user_cipher_output_t *ue_cipher_output){

    uint8_t pbSendBuffer[MAX_APDU_LENGTH_T0] = {0};
    uint8_t pbRecvBuffer[MAX_APDU_LENGTH_T0] = {0};
    uint32_t dwSendLength;
    uint32_t dwRecvLength;

    int r;

    dwSendLength = sizeof(pbSendBuffer);
    r = apdu_build_command(CASE2S, CLA_APPLICATION, INS_CYPHER, 0x00, 0x00, 0, NULL, 4 * AES_BLOCK_SIZE, pbSendBuffer, &dwSendLength);
    if (r < 0)
    {
        printf("chyba z karty");
        return -1;
    }

    dwRecvLength = sizeof(pbRecvBuffer);
    r = sc_transmit_data(*reader, pbSendBuffer, dwSendLength, pbRecvBuffer, &dwRecvLength, NULL);
    if (r < 0)
    {
        fprintf(stderr, "Error: %s\n", sc_get_error(r));
        return r;
    }

    memcpy(ue_cipher_output->cipher_output, pbRecvBuffer, 4 * AES_BLOCK_SIZE);


    uint8_t AES_cipher_user[256] = {0};
    WORD SW12;
    WORD La;
    uint8_t lc = 0;

    // AES cipher dates
    memcpy(&AES_cipher_user[lc], ue_cipher_output->cipher_output, 4 * AES_BLOCK_SIZE);
    lc += 4 * AES_BLOCK_SIZE;


    fprintf(stdout, "[+] TC request :\n");
    fprintf(stdout, "%02X %02X %02X %02X %02X ", CLA_APPLICATION, INS_CYPHER, 0x00,0x00, 4 * AES_BLOCK_SIZE);
    for (int i = 0; i < 4 * AES_BLOCK_SIZE; i++)
    {
        fprintf(stdout, "%02X ", ue_cipher_output->cipher_output[i]);
    }
    fprintf(stdout, "\n");


    SW12 = multosSendAPDU(CLA_APPLICATION, INS_CYPHER, 0x00, 0x00, lc, 0, &La, 0x00, AES_cipher_user, 4 * AES_BLOCK_SIZE, 1000);
    if (SW12 != 0x9000)
    {
        return -1;
    }

    fprintf(stdout, "[+] TC response :\n");
    fprintf(stdout, "%02X\n", SW12);
    fprintf(stdout, "\n");

    return 0;
}

int ue_get_user_data(reader_t *reader){
    uint8_t pbSendBuffer[MAX_APDU_LENGTH_T0] = {0};
    uint8_t pbRecvBuffer[MAX_APDU_LENGTH_T0] = {0};
    uint32_t dwSendLength;
    uint32_t dwRecvLength;

    int r;

    struct timespec ts_start1 = {0, 0};
    struct timespec ts_end1 = {0, 0};
    double elapsed_time1;

    clock_gettime(CLOCK_MONOTONIC, &ts_start1);


    dwSendLength = sizeof(pbSendBuffer);
    r = apdu_build_command(CASE2S, CLA_APPLICATION, INS_GET_DATA, 0x00, 0x00, 0, NULL, 142, pbSendBuffer, &dwSendLength);
    if (r < 0)
    {
        return -1;
    }

    dwRecvLength = sizeof(pbRecvBuffer);
    r = sc_transmit_data(*reader, pbSendBuffer, dwSendLength, pbRecvBuffer, &dwRecvLength, NULL);
    if (r < 0)
    {
        fprintf(stderr, "Error: %s\n", sc_get_error(r));
        return r;
    }

    FILE *userData;

    userData = fopen("/home/pi/web/userData.html", "w");

    time_t currentTime;
    time(&currentTime);

    struct tm *myTime = localtime(&currentTime);
    int systemTime = (myTime->tm_hour * 100) + myTime->tm_min;
    int userTimeIn;
    int userTimeOut;

    if(pbRecvBuffer[107] == ':')
        userTimeIn = ((pbRecvBuffer[105] - 48) * 10 + pbRecvBuffer[106] - 48) * 100 + (pbRecvBuffer[108] - 48) * 10 + pbRecvBuffer[109] - 48;
    else
        userTimeIn = ((pbRecvBuffer[105] - 48) * 100) + (pbRecvBuffer[107] - 48) * 10 + pbRecvBuffer[108] - 48;

    if(pbRecvBuffer[113] == ':')
        userTimeOut = ((pbRecvBuffer[111] - 48) * 10 + pbRecvBuffer[112] - 48) * 100 + (pbRecvBuffer[114] - 48) * 10 + pbRecvBuffer[115] - 48;
    else if(pbRecvBuffer[112] == ':')
        userTimeOut = ((pbRecvBuffer[110] - 48) * 10 + pbRecvBuffer[111] - 48) * 100 + (pbRecvBuffer[113] - 48) * 10 + pbRecvBuffer[114] - 48;
    else
        userTimeOut = (pbRecvBuffer[110] - 48) * 100 + (pbRecvBuffer[112] - 48) * 10 + pbRecvBuffer[113] - 48;


    if(userTimeOut > userTimeIn){
        if(systemTime > userTimeOut || systemTime < userTimeIn){
            fprintf(userData,"<center><p>Zamítnutí přístupu na základě času</p></center>");
            fclose(userData);
            return -1;
        }

    }
    if(systemTime < userTimeIn || systemTime > userTimeOut){
        fprintf(userData,"<center><p>Zamítnutí přístupu na základě času</p></center>");
        fclose(userData);
        return -1;
    }

    int endIndex = 0;
    int startIndex = 0;

    do {
        fprintf(userData,"<p>");
        if(endIndex == 0){
            fprintf(userData,"Name: ");
        }
        if(endIndex == 1){
            startIndex = 36;
            fprintf(userData,"Email: ");
        }
        if(endIndex == 2){
            fprintf(userData,"Phone: ");
            startIndex = 72;
        }
        if(endIndex == 3){
            fprintf(userData,"<br>\n");
            fprintf(userData,"Trustcore: ");
            startIndex = 84;
        }
        if(endIndex == 4){
            fprintf(userData,"Access time: ");
            startIndex = 105;
        }
        /*if(endIndex == 5){
            fprintf(userData,"Password: ");
            startIndex = 121;
        }*/

        for (int i = startIndex ; i < 121; i++) {
            if(pbRecvBuffer[i] == '*')break;
            fprintf(userData, "%c", pbRecvBuffer[i]);
        }
        fprintf(userData,"</p>\n");
        endIndex++;
    }while(endIndex != 5);
    fprintf(userData,"<br>\n<p>Current system time: %d:%d</p>\n",myTime->tm_hour,myTime->tm_min);

    fclose(userData);

    clock_gettime(CLOCK_MONOTONIC, &ts_end1);
    elapsed_time1 = ((double) ts_end1.tv_sec + 1.0e-9 * (double) ts_end1.tv_nsec) - ((double) ts_start1.tv_sec + 1.0e-9 * (double) ts_start1.tv_nsec);
    printf("Elapsed time (Transfer user data) = %f sec.\n\n", elapsed_time1);

    return 0;

}