/*******************************************************************************
 * Copyright (c) 2005, 2007 Intel Corporation.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Hoang M Nguyen, Intel - Initial API and Implementation
 *    IBM - Portions of old RAC code (brought over by Hoang M Nguyen)
 *
 * $Id: TransportSupportNamedPipe_win.c,v 1.24 2007/12/07 04:04:04 jkubasta Exp $
 *
 *******************************************************************************/ 

#ifdef _WIN32     // Windows-specific

#include "tptp/TransportSupport.h"

#include "tptp/NoLog.h"

#include "RAComm.h"


/**
 *********************************************************
 *
 * @brief
 *    read from the given named pipe
 *
 * @return
 *    The number of bytes successfully read
 *    Negative - Error.
 *********************************************************/
int readFromNamedPipe(HANDLE handle,
						 char *buffer,
						 int offset,
						 int length,
						 int *bytesRead) 
{
	int  rc = 0 ;
	BOOL isSuccessful ;
	OVERLAPPED overlap;

	ZeroMemory(&overlap, sizeof(OVERLAPPED));

	overlap.hEvent=CreateEvent(NULL, TRUE, FALSE, NULL);

	/* RKD:  Reading and writing on the same pipe cannot be done synchronously in our model. */
	isSuccessful = ReadFile(handle, buffer+offset, length-offset, bytesRead, &overlap);

	if(isSuccessful == FALSE) 
	{
		long errorCode = GetLastError();
		switch (errorCode)
		{
			case ERROR_IO_PENDING: 
				/* asynchronous call. This is expected */
				isSuccessful = TRUE ;
				break ;
			case ERROR_BROKEN_PIPE:
				rc = TPTP_PIPE_HAS_ENDED ;
				break ;

			default:
				rc = TPTP_UNABLE_TO_READ_FROM_NAMED_PIPE ;
				//printCurrentSysError() ;
		}
	}

	if (isSuccessful == TRUE)
	{
		WaitForSingleObject(overlap.hEvent, INFINITE);
		GetOverlappedResult(handle, &overlap, bytesRead, FALSE);
		
		rc = *bytesRead ;

	}

	CloseHandle(overlap.hEvent);


	return ( rc ) ;
}


/**
 *********************************************************
 *
 * @brief
 *    write to the given named pipe with given data
 *
 * @return
 *    The number of bytes successfully written
 *    Negative - Error
 *********************************************************/

int writeToNamedPipe(HANDLE handle,
					  char *buffer,
					  int offset,
					  int length,
					  int *bytesWritten) {
	BOOL  isSuccessful ;
	int   rc = 0;

	OVERLAPPED overlap;
	ZeroMemory(&overlap, sizeof(OVERLAPPED));
	overlap.hEvent=CreateEvent(NULL, TRUE, FALSE, NULL);

	/* RKD:  Reading and writing on the same pipe cannot be done synchronously in our model. */
	isSuccessful = WriteFile(handle, buffer+offset, length-offset, bytesWritten, &overlap);

	if(isSuccessful == FALSE) 
	{
		long errorCode = GetLastError();
		switch (errorCode)
		{
			case ERROR_IO_PENDING: 
				/* asynchronous call. This is expected */
				isSuccessful = TRUE ;
				break ;
			case ERROR_BROKEN_PIPE:
				rc = TPTP_PIPE_HAS_ENDED ;
				break ;

			default:
				rc = TPTP_UNABLE_TO_WRITE_TO_NAMED_PIPE ;
		}
	}

	if (isSuccessful == TRUE)
	{
		WaitForSingleObject(overlap.hEvent, INFINITE);
		GetOverlappedResult(handle, &overlap, bytesWritten, FALSE);
		
		rc = *bytesWritten ;

	}

	CloseHandle(overlap.hEvent);

	return ( rc ) ;
}


/**
 *********************************************************
 *
 * @brief
 *    Clean up a given pipe
 *
 * @return
 *    0 - Success
 *    nonzero - Error.
 *********************************************************/
int cleanPipeUp(HANDLE *pPipeHandle) 
{
	if (*pPipeHandle > 0) {
		FlushFileBuffers(*pPipeHandle);
		CloseHandle(*pPipeHandle) ;
		*pPipeHandle = 0 ;
	}
	
	return 0 ;
}


/**
 *********************************************************
 *
 * @brief
 *   Used by the creater of a named pipe to connect to a previously created named
 *   pipe for reading and waits for another application to open the pipe for writing. 
 *
 * @note: This is a blocking call (i.e., the call will not return until someone else
 *        has opened the pipe for writing.)
 *
 * @note: the two parameters are needed for platform-independent support
 *        (handle to be used in Windows and the name of the pipe for Linux)
 *
 * @param *pHandle -  pointer to the handle of the pipe to connect to
 *
 * @param pPipeName - the full name of the pipe to connect to
 *
 * @return
 *    0 - Success
 *    nonzero - Error.
 *********************************************************/
int connectToNamedPipe(HANDLE *pHandle, const char *pPipeName) 
{
	BOOL  isSuccessful = FALSE;

	OVERLAPPED overlap;

	// We don't use pPipeName in the windows version, so don't need to validate it.

	// We don't use pPipeName in the windows version, so don't need to validate it.

	if(*pHandle==INVALID_HANDLE_VALUE) 
	{
		TPTP_LOG_ERROR_MSG1("Error: invalid handle for the named pipe %s.", pPipeName);
	    return 1;
	}


	/* RKD:  As we create the pipe so it is non-blocking we need to wait for the
	     device to be signaled as the connect does not block.  Because the
		 pipe is an object it will be signaled when the request completes.
	*/
	ZeroMemory(&overlap, sizeof(OVERLAPPED));
	overlap.hEvent=CreateEvent(NULL, TRUE, FALSE, NULL);

	isSuccessful = ConnectNamedPipe(*pHandle, &overlap);
	if(isSuccessful == FALSE) 
	{
		long error=GetLastError();

		/* Is the error just because the client beat us to the punch */
		if 	(error == ERROR_PIPE_CONNECTED)
		{
			/* this is normal error and expected, ignore it */
			isSuccessful = TRUE ;
		}
	}

	if (isSuccessful == FALSE)
	{
		WaitForSingleObject(overlap.hEvent, INFINITE); 
		isSuccessful = TRUE ;
	}

	CloseHandle(overlap.hEvent);

	return (isSuccessful?0:1);
}


/**
 *********************************************************
 *
 * @brief
 *    Called by the server pipe to disconnect from a pipe
 *
 * @return
 *    TRUE if successful
 *    FALSE if error
 *********************************************************/
int disconnectFromNamedPipe(HANDLE handle) 
{
	FlushFileBuffers(handle);

	return ( DisconnectNamedPipe(handle) );
}


/**
 *********************************************************
 *
 * @brief
 *    completely remove the named pipe from the system
 *
 * @return
 *    0  - Success
 *    nonzero - Error.
 *********************************************************/
int destroyNamedPipe(const char *pFullPipeName)
{
	/* there is nothing to do on Windows side */

	return 0 ;
}





/**
 *********************************************************
 *
 * @brief
 * Create the named pipe given its name
 *
 * @return
 *    the handle of the created pipe
 *    negative if error
 *********************************************************/
HANDLE createGeneralNamedPipe(const char *pNameSpace, const char *pPipeName, int createOption, int bInheritHandle)
{
	HANDLE result = (HANDLE) -1;
	char pFullName[1024];
	DWORD   openMode = 0, bufferSize, timeOutValue = 0, maxInstanceNum;

	SECURITY_ATTRIBUTES sa;
	ZeroMemory(&sa, sizeof(sa));
	sa.bInheritHandle=bInheritHandle;
	sa.nLength=sizeof(sa);

	if(!pPipeName || !pNameSpace) 
	{
		TPTP_LOG_ERROR_MSG("Create pipe - Null name");
		return (HANDLE) -1;
	}

	/* Build the full pipe name */
	sprintf(pFullName, "%s%s", pNameSpace, pPipeName) ;

	TPTP_LOG_DEBUG_MSG2("Create pipe(%s) option(%d)", pFullName, createOption) ;

	switch(createOption) 
	{
		case TPTP_PIPE_READ_ONLY:   openMode = PIPE_ACCESS_INBOUND ;  break ;
		case TPTP_PIPE_WRITE_ONLY:  openMode = PIPE_ACCESS_OUTBOUND ; break ;
		case TPTP_PIPE_DUPLEX:      openMode = PIPE_ACCESS_DUPLEX ;   break ;
	}

	openMode = openMode | FILE_FLAG_OVERLAPPED | WRITE_DAC ;

	bufferSize = 4096 ;

	timeOutValue = 3000 ;

	maxInstanceNum = 1 ;

	result=CreateNamedPipe(pFullName,		
						   openMode,	
						   PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 
						   maxInstanceNum,						
						   bufferSize,				
						   bufferSize,				
						   timeOutValue,				
						   &sa);				
	if(result==INVALID_HANDLE_VALUE) 
	{
		result = (HANDLE) -1;
	}
    else 
	{
		/* Try to disable the security for the named pipe just created so other prcesses can connect to it */
		disableAllSecurityOfKernalObject(result);
    }
	return result;
}


/**
 *********************************************************
 *
 * @brief
 * Opens a pipe for writing. Note: This is a NON-blocking call. If another process
 * has yet to open a pipe for reading, this call will return immediately with 
 * a negative value. The intention is for the caller of this method to loop until 
 * a non-negative value is returned, which indicates a successful connection to 
 * the pipe. 
 *
 * @return
 *    the handle of the open pipe
 *    negative if error
 *********************************************************/
HANDLE  openGeneralNamedPipe(const char *pNameSpace, const char *pPipeName, int openOption, int bInheritHandle) 
{
	HANDLE result;
	char pFullName[1024];

	DWORD openMode = 0, shareMode = 0 ;

	SECURITY_ATTRIBUTES sa;
	ZeroMemory(&sa, sizeof(sa));
	sa.bInheritHandle=bInheritHandle;
	sa.nLength=sizeof(sa);


	if(pPipeName == NULL) 
	{
		return (HANDLE) -1;
	}

	/* Build the pipe name for the platform */
	sprintf(pFullName, "%s%s", pNameSpace, pPipeName) ;

	TPTP_LOG_DEBUG_MSG2("Open pipe(%s) option(%d)", pFullName, openOption) ;

	switch(openOption) 
	{
		case TPTP_PIPE_READ_ONLY:      openMode = GENERIC_READ ;  break ;
		case TPTP_PIPE_WRITE_ONLY:     openMode = GENERIC_WRITE | WRITE_DAC; break ;
		// On Linux, NC does something, here it doesn't
		case TPTP_PIPE_NC_WRITE_ONLY:  openMode = GENERIC_WRITE | WRITE_DAC; break ;
		case TPTP_PIPE_DUPLEX:         openMode = GENERIC_READ | GENERIC_WRITE ;   break ;
	}

	do
	{
		result=CreateFile(pFullName,			
						  openMode,					
						  shareMode,					
						  &sa,						
						  OPEN_EXISTING,				/* opens existing pipe */
						  FILE_FLAG_OVERLAPPED,			/* asyncronous I/O  */
						  NULL);						/* no template file */

		if(result==INVALID_HANDLE_VALUE) 
		{
			long error=GetLastError();

			if ( error == ERROR_PIPE_BUSY )
			{
				/* Attempt to wait until we are accepted as a client to the pipe server.  A couple of things can happen
				   here.  First, if there is no client it will return immediately.  If there is a client it will either
				   successfuly wait or we will timeout waiting.  If we timeout then try again.
				*/
				if(!WaitNamedPipe(pFullName, 3000)) 
				{
					DWORD error=GetLastError();

					TPTP_LOG_DEBUG_MSG1("Open named pipe. Failed to wait. error(%x)", error) ;

					return (TPTP_HANDLE)-1;
				}

			}
			else
			{
				TPTP_LOG_DEBUG_MSG1("Open named pipe UNsuccessfully. error(%d)", error) ;
				return (TPTP_HANDLE)-1;
			}
		}
		else
		{
			TPTP_LOG_DEBUG_MSG1("Open named pipe successfully. Handle(%x)", result) ;
		}
	}
	while ( result == INVALID_HANDLE_VALUE ); /* ends do..while */

	return result;
}


#endif   // end-of-Windows-specific

