///////////////////////////////////////////////////
// DIOmain.c
// 720020-11891
//
// Applied Data Systems
//
// See Dio.h for a description and history
//
///////////////////////////////////////////////////

#include <windows.h>
#include "DIOapp.h"
#include "DIO.h"

void BitWalkThread(LPVOID p);

// Using globals here so all threads can know what the available
// inputs and outputs are without calling IOCTLs.  Should be ok
// since they don't change.
DWORD dwOutput;			// Output capable DIO lines
DWORD dwInput;			// Input capable DIO lines

// WinMain
//
// Main application thread.  Presents a menu of DIO operations
// for the user to select from.  User can perform DIO operations
// until selecting to quit.

int WINAPI WinMain(
    HINSTANCE   hInst,
    HINSTANCE   hInstPrev,
    LPTSTR      lpszCmdLine,
    int         nCmdShow)
{
	HANDLE hDioPort = NULL;
	WCHAR wszPortName [] = TEXT("DIO1:");	// DIO port name
	DWORD dwDioMask;			// DIO driver mask
	DWORD dwNumberOfBytesRead;
	DWORD nOption;
	DWORD dwDioDir;
	BOOL bQuit = FALSE;
	DWORD dwDio;
	HANDLE hBitWalkThread = NULL;
	char szBuf[5];

	// Open the DIO port and get the port handle
	hDioPort = CreateFile(	wszPortName, 
							GENERIC_WRITE | GENERIC_READ, 
							0, 
							NULL, 
							OPEN_EXISTING, 
							FILE_ATTRIBUTE_NORMAL, 
							NULL);
	if (hDioPort == NULL) {
		RETAILMSG(1, (TEXT("Could not open port %s.\r\n"), wszPortName));
		RETAILMSG(1, (TEXT("GetLastError returned 0x%x"), GetLastError()));
		return 1;
	}
	wprintf(TEXT("Opened %s.\r\n"), wszPortName);
	RETAILMSG(1, (TEXT("Opened %s\r\n"), wszPortName));

	// Get the output capable lines
	if(!DeviceIoControl(	hDioPort, 
							DIO_IOCTL_CAPABLE_OUTPUTS, 
							NULL, 
							0, 
							&dwOutput, 
							sizeof(dwOutput),
							&dwNumberOfBytesRead, 
							NULL ))
	{
		RETAILMSG(1, (TEXT("DeviceIoControl : DIO_IOCTL_GET_CAPABLE_OUTPUTS failed.\r\n")));
		RETAILMSG(1, (TEXT("GetLastError returned 0x%x"), GetLastError()));
		bQuit = TRUE;
	}

	// Get the input capable lines
	if(!DeviceIoControl(	hDioPort, 
							DIO_IOCTL_CAPABLE_INPUTS, 
							NULL, 
							0, 
							&dwInput, 
							sizeof(dwInput),
							&dwNumberOfBytesRead, 
							NULL ))
	{
		RETAILMSG(1, (TEXT("DeviceIoControl : DIO_IOCTL_GET_CAPABLE_INPUTS failed.\r\n")));
		RETAILMSG(1, (TEXT("GetLastError returned 0x%x"), GetLastError()));
		bQuit = TRUE;
	}

	// Do the main menu loop until the user wants to quit
	while (bQuit == FALSE) {
		printf(	"\r\n(1) Read DIO\r\n"
				"(2) Write DIO\r\n"
				"(3) Read DIO direction\r\n"
				"(4) Set DIO direction\r\n"
				"(5) Read the DIO mask\r\n"
				"(6) Set the DIO mask\r\n"
				"(7) Walk bit on DIO\r\n"
				"(8) Quit\r\n"
				"Enter option (1-8): ");
		scanf("%d", &nOption);	// Get option number
		printf("\r\n");

		switch(nOption) {
		case 1: // read dio
			printf("Input capable DIO lines are: 0x%x\r\n", dwInput);
			if (!ReadDio(hDioPort, &dwDio))
				bQuit = TRUE;
			else
			printf("Read DIO state: 0x%x.\r\n\r\n", dwDio);
			break;
		case 2:	// write dio
			printf("Output capable DIO lines are: 0x%x\r\n", dwOutput);
			dwDio = GetUserDword("Enter data to write (in hex, i.e. 5a5af0f0): ");
			if (!WriteDio(hDioPort, dwDio))
				bQuit = TRUE;
			else 
			printf("\r\nWrote 0x%x to DIO\r\n\r\n", dwDio);
			break;
		case 3: // get dio direction
			if (!GetDirection(hDioPort, &dwDioDir))
				bQuit = TRUE;
			else 
				wprintf(TEXT("The current direction is 0x%x.\r\n\r\n"), dwDioDir);
			break;
		case 4: // set dio direction
			dwDioDir = GetUserDword("Enter direction to set (in hex, i.e. 5a5af0f0): ");
			if (!SetDirection(hDioPort, dwDioDir))
				bQuit = TRUE;
			else 
				printf("\r\nSet DIO direction 0x%x\r\n\n", dwDioDir);
			break;
		case 5: // get dio driver mask
			if (!GetMask(hDioPort, &dwDioMask))
				bQuit = TRUE;
			else 
				wprintf(TEXT("The current mask is 0x%x.\r\n\r\n"), dwDioMask);
			break;
		case 6: // set dio driver mask
			dwDioMask = GetUserDword("Enter mask to set (in hex, i.e. 5a5af0f0): ");
			if (!SetMask(hDioPort, dwDioMask))
				bQuit = TRUE;
			else 
				printf("\r\nSet DIO mask 0x%x\r\n\n", dwDioMask);
			break;
		case 7: // do a walking bit test
			hBitWalkThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)BitWalkThread, &hDioPort, 0, NULL);
			printf("Walking bit thread started.  All output capable DIO lines should strobe high.\r\n");
			printf("Enter <S> to stop the thread.\r\n");
			do {
				scanf("%s", &szBuf);
			} while (szBuf[0] != 's' && szBuf[0] != 'S');
			TerminateThread(hBitWalkThread, 0);		// End the bit walking thread
			break;
		case 8:
			bQuit = TRUE;	// user wants to quit
			break;
		default:
			printf("%d is an invalid selection.  Try again.\r\n", nOption);
		}
	}
	CloseHandle(hDioPort);				// Close the DIO port
	wprintf(TEXT("Closed %s.\r\n"), wszPortName);
	RETAILMSG(1, (TEXT("Closed %s\r\n"), wszPortName));
	
	return 0;
}

// BitWalkThread(LPVOID p)
// Walks a bit (shifts it) across the full 32 bit DIO register.  All output
// capable lines will strobe high, one by one, each for 100 ms.  Loops until
// the user stops the thread.
void BitWalkThread(LPVOID p) {
	HANDLE hDioPort = *((HANDLE *) p);	// Get port handle from thread params
	DWORD dwData = 1;
	int i;

	if (SetDirection(hDioPort, dwOutput))	// Set all output capable lines to output
	{
		
		for(i= 0; i<100; i++)
		{
			WriteDio(hDioPort, 0x00000000);
			WriteDio(hDioPort, 0xFFFFFFFF);
		}

		while (1) {		// Loop continues until thread is terminated by main
			if (WriteDio(hDioPort, dwData))
			{
				dwData <<= 1;		// shift the set bit left one position
				if (dwData == 0)	// set bit shifted out, start over
					dwData = 1;
				Sleep(100);			// wait 100 ms before shifting again.
			}
			else 
				break;				// Write error, exit loop and print error
		}
	}
	else							// SetDirection or WriteDIO failed
	{	
		printf("\r\nBit walk aborted due to error.\r\n");	
	}
}