/*
	Implement Local Client/Server Communications between BABEL modules.
	For C++ language.
	(C) Juan Antonio Fernandez Madrigal, 2002
*/

#include "localcomm.h"
#include "windows.h"
#include "memory.h"



//#include "stdio.h"

/* **************************************************************************************
*
*				Message
*
*****************************************************************************************/

/* ----------- private methods ---------- */

void LCSC_Message::changesizeandtype(const Header *h)
{Header *hea;

	if (h!=0)
	{
		dat=new unsigned char [h->len+sizeof(Header)];
		((Header *)dat)->len=h->len;
		((Header *)dat)->t=h->t;
	}
	else
	{
		dat=new Header;
		hea=(Header *)dat;
		hea->len=0;
		hea->t=0;
	}
}


/* ----------- constructors ---------- */

LCSC_Message::LCSC_Message(const Header *h)
{
	changesizeandtype(h);
}

LCSC_Message::~LCSC_Message()
{
	free(dat);
}


/* ----------- methods ----------- */

unsigned LCSC_Message::Size(bool withheader) const
{Header *h=(Header *)dat;

	if (withheader) return(h->len+sizeof(Header));
	return(h->len);
}

void LCSC_Message::ChangeSize(unsigned news)
{Header *h=(Header *)dat;
Header hea;

	if (h->len==news) return;
	hea.t=h->t;
	hea.len=news;
	free(dat);
	changesizeandtype(&hea);
}

unsigned LCSC_Message::Type(void) const
{Header *h=(Header *)dat;

	return(h->t);
}

void LCSC_Message::ChangeType(unsigned ty)
{Header *h=(Header *)dat;

	h->t=ty;
}

bool LCSC_Message::SetContents(void *data)
{Header *h=(Header *)dat;

	if (h->len>0) memcpy((unsigned char *)dat+sizeof(Header),data,h->len);
	return(true);
}

void *LCSC_Message::Contents(void)
{
	return((unsigned char *)dat+sizeof(Header));
}

void LCSC_Message::CopyContents(void *dest)
{
	memcpy(dest,Contents(),Size());
}

void *LCSC_Message::CompleteBody(void)
{
	return(dat);
}


/* **************************************************************************************
*
*				Communication Channels
*
*****************************************************************************************/

typedef struct {
				bool creator;
				bool activated;
				std::string name;
				HANDLE h;
				} InternalData;	// Internal private data of the class


/* ---------- private methods ---------- */

bool LCSC_Channel::ReceiveData_Blocking(LCSC_Message &msg)
/* Receive data according to MSG.Size(). Return FALSE if there is any error. If there is
no message, wait for one if there is a sender already connected (activated) to the other
end of the channel: if there is no sender, this method ends with FALSE */
{InternalData *id=(InternalData *)internals;
unsigned long nr;
unsigned l;

	if (!id->activated) return(false);
	l=msg.Size(true);
	if (msg.Size()==0) return(false);
	if (ReadFile(id->h,msg.CompleteBody(),l,&nr,NULL)==0) return(false);
//	printf("Receivedata-> %d received\r\n",nr);
	return(nr==l);
}


/* ---------- constructors ---------- */

LCSC_Channel::LCSC_Channel()
{InternalData *id;

	id=new InternalData;
	id->activated=false;
	internals=id;
}

LCSC_Channel::~LCSC_Channel()
{InternalData *id=(InternalData *)internals;

	if (id->activated) Deactivate();
	free(id);
}


/* ---------- methods ------------ */

bool LCSC_Channel::Activate(const std::string &name, bool creat)
{InternalData *id=(InternalData *)internals;

	if (id->activated) return(false);
	id->name="\\\\.\\pipe\\"+name;
	id->creator=creat;
	if (id->creator)
	{
		id->h=CreateNamedPipe(id->name.c_str(),PIPE_ACCESS_DUPLEX | FILE_FLAG_WRITE_THROUGH,
							  PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
							  PIPE_UNLIMITED_INSTANCES,
							  1024, 1024, 5000, NULL);
	}
	else
	{
		id->h=CreateFile(id->name.c_str(), GENERIC_READ | GENERIC_WRITE,
						 FILE_SHARE_READ | FILE_SHARE_WRITE,
						 NULL, OPEN_EXISTING,
						 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH,
						 NULL);
	}
	if (id->h==INVALID_HANDLE_VALUE) return(false);
	id->activated=true;
	return(true);
}

bool LCSC_Channel::Deactivate(void)
{InternalData *id=(InternalData *)internals;

	if (id->activated)
	{
		if (id->creator)
		{
			if (DisconnectNamedPipe(id->h)==FALSE) return(false);
		}
		if (CloseHandle(id->h)==FALSE) return(false);
		id->activated=false;
	}
	return(true);
}

bool LCSC_Channel::IsActivated(void) const
{InternalData *id=(InternalData *)internals;

	return(id->activated);
}

bool LCSC_Channel::SendMessage_NonBlocking(LCSC_Message &msg)
{InternalData *id=(InternalData *)internals;
unsigned long nw;

	if (!id->activated) return(false);
	if (msg.Size()==0) return(false);
	if (WriteFile(id->h,msg.CompleteBody(),msg.Size(true),&nw,NULL)==FALSE) return(false);
//	printf("Senddata-> %d sent\r\n",nw);
	return(true);
}

bool LCSC_Channel::ReceiveMessage_Blocking(const LCSC_Message::Header &h, LCSC_Message &msg)
{
	msg.ChangeType(h.t);
	msg.ChangeSize(h.len);
	return(ReceiveData_Blocking(msg));
}

bool LCSC_Channel::ReceiveMessage_Blocking(LCSC_Message &msg, unsigned sampleratio, unsigned timeout, bool *outoftime)
{LCSC_Message::Header hea;
unsigned to=timeout;
bool endtime=false;

	if (outoftime!=0) *outoftime=false;
	while (	(!endtime)&&
			(!IsThereMessageToReceive_NonBlocking(hea)) )
	{
		if (sampleratio==0) return(false);
		if (GetLastError()==109) return(false); // the pipe has been ended
		Sleep(sampleratio);
		if (timeout>0)
		{
			to--;
			endtime=(to==0);
		}
	}
	if (endtime)
	{
		if (outoftime!=0) *outoftime=true;
		return(false);
	}
	msg.ChangeType(hea.t);
	msg.ChangeSize(hea.len);
	return(ReceiveData_Blocking(msg));
}

bool LCSC_Channel::IsThereMessageToReceive_NonBlocking(LCSC_Message::Header &hea)
{InternalData *id=(InternalData *)internals;
unsigned long nr,na,nu;

	if (!id->activated) return(false);
	if (PeekNamedPipe(id->h,&hea,sizeof(LCSC_Message::Header),&nr,&na,&nu)==0)
	{
//		printf("ERROR:%d\r\n",GetLastError());
		return(false);
	}
//	printf("Peekdata-> %d received, %d available, %d unread\r\n",nr,na,nu);
	return(nr==sizeof(LCSC_Message::Header));
}

bool LCSC_Channel::SendReceiveMessage_Blocking(LCSC_Message &msg2send, LCSC_Message &msg2receive,
											   unsigned sampleratio, unsigned timeout, bool *outoftime)
{
	return(	(SendMessage_NonBlocking(msg2send)) &&
			(ReceiveMessage_Blocking(msg2receive,sampleratio,timeout,outoftime)));
}

