/*
 * keygen_args.cpp
 *
 * Notes: since argv[] array contains c-style char strings, I chose to use C
 *        functions to work on them instead of dealing with C++ class string
 *        conversions.
 *
 */

#include "keygen_args.h"
#include "miller_rabin.h"

char *pubkey;
char *prikey;
long prime_p;
long prime_q;
int random_f;

int keygen_args(int argc, char **argv)
{
    /* flags */
    int primep_f  = 0;
    int primeq_f  = 0;
    int fname_f   = 0;
    int usage_f   = 0;
    random_f      = 0;
    int primep_args = 0;
    int primeq_args = 0;
    int fname_args  = 0;
    int opt_args    = 0;

    int i;
    for (i = 1; i < argc; i++)
    {
        if (argc > 9)
        {
            fprintf(stderr, "too many options\n");
            usage_f = 1;
            break;
        }

        /* skip non option arguments */
        if (!strchr(argv[i], '-'))
        {
            opt_args++;

            if (primep_f)
            {
                if (i-1 == primep_f)
                    primep_args++;

                /* not allowed to have 2 args */
                if ((i-2 == primep_f) && argv[i-1][0] != '-')
                {
                    fprintf(stderr, "too many arguments to \"-p\" option\n");
                    usage_f = 1;
                    break;
                }
            }

            if (primeq_f)
            {
                if (i-1 == primeq_f)
                    primeq_args++;

                /* not allowed to have 2 args */
                if ((i-2 == primeq_f) && argv[i-1][0] != '-')
                {
                    fprintf(stderr, "too many arguments to \"-q\" option\n");
                    usage_f = 1;
                    break;
                }
            }

            if (fname_f)
            {
                if ((i-1 == fname_f) || (i-2 == fname_f))
                    fname_args++;

                if ((i-3 == fname_f)) /* not allowed to have 3 args */
                {
                    fprintf(stderr, "too many arguments to \"-o\" option\n");
                    usage_f = 1;
                    break;
                }
            }

            continue;
        }
        else if (strcmp(argv[i], "-p") == 0)
            primep_f = i;
        else if (strcmp(argv[i], "-q") == 0)
            primeq_f = i;
        else if (strcmp(argv[i], "-o") == 0)
            fname_f = i;
        else if (strcmp(argv[i], "-c") == 0)
            random_f = i;
        else
        {
            fprintf(stderr, "unknown option \"%s\"\n", argv[i]);
            usage_f = 1;
            break;
        }
    }

    /* error checking */
    if (usage_f || opt_args > 4 || fname_args == 1)
    {
        if (fname_args == 1)
            fprintf(stderr, "too few arguments to \"-o\" option\n");
        printf("usage: %s [-p] [-q] [-o pubkey prikey] [-c]\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    ssize_t amount_read = 0;
    int args_parsed = 0;
    size_t line_sz = 0;
    char *line_ptr = NULL;

    /* handle key filenames */
    if (fname_args == 2)
    {
        pubkey = strdup(argv[fname_f+1]);
        prikey = strdup(argv[fname_f+2]);
    }
    else if (fname_f) /* fname_args equals 0 here */
    {
        /* ask user for the filenames */
        puts("please provide pubkey and prikey filenames separated by a space");
        printf("e.g. \"pubkey.xml prikey.xml\": ");


        pubkey = (char *) malloc(sizeof(char) * 100);
        prikey = (char *) malloc(sizeof(char) * 100);
        if (!line_ptr || !pubkey || !prikey)
        {
            perror("malloc");
            exit(EXIT_FAILURE);
        }

        do
        {
            fflush(stdin);
            amount_read = getline(&line_ptr, &line_sz, stdin);
            args_parsed = sscanf(line_ptr, "%s %s", pubkey, prikey);
            if (args_parsed != 2)
                fprintf(stderr, "invalid input, please try again: ");
        } while (args_parsed != 2);
    }
    else
    {
        pubkey = strdup("pubkey.xml");
        prikey = strdup("prikey.xml");
    }

    if (primep_f)
    {
	    /* if number was not specified or it's not a prime ask for the number */
        if (primep_args == 0)
        {
            printf("please provide a value for prime p: ");

            do
            {
                fflush(stdin);
                amount_read = getline(&line_ptr, &line_sz, stdin);
                args_parsed = sscanf(line_ptr, "%ld", &prime_p);
                if (args_parsed != 1)
                    fprintf(stderr, "invalid input, please try again: ");
                if (!miller_rabin_16(prime_p))
                    fprintf(stderr, "not a prime number, please try again: ");
            } while (args_parsed != 1 || !miller_rabin_16(prime_p));
        }
        else
        {
            prime_p = atol(argv[primep_f+1]);
            if (!miller_rabin_16(prime_p))
            {
                fprintf(stderr, "prime p on the command line is not prime\n");
                exit(EXIT_FAILURE);
            }
        }
    }

    if (primeq_f)
    {
	    /* if number was not specified or it's not a prime ask for the number */
        if (primeq_args == 0)
        {
            printf("please provide a value for prime q: ");

            do
            {
                fflush(stdin);
                amount_read = getline(&line_ptr, &line_sz, stdin);
                args_parsed = sscanf(line_ptr, "%ld", &prime_q);
                if (args_parsed != 1)
                    fprintf(stderr, "invalid input, please try again: ");
                if (!miller_rabin_16(prime_q))
                    fprintf(stderr, "not a prime number, please try again: ");
            } while (args_parsed != 1 || !miller_rabin_16(prime_q));
        }
        else
        {
            prime_q = atol(argv[primeq_f+1]);
            if (!miller_rabin_16(prime_q))
            {
                fprintf(stderr, "prime q on the command line is not prime\n");
                exit(EXIT_FAILURE);
            }
        }
    }

    if (line_ptr)
        free(line_ptr);

    return 0;
}