#include <stdint.h>
#include <wolfssl/options.h>
#include <wolfssl/wolfcrypt/settings.h>
#include <wolfssl/wolfcrypt/ecc.h>
#include <wolfssl/wolfcrypt/pwdbased.h>
#include <wolfssl/wolfcrypt/sha512.h>
#include <wolfssl/wolfcrypt/chacha.h>

#include "../src/cryptoengine.h"

#include <fcntl.h>
#include <err.h>
#include <termio.h>
#include <linux/serial.h>
#include <stdio.h>
#include <errno.h>
#include <sys/poll.h>

#define HOMEPATH_PREFIX "~/.pvault"
//#define HOMEPATH_PREFIX "/root/.pvault"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_SVC_NAME_LEN 16

static char pass_prompt[200] = "";
static char pass_confirm[200] = "";
static const char pass_prompt_phrase[] = "Enter your passphrase: ";
static const char pass_confirm_phrase[] = "Confirm your passphrase : ";

static const char pass_prompt_svc[] = "Password: ";
static const char pass_confirm_svc[] = "Confirm Password: ";

static int sfd = -1;;
static int pk_fd = -1;

static struct vault_status Status = { };
static int get_vault_status(void);


struct __attribute__((packed)) tofu_packet {
    uint32_t cdc_magic; /* 0x5afeca5e */
    uint16_t cdc_cmd;
    uint16_t cdc_len;
    struct vault_header vh;
};

void clearScreen() {
    system("clear");
}

void disableEcho() {
    struct termios term;
    tcgetattr(0, &term);
    term.c_lflag &= ~ECHO;
    tcsetattr(0, TCSANOW, &term);
}

void enableEcho() {
    struct termios term;
    tcgetattr(0, &term);
    term.c_lflag |= ECHO;
    tcsetattr(0, TCSANOW, &term);
}

void printPrompt() {
    printf(pass_prompt);
}

static void getPassword(char *password, int size) {
    int i = 0;
    char c;

    while (i < size - 1) {
        c = getchar();
        if (c == '\n') {
            break;
        }
        password[i++] = c;
        if (i >= 99)
            break;
    }

    password[i] = '\0';
}

int confirmPassphrase(const char *passphrase) {
    char confirmation[100];

    printf(pass_confirm);
    getPassword(confirmation, sizeof(confirmation));

    return strcmp(passphrase, confirmation) == 0;
}

static char passphrase[100];
static int askpass(void)
{
    int ret = 0;
    disableEcho();
    printPrompt();
    getPassword(passphrase, sizeof(passphrase));
    printf("\n");

    if (confirmPassphrase(passphrase)) {
        printf("\nConfirmed.\n");
        ret = 1;
    } else {
        printf("These passwords don't match. Please try again.\n");
    }
    enableEcho();
    return ret;
}



static int rate_to_constant(int baudrate) {
#ifdef __MACH__
#define B(x) case x: return x
#else
#define B(x) case x: return B##x
#endif
    switch(baudrate) {
        B(50);     B(75);     B(110);    B(134);    B(150);
        B(200);    B(300);    B(600);    B(1200);   B(1800);
        B(2400);   B(4800);   B(9600);   B(19200);  B(38400);
        B(57600);  B(115200); B(230400); B(460800); B(500000);
        B(576000); B(921600); B(1000000);B(1152000);B(1500000);
default: return 0;
}
#undef B
}

/* Open serial port in raw mode, with custom baudrate if necessary */
static int serial_open(const char *device, int rate)
{
	struct termios options;
	int fd;
	int speed = 0;

	/* Open and configure serial port */
	if ((fd = open(device,O_RDWR|O_NOCTTY)) == -1)
		return -1;

	speed = rate_to_constant(rate);

#ifndef __MACH__
	if (speed == 0) {
		/* Custom divisor */
        struct serial_struct serinfo;
		serinfo.reserved_char[0] = 0;
		if (ioctl(fd, TIOCGSERIAL, &serinfo) < 0)
			return -1;
		serinfo.flags &= ~ASYNC_SPD_MASK;
		serinfo.flags |= ASYNC_SPD_CUST;
		serinfo.custom_divisor = (serinfo.baud_base + (rate / 2)) / rate;
		if (serinfo.custom_divisor < 1)
			serinfo.custom_divisor = 1;
		if (ioctl(fd, TIOCSSERIAL, &serinfo) < 0)
			return -1;
		if (ioctl(fd, TIOCGSERIAL, &serinfo) < 0)
			return -1;
		if (serinfo.custom_divisor * rate != serinfo.baud_base) {
			warnx("actual baudrate is %d / %d = %f",
			      serinfo.baud_base, serinfo.custom_divisor,
			      (float)serinfo.baud_base / serinfo.custom_divisor);
		}
	}
#endif

	fcntl(fd, F_SETFL, 0);
	tcgetattr(fd, &options);
	cfsetispeed(&options, speed ?: 460800);
	cfsetospeed(&options, speed ?: 460800);
	cfmakeraw(&options);
	options.c_cflag |= (CLOCAL | CREAD);
	options.c_cflag &= ~CRTSCTS;
	if (tcsetattr(fd, TCSANOW, &options) != 0)
		return -1;

	return fd;
}

static int tofu(const char *password)
{
    int ret = 0, i;
    int outlen = CRYPTO_KEY_SIZE;
    int siglen = PK_SIGNATURE_SIZE;
    uint8_t clr_sig[PK_SIGNATURE_SIZE];
    struct tofu_packet tofu;
    uint8_t Ke[CRYPTO_KEY_SIZE], Sm[CRYPTO_KEY_SIZE];
    uint8_t resp[256];
    char privkey_fname[32];
    uint32_t mgc = 0xffffffff;
    int hash, hash_len;
    uint8_t iv[24];
    uint8_t sig_dec_verify[64];
    struct pollfd pfd;
    uint32_t qxLen = CRYPTO_KEY_SIZE, qyLen = CRYPTO_KEY_SIZE,
             dLen = CRYPTO_KEY_SIZE;


    tofu.cdc_magic = VAULT_MAGIC;
    tofu.cdc_cmd = CDC_TOFU_INIT;
    tofu.cdc_len = sizeof(struct tofu_packet) - 8;
    
    tofu.vh.magic = VAULT_MAGIC;
    tofu.vh.size = sizeof(struct tofu_packet) - 8;
    

    ecc_key ecc;
    ChaCha cha;
    RNG rng;
    
    Sha512 sha;


    /* Initialize Rng (needed for master key + salt + secret */
    wc_InitRng(&rng);
    
    /* Generate ID */
    wc_RNG_GenerateBlock(&rng, (void *)tofu.vh.id, 8);
    sprintf(privkey_fname, "%08x-%08x.der", tofu.vh.id[0], tofu.vh.id[1]);
    printf("ID: %s\n", privkey_fname);

    /* Generate salt */
    wc_RNG_GenerateBlock(&rng, tofu.vh.host_salt, SALT_LEN);
    printf("Salt: ");
    for (i = 0; i < SALT_LEN; i++) {
        printf("%02x", tofu.vh.host_salt[i]);
    }
    printf("\n");

    /* Derive device key (password based) to create encryption key */
    ret = wc_PBKDF2(Ke, password, strlen(password), tofu.vh.host_salt, SALT_LEN, 1000, CRYPTO_KEY_SIZE, WC_SHA512);
    wc_Chacha_SetKey(&cha, Ke, CRYPTO_KEY_SIZE);
    wc_Chacha_SetIV(&cha, tofu.vh.host_seed, 0);

    /* Generate master signing key */
    wc_ecc_init(&ecc);
    wc_ecc_make_key(&rng, CRYPTO_KEY_SIZE, &ecc);
    qxLen = CRYPTO_KEY_SIZE;
    qyLen = CRYPTO_KEY_SIZE;
    wc_ecc_export_public_raw(&ecc, tofu.vh.auth_pubkey, &qxLen, tofu.vh.auth_pubkey + CRYPTO_KEY_SIZE, &qyLen);
    printf("Public signing key %d: ", qxLen + qyLen);
    for (i = 0; i < qxLen + qyLen; i++) {
        printf("%02x", tofu.vh.auth_pubkey[i]);
    }
    printf("\n");

    
    /* Generate key seed */
    wc_RNG_GenerateBlock(&rng, tofu.vh.host_seed, SEED_LEN);
    printf("Seed: ");
    for (i = 0; i < SEED_LEN; i++) {
        printf("%02x", tofu.vh.host_seed[i]);
    }
    printf("\n");

    wc_InitSha512(&sha);
    for (hash = 0; hash < SHA_PAYLOAD_SIZE;) {
        hash_len = WC_SHA512_BLOCK_SIZE;
        if ((SHA_PAYLOAD_SIZE - hash) < hash_len)
            hash_len = SHA_PAYLOAD_SIZE - hash;
        wc_Sha512Update(&sha, ((uint8_t*)&tofu.vh) + hash, hash_len);
        hash += hash_len;
        printf("update %d/%d\n", hash, SHA_PAYLOAD_SIZE);
    }
    wc_Sha512Final(&sha, tofu.vh.digest);
    printf("Digest (%d): ", sizeof(tofu.vh.digest));
    for (i = 0; i < sizeof(tofu.vh.digest); i++) {
        printf("%02x ", tofu.vh.digest[i]);
        if ((i % 16) == 15)
            printf("\n");
    }
    printf("\n");
    wc_Sha512Free(&sha);

    {
        mp_int r, s;
        mp_init(&r); mp_init(&s);
        ret = wc_ecc_sign_hash_ex(tofu.vh.digest, VAULT_DIGEST_SIZE, &rng, &ecc,
                &r, &s); 
        mp_to_unsigned_bin(&r, &clr_sig[0]);
        mp_to_unsigned_bin(&s, &clr_sig[CRYPTO_KEY_SIZE]);
        mp_clear(&r);
        mp_clear(&s);
    }
    printf("Signature (%d) returned %d:\n", PK_SIGNATURE_SIZE, ret);
    for (i = 0; i < PK_SIGNATURE_SIZE; i++) {
        printf("%02x ", clr_sig[i]);
        if ((i % 16) == 15)
            printf("\n");
    }
    printf("\n");
    {
        int vr, vres;
        mp_int r, s;
        mp_init(&r); mp_init(&s);
        mp_read_unsigned_bin(&r, &clr_sig[0], CRYPTO_KEY_SIZE);
        mp_read_unsigned_bin(&s, &clr_sig[CRYPTO_KEY_SIZE], CRYPTO_KEY_SIZE);
        vr = wc_ecc_verify_hash_ex(&r, &s, tofu.vh.digest, VAULT_DIGEST_SIZE,
            &vres, &ecc);
        mp_clear(&r);
        mp_clear(&s);
        printf("sanity check verify ret %d res %d\n", vr, vres);
    }


    wc_Chacha_SetIV(&cha, tofu.vh.host_seed, 0);
    wc_Chacha_Process(&cha, tofu.vh.signature, clr_sig, siglen);
    wc_Chacha_SetIV(&cha, tofu.vh.host_seed, 0);
    wc_Chacha_Process(&cha, sig_dec_verify, tofu.vh.signature, siglen);

    if (memcmp(clr_sig, sig_dec_verify, siglen) != 0) {
        printf("Error verifying chacha encryption\n");
        return 4;
    }

    printf("Packet (%d): ", sizeof(tofu));
    for (i = 0; i < sizeof(tofu); i++) {
        printf("%02x ", *(((uint8_t*)(&tofu)) + i));
        if ((i % 16) == 15)
            printf("\n");
    }
    printf("\n");

    pfd.fd = sfd;
    pfd.events = POLLIN;
    for(i = 0; i < 10; i++) {
        char buf[8];
        ret = poll(&pfd, 1, 100);
        if (ret > 0) {
            read(sfd, buf, 8);
        } 
    }

    ret = write(sfd, &tofu, sizeof(tofu));
    printf("Sent %d/%d bvtes (tofu) \r\n", ret, sizeof(tofu));
    do {
        ret = read(sfd, &mgc, 4);
    } while (mgc != 0x5afeca5e);

    memcpy (resp, &mgc, 4);
    ret = read(sfd, resp + 4, 64 + 4);
    if (ret >= 4) {
        uint16_t code = *((uint16_t *)(resp + 4));
        uint16_t len = *((uint16_t *)(resp + 6));

        printf("received %d bytes: resp %04x len %04x\n", ret + 4, *((uint16_t *)(resp + 4)), *((uint16_t *)(resp + 6)));
        if (ret > 4) {
            printf("MSG: %s\r\n", resp + 8);
        }
        if (code == CDC_OK) {
            int fd;
            uint8_t privkey_buf[2 * CRYPTO_KEY_SIZE];
            char pkfname[MAX_PATH];
            char *homedir;
            homedir = getenv("HOME");
            if (!homedir || strlen(homedir) == 0) {
                perror("getenv(HOME)");
                exit(1);
            }

            snprintf(pkfname, MAX_PATH - 1, "%s/.pvault/%s", homedir, privkey_fname);
            printf("Saving private key %s\n", privkey_fname);
            fd = open(pkfname, O_WRONLY | O_CREAT | O_EXCL, 0600);
            if (fd < 0) {
                printf("FATAL Error opening private key: %s\n", strerror(errno));
                return 6;
            }
            outlen = 2 * CRYPTO_KEY_SIZE;
            if (wc_ecc_export_private_raw(&ecc, privkey_buf, &qxLen,
                        privkey_buf + qxLen, &qyLen,
                        privkey_buf + qxLen + qyLen, &dLen) != 0) {
                fprintf(stderr, "Unable to export private key to DER\n");
                exit(2);
            }
            outlen = qxLen + qyLen + dLen;
            printf("Exporting private key (%d bytes)\n", outlen);
            write(fd, privkey_buf, outlen); 
            wc_ecc_free(&ecc);
            close(fd);
        }
    }
    return 0;
}

static void addservice(struct vault_service *vs, uint8_t *Ke)
{
    struct vault_service vs_local;
    char gadget_passphrase[64];
    int ret;
    uint8_t Ke_local[CRYPTO_KEY_SIZE];
    uint8_t eccRawKey[CRYPTO_KEY_SIZE * 3];
    uint8_t snd_buffer[sizeof(struct cdc_packet_hdr) + SVC_SIZE];
    struct cdc_packet_hdr *hdr = (struct cdc_packet_hdr *)snd_buffer;
    int i;
    struct pollfd pfd;
    uint32_t mgc = 0;
    int interactive = 0;
    uint32_t slot = 0;
    Sha512 sha;
    ecc_key ecc;
    ChaCha cha;
    RNG rng;
    strcpy(pass_prompt, pass_prompt_svc);
    strcpy(pass_confirm, pass_confirm_svc);


    if (!vs) {
        vs = &vs_local;
        Ke = Ke_local;
        interactive = 1;
        printf("Service name (max 16 char.) : ");
        scanf("%s", vs->name); 
        printf("\n");
        printf("Username (empty = none) : ");
        if (scanf("%s", vs->user) <= 0)
            vs->user[0] = '\0';
        getchar();
        while(!askpass()) {
            printf("Press a key to continue...\r\n");
            getchar();
        }
        strcpy((char *)(vs->pass), passphrase); 
    }
    vs->flags = SVC_FLAG_ACTIVE;
    vs->reserved = 0;

    if (!Ke) {
        printf("Unspecified key\n");
        return;
    }


    wc_InitSha512(&sha);
    wc_Sha512Update(&sha, (uint8_t *)vs, WC_SHA512_BLOCK_SIZE);
    wc_Sha512Final(&sha, vs->dig);

    printf("\n----- RECAP -----\n");
    printf("Service name: %s\n", vs->name);
    printf("Username: %s\n", vs->user);
    printf("Password: ******\n");
    printf("Sha: ");
    for (i = 0; i < VAULT_DIGEST_SIZE; i++) {
        printf("%02x", vs->dig[i]);
    }
    printf("\n-----------------\n\n");


    if (interactive)  {
        printf("Service configured, press a key to send, ctrl+c to abort...\r\n");
        disableEcho();
        printf("Enter gadget passphrase to encrypt and send info: ");
        getPassword(gadget_passphrase, 64);
        enableEcho();
        printf("\n");
        /* Derive device key (password based) to create encryption key */
        ret = wc_PBKDF2(Ke, gadget_passphrase, strlen(gadget_passphrase),
                Status.salt, SALT_LEN, 1000, CRYPTO_KEY_SIZE, WC_SHA512);

        if (ret < 0) {
            printf("Failed to derive password.\n");
            return;
        }
    }


    lseek(pk_fd, SEEK_SET, 0);
    ret = read(pk_fd, eccRawKey, CRYPTO_KEY_SIZE * 3);
    if (ret != CRYPTO_KEY_SIZE * 3) {
        printf("Error reading signing key\n");
        return;
    }

    wc_ecc_init(&ecc);
    if (wc_ecc_import_unsigned(&ecc, eccRawKey, eccRawKey + CRYPTO_KEY_SIZE,
              eccRawKey + 2 * CRYPTO_KEY_SIZE, ECC_SECP256R1) < 0) { 
        printf("Failed importing signing key\n");
        return;
    }

    slot = Status.first_avail;

    /* Initialize Rng (needed for sign)*/
    wc_InitRng(&rng);

    printf("Slot is %u\n", slot);
    {
        mp_int r, s;
        mp_init(&r); mp_init(&s);
        ret = wc_ecc_sign_hash_ex(vs->dig, VAULT_DIGEST_SIZE, &rng, &ecc,
                &r, &s);
        mp_to_unsigned_bin(&r, &vs->sig[0]);
        mp_to_unsigned_bin(&s, &vs->sig[CRYPTO_KEY_SIZE]);
        mp_clear(&r);
        mp_clear(&s);
    }

    printf("Signature (%d):\n", PK_SIGNATURE_SIZE);
    for (i = 0; i < PK_SIGNATURE_SIZE; i++) {
        printf("%02x ", vs->sig[i]);
        if ((i % 16) == 15)
            printf("\n");
    }
    printf("\n");

    wc_ecc_free(&ecc);

    /* Copy the part in clear (flags, reserved) */
    memcpy(snd_buffer + sizeof(struct cdc_packet_hdr), vs, SVC_ENC_OFF);

    /* Set Key and IV */
    wc_Chacha_SetKey(&cha, Ke, CRYPTO_KEY_SIZE);
    wc_Chacha_SetIV(&cha, Status.seed, SVC_FLASH_OFFSET + slot * SVC_SIZE);
    wc_Chacha_Process(&cha, snd_buffer + sizeof(struct cdc_packet_hdr) + SVC_ENC_OFF, 
            ((uint8_t *)(vs)) + SVC_ENC_OFF, SVC_ENC_SIZE);

    hdr->magic = VAULT_MAGIC;
    hdr->cmd = CDC_ADDSERV;
    hdr->len = SVC_SIZE;

    pfd.fd = sfd;
    pfd.events = POLLIN;
    for (i = 0; i < 10; i++) {
        char buf[8];
        ret = poll(&pfd, 1, 10);
        if (ret > 0) {
            read(sfd, buf, 8);
        } 
    }
    
    printf("Packet (%d): ", sizeof(snd_buffer));
    for (i = 0; i < sizeof(snd_buffer); i++) {
        printf("%02x ", snd_buffer[i]);
        if ((i % 16) == 15)
            printf("\n");
    }
    printf("\n");

    printf("Sending svc packet... \n");
    write(sfd, snd_buffer, sizeof(snd_buffer));

    for (i = 0; i < 3; i++) {
        char buf[256];
        ret = poll(&pfd, 1, 5000);
        if (ret > 0) {
            uint16_t code;
            do {
                ret = read(sfd, &mgc, 4);
            } while (mgc != 0x5afeca5e);
            ret = read(sfd, buf, 256);
            code = *((uint16_t *)(buf));
           
            printf("Received response (code %02x, len %d): %s\n",
                    code, ret, (code == CDC_FAIL)?(buf + 4):""); 
            if ((code == CDC_FAIL) || ((code == CDC_OK) && ret == 4))
                break;
        } 
    }

}


char* removeQuotes(char* str) {
    int len = strlen(str);
    int i;

    for (i = 0; i < len; i++)
        if (str[i] != '\"')
            break;
    str = str + i;

    len = strlen(str);

    for (i = len -1; i > 0; i--)
        if (str[i] == '\"')
            str[i] = '\0';
    return str;
}

static void parseCSV(const char* filename) {
    struct vault_service svc;
    FILE* file = fopen(filename, "r");
    uint8_t cKe[CRYPTO_KEY_SIZE];
    int ret;
    char gadget_passphrase[64 + 1];
    char line[1024];
    int header_line_skipped = 0;
    if (file == NULL) {
        printf("Failed to open the file.\n");
        return;
    }

    printf("Enter gadget passphrase to encrypt and send info: ");
    disableEcho();
    getPassword(gadget_passphrase, 64);
    enableEcho();
    printf("\n");
    /* Derive device key (password based) to create encryption key */
    ret = wc_PBKDF2(cKe, gadget_passphrase, strlen(gadget_passphrase),
            Status.salt, SALT_LEN, 1000, CRYPTO_KEY_SIZE, WC_SHA512);

    if (ret < 0) {
        printf("Failed to derive password.\n");
        return;
    }

    while (fgets(line, sizeof(line), file)) {
        int i;

        // Skip header line
        if (!header_line_skipped) {
            header_line_skipped = 1;
            continue;
        }

        // Parse each line of the CSV
        char* token = strtok(line, ",");
        if (token == NULL) {
            continue;
        }

        // Remove quotes from the fields
        token = removeQuotes(token);
        char* url = token;

        // Strip "https:" if present
        if (strncmp(url, "https:", 6) == 0) {
            url += 6;
        }
        
        // Strip "http:" if present
        if (strncmp(url, "http:", 5) == 0) {
            url += 6;
        }

        // Strip slashes ("/") if present
        while (*url == '/') {
            url++;
        }

        // Strip content after the domain name if present
        char* slash = strchr(url, '/');
        if (slash != NULL) {
            *slash = '\0';
        }

        // Parse the domain from right to left
        char* domainStart = NULL;
        char domain[MAX_SVC_NAME_LEN + 1] = "";
        char *domainDot = strrchr(url, '.');

        if (domainDot != NULL) {
            *domainDot=',';
            domainStart = strrchr(url, '.');
            *domainDot='.';
        }
        if (domainStart == NULL) {
            strncpy(domain, url, MAX_SVC_NAME_LEN);
            domain[strlen(url)] = '\0';
        } else {
            int domainLen = strlen(domainStart + 1);
            strncpy(domain, domainStart + 1, domainLen);
        }

        // Store the extracted domain, user, and pass fields
        memset(&svc, 0, sizeof(svc));
        printf("Domain: %s\n", domain);
        strncpy(svc.name,domain, 16); 

        token = strtok(NULL, ",");
        if (token != NULL) {
            token = removeQuotes(token);
            strncpy(svc.user, token, sizeof(svc.user) - 1);
        }

        token = strtok(NULL, ",");
        if (token != NULL) {
            token = removeQuotes(token);
            strncpy(svc.pass, token, sizeof(svc.pass));
        }
        for (i = 0; i < 3; i++) { 
            ret = get_vault_status();
            if (ret == 0)
                break;
        }
        if (ret < 0) {
            printf("Adding service: failed.\n");
            getchar();
            return;
        }
        addservice(&svc, cKe);
    }
    fclose(file);
}


void printMenu() {
    printf("\n");
    printf("0) Device status\n");
    printf("1) TOFU\n");
    printf("2) Add service\n");
    printf("3) Import passwords from CSV\n");
    printf("q) Quit\n\n");
}

char getUserInput() {
    char input;
    printf("Choice: ");
    scanf(" %c", &input);
    getchar(); // Consume the newline character
    return input;
}

void processChoice(char choice) {
    char csv_filename[PATH_MAX];
    switch (choice) {
        case '0':
            // Handle option 0 (Device status)
            clearScreen();
            printf("Device status selected.\n");
            break;
        case '1':
            // Handle option 1 (TOFU)
            clearScreen();
            printf("TOFU selected.\n");
            strcpy(pass_prompt, pass_prompt_phrase);
            strcpy(pass_confirm, pass_confirm_phrase);
            memset(passphrase, 0, sizeof(passphrase));
            while(!askpass()) {
                printf("Press a key to continue...\r\n");
                getchar();
            }

            tofu(passphrase);
            break;
        case '2':
            // Handle option 2 (Add service)
            clearScreen();
            printf("Add service selected.\n");
            strcpy(pass_prompt, pass_prompt_phrase);
            strcpy(pass_confirm, pass_confirm_phrase);
            if (Status.state < 0x02) {
                printf("Cannot add service: device not initialized.\n");
                break;
            }
            if (pk_fd < 0) {
                printf("Cannot add service: private key not present for this device.\n");
                break;
            }
            addservice(NULL, NULL);
            break;
        case '3':
            // Handle option 3 (Import CSV)
            clearScreen();
            printf("CSV file: ");
            scanf("%s", csv_filename);
            getchar();
            parseCSV(csv_filename);
            break;
        case 'q':
        case 'Q':
            // Quit the program
            printf("Quitting...\n");
            exit(0);
        default:
            printf("Invalid choice.\n");
            break;
    }

    printf("Press Enter to continue...\n");
    getchar(); // Wait for Enter key
}


static int get_vault_status(void)
{
    uint32_t rcount = 0;
    struct cdc_packet_hdr hdr;
    int ret;
    int i;
    struct pollfd pfd;
    uint8_t rxbuf[sizeof(struct cdc_packet_hdr) + sizeof(struct vault_status)];
    pfd.fd = sfd;
    pfd.events = POLLIN;

    hdr.magic = VAULT_MAGIC;
    hdr.cmd = CDC_STATUS;
    hdr.len = 0;
    for (i = 0; i < 3; i++) {
        ret = write(sfd, &hdr, sizeof(hdr));
        if (ret <= 0)
            return ret;
    
        ret = poll(&pfd, 1, 1000);
        if (ret == 0) {
            printf("Get status: timeout");
            continue;
        }
        if (ret < 0) {
            perror("get_status(): polling device");
            return -1;
        }
        do {
            ret = read(sfd, rxbuf + rcount, sizeof(rxbuf) - rcount);
            if (ret < 0) {
                return -1;
            }
            rcount += ret;
        } while (rcount < sizeof(rxbuf));
        break;
    }
    if (rcount > 0) {
        if (*(uint32_t *)(rxbuf) != VAULT_MAGIC)
            return -2;
        if (*(uint16_t *)(rxbuf + 4) != CDC_STATUS)
            return -3;
        memcpy(&Status, rxbuf + sizeof(struct cdc_packet_hdr), sizeof(Status));
        return 0;
    }
    return -1;
}


static const char vstatenames[][32] = {
    "OFF",
    "TOFU",
    "BOOTUP",
    "VERIFY_PASSPHRASE",
    "VERIFY_FAILED",
    "MAIN_MENU",
    "SETTINGS_MENU",
    "SERVICE_LIST"
};




int main(int argc, char *argv[]) 
{
    clearScreen();
    system("mkdir -p " HOMEPATH_PREFIX);
    
    if (argc != 2) {
        fprintf(stderr, "Usage: %s TTY\n", argv[0]);
        return 2;
    }

    sfd = serial_open(argv[1], 115200);
    if (sfd < 0) {
        perror("serial_open");
        if (errno == EACCES) {
            printf("\n\nPerhaps you should review your permission for %s, or 'sudo adduser $YOUR_USER dialout'\r\n", argv[1]);
        }
        return 1;
    }
    while (1) {
        char choice;
        if (get_vault_status() < 0) {
            printf("Not connected\n");
        } else {
            char pkfname[MAX_PATH];
            char *homedir;
            homedir = getenv("HOME");
            if (!homedir || strlen(homedir) == 0) {
                perror("getenv(HOME)");
                exit(1);
            }

            snprintf(pkfname, MAX_PATH - 1, "%s/.pvault/%08x-%08x.der", homedir, Status.id[0], Status.id[1]);
            printf("Device %08x-%08x Connected.\n", Status.id[0], Status.id[1]);
            pk_fd = open(pkfname, O_RDONLY);
            if (pk_fd < 0) {
                perror("open");
                printf(pkfname);
                printf("\n");

                printf("Private key not available.\n");
            } else {
                printf("Private key loaded.\n");
            }
            printf("State: %s - Services: %hu. Spot: %hu\n", 
                    vstatenames[Status.state], Status.services_active, Status.first_avail);
        }
        printMenu();
        choice = getUserInput();
        processChoice(choice);
    }

    return 0;
}

