#define WIN32_LEAN_AND_MEAN
#include <windows.h>

#if defined(__cplusplus)
#include <stdlib.h>
#include <math.h>
extern "C" {
#endif

#include <EXTERN.h>
#include "perl.h"
#include "XSub.h"

#if defined(__cplusplus)
}
#endif

#include "../ppport.h"

#include "CPipe.hpp"
#include "pipe.h"

CPipe::CPipe(char *szName, DWORD dWait){
	int	iTemp;
	int	iFlag = 1;
	WCHAR	wbuffer[MAX_PATH+1];
	dTHX;
	
	hPipe = 0;                 
	dBufferSize = BUFFER_SIZE;
	dBytes = 0;
			              	
	char szPipeName[PIPE_NAME_SIZE + 1];				
	dwOpenMode = PIPE_ACCESS_DUPLEX;		
	dwPipeMode = 	PIPE_TYPE_BYTE	 |      
					PIPE_READMODE_BYTE	 |  
					PIPE_WAIT;				
	nMaxInstances =	PIPE_UNLIMITED_INSTANCES;
	nOutBufferSize = dBufferSize;			
	nInBufferSize  = dBufferSize;
	nDefaultTimeOut = PIPE_TIMEOUT;			
	lpSecurityAttributes= NULL; 
	iError = 0;
	strcpy((char *)szError, "");
	
	cBuffer = new char [dBufferSize];
	if (! cBuffer){
		dBufferSize = 0;
	}

	memset((void *)szError, 0, ERROR_TEXT_SIZE);
	memset((void *)szPipeName, 0, PIPE_NAME_SIZE + 1);
	if (strncmp((char *)szName, "\\\\", 2) == 0){
		iPipeType = CLIENT;
		iTemp = 0;
	}else{
		iPipeType = SERVER;
		strcpy(szPipeName, PIPE_NAME_PREFIX);
		iTemp = strlen(PIPE_NAME_PREFIX);
	}
	strncat(szPipeName, szName, PIPE_NAME_SIZE - iTemp);
	if (USING_WIDE()) {
	    A2WHELPER(szPipeName, wbuffer, sizeof(wbuffer));
	}
	if(iPipeType == SERVER){
	    if (USING_WIDE()) {
		hPipe = CreateNamedPipeW(wbuffer,
					dwOpenMode, 
					dwPipeMode, 
					nMaxInstances, 
					nOutBufferSize, 
					nInBufferSize, 
					nDefaultTimeOut,
					lpSecurityAttributes);
	    }
	    else {
		hPipe = CreateNamedPipeA(szPipeName,
					dwOpenMode, 
					dwPipeMode, 
					nMaxInstances, 
					nOutBufferSize, 
					nInBufferSize, 
					nDefaultTimeOut,
					lpSecurityAttributes);
	    }
	}else{
		while(iFlag){
		    if (USING_WIDE()) {
			hPipe = CreateFileW(wbuffer,
					    GENERIC_READ | GENERIC_WRITE, 
					    FILE_SHARE_READ	| FILE_SHARE_WRITE,
					    NULL,
					    OPEN_EXISTING,
					    FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH,
					    NULL);
		    }
		    else {
			hPipe = CreateFileA(szPipeName,
					    GENERIC_READ | GENERIC_WRITE, 
					    FILE_SHARE_READ	| FILE_SHARE_WRITE,
					    NULL,
					    OPEN_EXISTING,
					    FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH,
					    NULL);
		    }

		    if (GetLastError() == ERROR_PIPE_BUSY){
			if (USING_WIDE())
			    iFlag = WaitNamedPipeW(wbuffer, dWait);
			else
			    iFlag = WaitNamedPipeA(szPipeName, dWait);
		    }else{
			    iFlag = 0;
		    }
		}
	}
	if (cBuffer == 0){
		iError = 998;
		strcpy((char *)szError, "Could not allocate a buffer for the pipe connection");
	}
	if (hPipe == INVALID_HANDLE_VALUE){
		iError = 999;
		strcpy((char *)szError, "Could not connect");
		delete this;
	}
}									  

CPipe::~CPipe(){
	Disconnect(TRUE);
	if (hPipe){
		CloseHandle(hPipe);
		hPipe = 0;
	}
	if(cBuffer){
		delete [] cBuffer;
	}
}

DWORD CPipe::BufferSize(){
	return dBufferSize;
}

DWORD CPipe::ResizeBuffer(DWORD dNewSize){
	CHAR	*szNewBuffer;

	if (dNewSize > 0){
		if (szNewBuffer = new char [dNewSize]){
			memset((void *)szNewBuffer, 0, dNewSize);
			delete [] cBuffer;
			dBufferSize = dNewSize;
			cBuffer = szNewBuffer;
		}
	}
	return dBufferSize;
}

char *CPipe::Read(DWORD *dLen){                   
	BOOL bResult;
	DWORD	cbBytes = 0, cbReply = 0;

	bResult = ReadFile(hPipe, cBuffer, dBufferSize, dLen, NULL); 
	dBytes = *dLen;
	return (bResult)? cBuffer:0;
}

int	CPipe::Write(void *vBuffer, DWORD dSize){
	BOOL bResult;
	DWORD	cbBytes = 0, cbReply = 0;

	bResult = WriteFile(hPipe, vBuffer, dSize, &cbBytes, NULL);
	return bResult;
}

int CPipe::Connect(){
	BOOL bResult;

	bResult = ConnectNamedPipe(hPipe, NULL);

		//	Just in case the pipe is already connected return TRUE even though
		//	ConnectNamedPipe() returned FALSE!
	if (!bResult && GetLastError() == ERROR_PIPE_CONNECTED){
		bResult = 1;
	}
		
	return bResult;
}

int CPipe::Disconnect(int iPurge){
	BOOL	bResult = 0;

	if (iPurge){
		FlushFileBuffers(hPipe);
	}
	if (iPipeType == SERVER){
		bResult = DisconnectNamedPipe(hPipe);
	}
	if (iPipeType == CLIENT){
		bResult = CloseHandle(hPipe);
		hPipe = 0;
	}
	
	return bResult;
}	


int	CPipe::Error(int iErrorNum, char *szErrorText){
	strncpy((char *)szError, szErrorText, ERROR_TEXT_SIZE);
	szError[ERROR_TEXT_SIZE] = '\0';
	iError = iErrorNum;
	return iError;
}

int	CPipe::EndOfFile(){
	return (dBytes)? 1:0;
}