//
// Purpose:
//     This sample demonstrates how to develop an advanced named
//     pipe server that is capable of servicing 5 named pipe
//     instances. The application is an echo server where data is
//     received from a client and echoed back to the client. All
//     the pipe instances are serviced in the main application
//     thread using Win32 overlapped I/O.
//
// Command line options: None
//
#include <windows.h>
#include <stdio.h>

#define NUM_PIPES 5
#define BUFFER_SIZE 256

void main(void)
{
	HANDLE PipeHandles[NUM_PIPES];
	DWORD BytesTransferred;
	CHAR Buffer[NUM_PIPES][BUFFER_SIZE];
	INT i;
	OVERLAPPED Ovlap[NUM_PIPES];
	HANDLE Event[NUM_PIPES];
	
	// For each pipe handle instance, the code must maintain the
	// pipes' current state, which determines if a ReadFile or
	// WriteFile is posted on the named pipe. This is done using
	// the DataRead variable array. By knowing each pipe's
	// current state, the code can determine what the next I/O
	// operation should be.
	BOOL DataRead[NUM_PIPES];
	DWORD Ret;
	DWORD Pipe;
	
	for(i = 0; i < NUM_PIPES; i++)
	{
		// Create a named pipe instance
		if ((PipeHandles[i] = CreateNamedPipe(L"\\\\.\\Pipe\\jim",
			PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
			PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, NUM_PIPES,
			0, 0, 1000, NULL)) == INVALID_HANDLE_VALUE)
		{
			printf("CreateNamedPipe() for pipe %d failed with error %d\n", i, GetLastError());
			return;
		}
		else
			printf("CreateNamedPipe() for pipe %d is OK!\n", i);
		
		// Create an event handle for each pipe instance. This
		// will be used to monitor overlapped I/O activity on each pipe
		if ((Event[i] = CreateEvent(NULL, TRUE, FALSE, NULL)) == NULL)
		{
			printf("CreateEvent() for pipe %d failed with error %d\n",  i, GetLastError());
			continue;
		}
		else
			printf("CreateEvent() for pipe %d is OK!\n", i);
		
		// Maintain a state flag for each pipe to determine when data
		// is to be read from or written to the pipe
		DataRead[i] = FALSE;
		ZeroMemory(&Ovlap[i], sizeof(OVERLAPPED));
		Ovlap[i].hEvent = Event[i];
		
		// Listen for client connections using ConnectNamedPipe()
		if (ConnectNamedPipe(PipeHandles[i], &Ovlap[i]) == 0)
		{
			if (GetLastError() != ERROR_IO_PENDING)
			{
				printf("ConnectNamedPipe() for pipe %d failed with error %d\n", i, GetLastError());
				printf("Closing handle...\n");
				CloseHandle(PipeHandles[i]);
				return;
			}
		}
		else
			printf("ConnectNamedPipe() for pipe %d is OK!\n", i);
	}
	
	printf("Server is now running....\n");
	
	// Read and echo data back to Named Pipe clients forever
	while(1)
	{
		if ((Ret = WaitForMultipleObjects(NUM_PIPES, Event, FALSE, INFINITE)) == WAIT_FAILED)
		{
			printf("WaitForMultipleObjects() failed with error %d\n", GetLastError());
			return;
		}
		else
			printf("WaitForMultipleObjects() is OK!\n");
		
		Pipe = Ret - WAIT_OBJECT_0;
		ResetEvent(Event[Pipe]);
		
		// Check overlapped results, and if they fail, reestablish
		// communication for a new client; otherwise, process read
		// and write operations with the client
		if (GetOverlappedResult(PipeHandles[Pipe], &Ovlap[Pipe], &BytesTransferred, TRUE) == 0)
		{
			printf("GetOverlapped() result failed %d start over...\n", GetLastError());
			if (DisconnectNamedPipe(PipeHandles[Pipe]) == 0)
			{
				printf("DisconnectNamedPipe() failed with error %d\n", GetLastError());
				return;
			}
			else
				printf("DisconnectNamedPipe() is OK!\n");
			
			if (ConnectNamedPipe(PipeHandles[Pipe],  &Ovlap[Pipe]) == 0)
			{
				if (GetLastError() != ERROR_IO_PENDING)
				{
					// Severe error on pipe. Close this handle forever
					printf("ConnectNamedPipe() for pipe %d failed with error %d\n", i, GetLastError());
					printf("Closing handle...\n");
					CloseHandle(PipeHandles[Pipe]);
				}
				else
					printf("ConnectNamedPipe() for pipe %d is OK!\n", i);
			}
			
			DataRead[Pipe] = FALSE;
		}
		else
		{
			// Check the state of the pipe. If DataRead equals
			// FALSE, post a read on the pipe for incoming data.
			// If DataRead equals TRUE, then prepare to echo data
			// back to the client.
			if (DataRead[Pipe] == FALSE)
			{
				// Prepare to read data from a client by posting a
				// ReadFile operation
				ZeroMemory(&Ovlap[Pipe], sizeof(OVERLAPPED));
				Ovlap[Pipe].hEvent = Event[Pipe];
				if (ReadFile(PipeHandles[Pipe], Buffer[Pipe],  BUFFER_SIZE, NULL, &Ovlap[Pipe]) == 0)
				{
					if (GetLastError() != ERROR_IO_PENDING)
					{
						printf("ReadFile() failed with error %d\n",  GetLastError());
					}
				}
				else
					printf("ReadFile() should be fine!\n");

				DataRead[Pipe] = TRUE;
			}
			else
			{
				// Write received data back to the client by
				// posting a WriteFile operation.
				printf("Received %d bytes, echo bytes back\n",   BytesTransferred);
				
				ZeroMemory(&Ovlap[Pipe], sizeof(OVERLAPPED));
				Ovlap[Pipe].hEvent = Event[Pipe];
				
				if (WriteFile(PipeHandles[Pipe], Buffer[Pipe],  BytesTransferred, NULL, &Ovlap[Pipe]) == 0)
				{
					if (GetLastError() != ERROR_IO_PENDING)
					{
						printf("WriteFile() failed with error %d\n", GetLastError());
					}
				}
				else
					printf("WriteFile() should be OK!\n");

				DataRead[Pipe] = FALSE;
			}
		}
	}
}
