#define OS_CRYPT	/* Use OS supplied crypto random */

#include <windows.h>
#include <conio.h>
#include <time.h>
#include <ctype.h>
#ifdef notdef
#include <termios.h>
#include <unistd.h>
#endif
#include <stdio.h>
#include "types.h"
#include "bigdeal.h"
#include "collect.h"
#include "os.h"

extern FILE *flog;

#ifdef OS_CRYPT

#include <Wincrypt.h>

#ifdef notdef
/* reads from keypress, doesn't echo */
int getch(void) {
    struct termios oldattr, newattr;
    int ch;
    tcgetattr( STDIN_FILENO, &oldattr );
    newattr = oldattr;
    newattr.c_lflag &= ~( ICANON | ECHO );
    tcsetattr( STDIN_FILENO, TCSANOW, &newattr );
    ch = getchar();
    tcsetattr( STDIN_FILENO, TCSANOW, &oldattr );
    return ch;
}
#endif

/*
 * Use Windows crypto. I do not fully understand this, but copy/pasted code from Windows doc.
 * During testing it turned out some computers failed the first CryptAcquireContext with final parameter NULL
 * Most computers did not fail.
 * The current code is again copy/pasted. Seems to do the following: use current container if possible, otherwise create new
 * ???
 *
 * Let's get more bytes than needed. We actually only need 4/3*160 *bits*
 * which is 27 bytes. My trust in the OS is not high enough to dare that
 * currently
 */
#define N_OSC_BYTES 200

void MyHandleError(LPTSTR psz)
{
    fprintf(stderr, TEXT("An error occurred in the program. \n"));
    fprintf(stderr, TEXT("%s\n"), psz);
    fprintf(stderr, TEXT("Error number %x.\n"), GetLastError());
}

void
os_collect()
{
    HCRYPTPROV   hCryptProv;
    BYTE         osrand[N_OSC_BYTES];

    /*
     * Acquire a cryptographic provider context handle.
     */

    if(!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, 0)) {
	/*
	 * CAC failed
	* MyHandleError(TEXT("CAC"));
	 */
	if (GetLastError() == NTE_BAD_KEYSET)
        {
            // No default container was found. Attempt to create it.
            if(!CryptAcquireContext( &hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET)) 
            {
                MyHandleError(TEXT("Could not create the default ") TEXT("key container.\n"));
		return;
            }
        }
        else
        {
            MyHandleError(TEXT("A general error running ") TEXT("CryptAcquireContext."));
	    return;
        }
    }

    /*
     * Generate a random initialization vector.
     */

    if(CryptGenRandom(hCryptProv, N_OSC_BYTES, osrand)) {
	if (flog)
	    fprintf(flog, "Random sequence of %d bytes generated by OS\n", N_OSC_BYTES);
	/*
	 * Throw into entropy pool
	 * Guess four bits per byte, pessimistic
	 */
	collect_more((byte *) osrand, N_OSC_BYTES, 4*N_OSC_BYTES);
    } else {
	/*
	 * CAC failed
	 */
	MyHandleError(TEXT("CGR"));
    }
}
#else

void
os_collect() {
    int pid, getpid();

    collect_more((byte *) &frequency, sizeof(frequency), 0);
    collect_more((byte *) &epoch, sizeof(epoch), 8);
    pid = getpid();
    collect_more((byte *) &pid, sizeof(pid), 3);
}

#endif

static int highfreqclock;
static LARGE_INTEGER frequency, epoch;

void
os_start()
{

    /*
     * Start the clock running
     */
    if (QueryPerformanceFrequency(&frequency) == 0) {
	highfreqclock = 0;
    } else {
	highfreqclock = 1;
	QueryPerformanceCounter(&epoch);
	if (flog)
	    fprintf(flog, "Clock frequency %lld, epoch %lld\n",
		frequency.QuadPart, epoch.QuadPart);
    }
}

void
os_finish()
{

}

static int bestclock()
{
   LARGE_INTEGER t;

   if (highfreqclock) {
       QueryPerformanceCounter(&t);
       /* compute the elapsed time in ticks */
       return (t.QuadPart - epoch.QuadPart);
   }
#ifdef UCLOCKS_PER_SEC
    return (int) uclock();
#else
#ifdef CLOCKS_PER_SEC
    return (int) clock();
#else
There is no clock, this program needs it, so do not compile
#endif
#endif
}

#define MAX_INTERVAL_ENTROPY	6

int
getchtm(int *nbits)
/*
 * Read one character from standard input
 * Time the wait, and use bits for random
 * MUST be in cbreak mode for this to work
 */
{
    int t1, t2;
    int tdiff;
    int c;
    int shift;
    int b;

    /*
     * First get the best clock we have
     */
    t1 = bestclock();
    /*
     * Now get one character
     */
    c = getch();
    /*
     * If it is zero, which can happen with getch() under DOS
     * we have a two character sequence(function key probably)
     * and we make one character out of it by appending the next
     */
    if (c == 0)
	c = getch() + 256;
    /*
     * and get the clock after the keypress
     */
    t2 = bestclock();
    /*
     * Number of ticks we waited goes to tdiff
     */
    tdiff = t2 - t1;
    /*
     * Now we are going to believe half the bits we get
     */
    for (shift = tdiff, b = 0; shift>2 ; shift >>= 2, b++)
	;
    if (b < MAX_INTERVAL_ENTROPY)
	*nbits = b;
    else
	*nbits = MAX_INTERVAL_ENTROPY;
    /*
     * We send all the bits to the collect pool
     * but do not count the bits yet (parameter 0)
     * because higher layers might decide not to believe this
     * character, and its associated timing
     */
    collect_more((byte *) &t1, sizeof(t1), 0);
    collect_more((byte *) &t2, sizeof(t2), 0);

    if (flog)
	fprintf(flog, "Collected character %d, timediff %d, timebits %d\n",
	    c, tdiff, b);
    return c;
}

void
cbreak()
{

}

void
cooked()
{

}

int
legal_filename_prefix(char *s)
/*
 * Legal prefix to make legal name when three letter suffix added
 */
{

	if (*s == 0)	/* Too short */
		return 0;
	if (strlen(s) > 50)
		return 0;
	while (*s) {
		if (!isalnum((int) *s) && *s != '-' && *s != '_')
			return 0;
		s++;
	}
	return 1;
}

char *os_init_file_name()
{

	return "bigdeal.ini";
}
