#include <../arch/arch.h>

#ifdef	ARCH_IPC_SUPPORTED

/*
 * Server-side functions
 */

V_LOCAL char *kern_prefix = "\\\\.\\mailslot\\";

vboolean V_CALL
archInboundPipe(char *name,arch_pipe_p pipe_ret)
{
	ARCH_CALLER_NAME(archInboundPipe)
	char object_name[_MAX_PATH], full_name[_MAX_PATH];
	pipe_ret->slot = pipe_ret->ev_data = INVALID_HANDLE_VALUE;
	if(strlen(name) >= ((_MAX_PATH >> 1) - strlen(kern_prefix)))
	{
		ARCH_ERROR("name too long");
		return false;
	}
	str2HexCodes(object_name,name);
	strCopy(full_name,kern_prefix,_MAX_PATH);
	strCat2(full_name,object_name,_MAX_PATH);
	pipe_ret->slot = CreateMailslot(full_name,IPC_MSG_LEN,
		MAILSLOT_WAIT_FOREVER,NULL);
	if(pipe_ret->slot == INVALID_HANDLE_VALUE)
	{
		SYSCALL_FAILED_1STR("CreateMailslot",full_name);
		return false;
	}
	pipe_ret->ev_data = CreateEvent(NULL,FALSE,FALSE,object_name);
	if(pipe_ret->ev_data == INVALID_HANDLE_VALUE)
	{
		SYSCALL_FAILED_1STR("CreateEvent",object_name);
		CloseHandle(pipe_ret->slot);
		pipe_ret->slot = INVALID_HANDLE_VALUE;
		return false;
	}
	return true;
}

vboolean V_CALL
archInboundPipeRecv(arch_pipe_p pipe,void_p msg)
{
	ARCH_CALLER_NAME(archInboundPipeRecv)
	DWORD b_read = 0, remainder = IPC_MSG_LEN;
	for(;;)
	{
		if(!ReadFile(pipe->slot,msg,remainder,&b_read,NULL) || !b_read)
		{
			SYSCALL_FAILED("ReadFile");
			break;
		}
		remainder -= b_read;
		if(!remainder)
			return true;
	}
	return false;
}

vboolean V_CALL
archInboundPipeHasData(arch_pipe_p pipe)
{
	ARCH_CALLER_NAME(archInboundPipeHasData)
	DWORD next, count;
	if(GetMailslotInfo(pipe->slot,NULL,&next,&count,NULL))
	{
		if((next != MAILSLOT_NO_MESSAGE && next >= IPC_MSG_LEN) || count)
			return true;
		return false;
	}
	SYSCALL_FAILED("GetMailslotInfo");
	return false;
}

vboolean V_CALL
archInboundPipeWait(arch_pipe_p pipe,vsize_t timeout_ms)
{
	ARCH_CALLER_NAME(archInboundPipeWait)
	switch(WaitForSingleObject(pipe->ev_data,timeout_ms))
	{
		case WAIT_OBJECT_0:
			return true;
		case WAIT_FAILED:
			SYSCALL_FAILED("WaitForSingleObject");
			break;
		default:
			break;
	}
	return false;
}

/*
 * Client-side functions
 */

vboolean V_CALL
archOutboundPipe(char *name,arch_pipe_p pipe_ret)
{
	ARCH_CALLER_NAME(archOpenOutboundPipe)
	char object_name[_MAX_PATH], full_name[_MAX_PATH];
	pipe_ret->slot = pipe_ret->ev_data = INVALID_HANDLE_VALUE;
	if(strlen(name) >= ((_MAX_PATH >> 1) - strlen(kern_prefix)))
	{
		ARCH_ERROR("name too long");
		return false;
	}
	str2HexCodes(object_name,name);
	strCopy(full_name,kern_prefix,_MAX_PATH);
	strCat2(full_name,object_name,_MAX_PATH);
	pipe_ret->slot = CreateFile(full_name,
		GENERIC_WRITE,FILE_SHARE_READ,NULL,OPEN_EXISTING,0,NULL);
	if(pipe_ret->slot == INVALID_HANDLE_VALUE)
	{
		SYSCALL_FAILED_1STR("CreateFile",full_name);
		return false;
	}
	pipe_ret->ev_data = OpenEvent(EVENT_MODIFY_STATE,FALSE,object_name);
	if(pipe_ret->ev_data == INVALID_HANDLE_VALUE)
	{
		SYSCALL_FAILED_1STR("OpenEvent",object_name);
		CloseHandle(pipe_ret->slot);
		pipe_ret->slot = INVALID_HANDLE_VALUE;
		return false;
	}
	return true;
}

vboolean V_CALL
archOutboundPipeSend(arch_pipe_p pipe,void_p msg)
{
	ARCH_CALLER_NAME(archOutboundPipeSend)
	DWORD wrote = 0, remainder = IPC_MSG_LEN;
	for(;;)
	{
		if(!WriteFile(pipe->slot,msg,remainder,&wrote,NULL) || !wrote)
		{
			SYSCALL_FAILED("WriteFile");
			break;
		}
		remainder -= wrote;
		if(!remainder)
		{
			SetEvent(pipe->ev_data);
			return true;
		}
	}
	return false;
}

void V_CALL
archPipeClose(arch_pipe_p pipe)
{
	ARCH_CALLER_NAME(archPipeClose)
	if(!CloseHandle(pipe->slot))
		SYSCALL_FAILED_1STR("CloseHandle","pipe->slot");
	if(!CloseHandle(pipe->ev_data))
		SYSCALL_FAILED_1STR("CloseHandle","pipe->ev_data");
	pipe->slot = pipe->ev_data = INVALID_HANDLE_VALUE;
	return;
}

#endif	/* ARCH_IPC_SUPPORTED */
