#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include "pcsc/reader.h"
#include "src/controllers/verifier.h"
#include "src/controllers/multos/verifier.h"
# include "src/controllers/user.h"
# include "src/controllers/issue.h"

#include "helpers/multosio.h"

#include <sys/time.h>
#include <stdbool.h>
#include <wiringPi.h>

#define TC_AID "F0000002000001"

#define	LED_RED	21
#define	LED_GREEN	23
#define	RELAY 29

int tcSelectApp()
{
    int selected = multosSelectApplication((char*)TC_AID);
    return selected;
}

reader_t connect_card(){

    int r;
    ///choose multos Application
    uint8_t pbRecvBuffer[MAX_APDU_LENGTH_T0] = {0};
    uint32_t dwRecvLength;
    reader_t reader;

    r = sc_get_card_connection(&reader);
    if (r < 0)
    {
        fprintf(stderr, "Error: %s\n", sc_get_error(r));
        //exit(1);
    }

# ifndef NDEBUG
    fprintf(stdout, "[-] Command: APDU_SCARD_SELECT_APPLICATION\n");
# endif

    dwRecvLength = sizeof(pbRecvBuffer);
    r = sc_transmit_data(reader, APDU_SCARD_SELECT_APPLICATION, sizeof(APDU_SCARD_SELECT_APPLICATION), pbRecvBuffer, &dwRecvLength, NULL);
    if (r < 0)
    {
        fprintf(stderr, "Error: %s\n", sc_get_error(r));
        //exit(1);
    }

    return reader;
}

void print_help(){
    printf("-i Get Trust Core ID\n");
    printf("-u Update Trust Core Secret Key\n");
    printf("-v Authentication protocol\n");
    printf("-h Help\n");
}

int main(int argc, char **argv)
{

    user_identifier_t ue_identifier = {0};
    user_keys_t ue_keys = {0};
    user_nonce_t ue_nonce = {0};
    user_cipher_output_t ue_cipher_output = {0};

    verifier_identifier_t ve_identifier = {0};
    verifier_keys_t ve_keys = {0};
    verifier_nonce_t ve_nonce = {0};
    verifier_cipher_output_t ve_cipher_output = {0};
    verifier_session_key_t sessionKey = {0};

    int r;

    reader_t reader;

    /// Setup up MULTOS device
    if(!multosInit())
    {
        printf("Failed to initialise MULTOS device\n");
        return(1);
    }

    if(!multosReset())
    {
        printf("Failed to reset MULTOS device\n");
        return(1);
    }

    if(!tcSelectApp())
    {
        printf("Failed to choose Multos application");
        return(1);
    }

    if (!strcmp(argv[1], "-i")){
        /// verifier -  get + set verifier ID from multos card
        r = ve_get_verifier_identifier_M(&ve_identifier);
        if (r < 0) {
            fprintf(stderr, "Error: cannot get the verifier identifier!\n");
            return 1;
        }
        printf("%u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u",
               ve_identifier.buffer[0],ve_identifier.buffer[1],ve_identifier.buffer[2],ve_identifier.buffer[3],
               ve_identifier.buffer[4],ve_identifier.buffer[5],ve_identifier.buffer[6],ve_identifier.buffer[7],
               ve_identifier.buffer[8],ve_identifier.buffer[9],ve_identifier.buffer[10],ve_identifier.buffer[11],
               ve_identifier.buffer[12],ve_identifier.buffer[13],ve_identifier.buffer[14],ve_identifier.buffer[15]);
        return 0;
    }
    if (!strcmp(argv[1], "-u")){

        for(int i = 0; i < sizeof(ve_keys.private_key.sk); i++){
            ve_keys.private_key.sk[i] = atoi(argv[2 + i]);
        }

        /// verifier - set secret key on multos device
        r = ve_set_verifier_private_key_M(&ve_keys.private_key);
        if (r < 0)
        {
            fprintf(stderr, "Error: cannot set the private key!\n");
            return 1;
        }
        printf("Trust Core private key was updated successfully");
        return 0;
    }
    if (!strcmp(argv[1], "-v")){

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

        clock_gettime(CLOCK_MONOTONIC, &ts_start1);

        reader = connect_card();

        r = ue_connect(&reader, &ue_identifier);
        if(r < 0){
            fprintf(stderr,"ID uživatele neprošlo\n");
            wiringPiSetup() ;

            pinMode (LED_RED, OUTPUT) ;
            digitalWrite (LED_RED, HIGH) ;	// led on
            delay (5000);
            digitalWrite (LED_RED, LOW) ;	// led off
        }
        ///show-verify


        ///karta - ID, nonce na Rpi
        r = ue_get_user_nonce(&reader,&ue_nonce);
        if (r < 0) {
            fprintf(stderr, "Error (%d): nonce user!\n", r);
            wiringPiSetup() ;

            pinMode (LED_RED, OUTPUT) ;
            digitalWrite (LED_RED, HIGH) ;	// led on
            delay (5000);
            digitalWrite (LED_RED, LOW) ;	// led off
            return 1;
        }
        ///karta konec

        ///Rpi, poslání ID, nonce uživatele na TC

        // verifier - set user ID on multos
        r = ve_set_user_identifier_M(&ue_identifier);
        if (r < 0) {
            fprintf(stderr, "Error: cannot set the user1 id!\n");
            wiringPiSetup() ;

            pinMode (LED_RED, OUTPUT) ;
            digitalWrite (LED_RED, HIGH) ;	// led on
            delay (5000);
            digitalWrite (LED_RED, LOW) ;	// led off
            return 1;
        }
        // verifier - set user nonce on multos
        r = ve_set_user_nonce_M(&ue_nonce);
        if (r < 0) {
            fprintf(stderr, "Error: cannot set the user id!\n");
            wiringPiSetup() ;

            pinMode (LED_RED, OUTPUT) ;
            digitalWrite (LED_RED, HIGH) ;	// led on
            delay (5000);
            digitalWrite (LED_RED, LOW) ;	// led off
            return 1;
        }
        /// Rpi

        // verifier -  get + set verifier ID from multos device
        r = ve_get_verifier_identifier_M(&ve_identifier);
        if (r < 0) {
            fprintf(stderr, "Error: cannot get the verifier identifier!\n");
            wiringPiSetup() ;

            pinMode (LED_RED, OUTPUT) ;
            digitalWrite (LED_RED, HIGH) ;	// led on
            delay (5000);
            digitalWrite (LED_RED, LOW) ;	// led off
            return 1;
        }

        // verifier - generates and get verifier nonce from multos
        r = ve_get_verifier_nonce_M(&ve_nonce);
        if (r < 0) {
            fprintf(stderr, "Error: cannot set the verifier nonce!\n");
            wiringPiSetup() ;

            pinMode (LED_RED, OUTPUT) ;
            digitalWrite (LED_RED, HIGH) ;	// led on
            delay (5000);
            digitalWrite (LED_RED, LOW) ;	// led off
            return 1;
        }

        /// karta - odeslání nonce a ID ověřovatele

        r = ue_set_verifier_identifier(&reader,&ve_identifier);
        if (r < 0) {
            fprintf(stderr, "Error: cannot set the verifier ID na kartu!\n");
            wiringPiSetup() ;

            pinMode (LED_RED, OUTPUT) ;
            digitalWrite (LED_RED, HIGH) ;	// led on
            delay (5000);
            digitalWrite (LED_RED, LOW) ;	// led off
            return 1;
        }

        r = ue_set_verifier_nonce(&reader,&ve_nonce);
        if (r < 0) {
            fprintf(stderr, "Error: cannot set the verifier nonce na kartu!\n");
            wiringPiSetup() ;

            pinMode (LED_RED, OUTPUT) ;
            digitalWrite (LED_RED, HIGH) ;	// led on
            delay (5000);
            digitalWrite (LED_RED, LOW) ;	// led off
            return 1;
        }
        ///karta

        ///show - user -> Rpi, Rpi -> Tc

        r = ue_show_K(&reader,&ue_cipher_output);
        if (r < 0) {
            fprintf(stderr, "Error: Show na Rpi!\n");
            wiringPiSetup() ;

            pinMode (LED_RED, OUTPUT) ;
            digitalWrite (LED_RED, HIGH) ;	// led on
            delay (5000);
            digitalWrite (LED_RED, LOW) ;	// led off
            return 1;
        }

        ///show - user

        ///verify - TC -> rpi, rpi -> user

        r = ve_verify_M(&reader, &sessionKey, &ve_cipher_output, &ue_keys, &ve_identifier, &ue_identifier, &ve_nonce, &ue_nonce);
        if (r < 0) {
            fprintf(stderr, "Error: cannot verify!\n");
            wiringPiSetup() ;

            pinMode (LED_RED, OUTPUT) ;
            digitalWrite (LED_RED, HIGH) ;	// led on
            delay (5000);
            digitalWrite (LED_RED, LOW) ;	// led off
            return 1;
        }


        ///verify - TC

        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 (Authentication protocol tooks) = %f sec.\n\n", elapsed_time1);

        r = ue_get_user_data(&reader);
        if (r < 0) {
            fprintf(stderr, "Error: No data transfer!\n\n");
            wiringPiSetup() ;

            pinMode (LED_RED, OUTPUT) ;
            digitalWrite (LED_RED, HIGH) ;	// led on
            delay (5000);
            digitalWrite (LED_RED, LOW) ;	// led off

            system("xdg-open http://localhost:8000");
            return 1;
        }

        wiringPiSetup() ;
        pinMode (RELAY, OUTPUT) ;
        pinMode (LED_GREEN, OUTPUT) ;

        digitalWrite (RELAY, LOW) ;	// relay on
        digitalWrite (LED_GREEN, HIGH) ;	// led on
        delay (5000);
        digitalWrite (RELAY, HIGH) ;	// relay off
        digitalWrite (LED_GREEN, LOW) ;	// led off

        system("xdg-open http://localhost:8000");

        return 0;
    }

    if (!strcmp(argv[1], "-h")){

        print_help();

        return 0;
    }


}
