/*
 * serverhandle.c
 *
 * Copyright (C) Leni Mayo 2001-2002
 *
 *	This program is free software; you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License, version 2, as
 *	published by the Free Software Foundation.
 *	
 *	This program is distributed in the hope that it will be useful,
 *	but WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *	GNU General Public License for more details.
 *	
 *	You should have received a copy of the GNU General Public License
 *	along with this program; if not, write to the Free Software
 *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <stdlib.h> /* for malloc/free */
#include <unistd.h>
#include <sys/stat.h>
#include <sys/time.h> /* for gettimeofday() stuff */
#include <fcntl.h>
#include <string.h> /* for strerror() */
#include <errno.h>
#include "log.h"
#include "servercallbacks.h"
#include "serverhandle.h"

/*
 * serverHandleInitialise()
 *
 * Returns
 * TRUE upon success, false otherwise
 *
 * Comments
 * From man 4 fifo:
 *   Under  Linux,  opening a FIFO for read and write will succeed
 *   both in blocking and non-blocking mode.  POSIX leaves this
 *   behaviour undefined. This can be used to open a FIFO for
 *   writing while there are no readers available.
 *
 * This means that we're faced with a tradeoff b/n portability and
 * ease of idiom.  Opening the FIFO RDONLY is good for portability,
 * but in the absence of writers, read doesn't block, it returns
 * an end-of-file condition, which means we'd need some
 * other mechanism to block on an empty pipe (eg. select).
 * For now, take the easy option of opening the FIFO RDWR, and perhaps
 * return to this in future.
 *
 * leni - Thu Nov 22 01:57:24 EST 2001
 */
int serverHandleInitialise(ServerHandle *pHandle)
{
	int fd;
	int ret;
	char fifoName[PATH_MAX];

	getClientServerPipePath(fifoName, sizeof(fifoName));

	assert(pHandle != NULL);

	fd = open(fifoName, O_RDWR, 0);

	if (fd < 0)
	{
		ret = mkfifo(fifoName, S_IRUSR | S_IWUSR);

		if (ret != 0)
		{
			fd = -1;
		}
		else
		{
    		fd = open(fifoName, O_RDWR, 0);
		}
	}

	if (fd < 0)
	{
		fprintf(stderr, "Unable to create/open fifo: \"%s\" - %s\n",
		                fifoName, strerror(errno));
	}

	pHandle->fContinueEventLoop = TRUE;
	pHandle->fdFifo = fd;

	return pHandle->fdFifo >= 0;
}

void serverHandleEventLoop(ServerHandle *pHandle)
{
	char  buffer[PIPE_BUF];
	ssize_t cBytes;
	MessageHeader msgHeader;
	Command *pCommand;
	Message msg;
	struct timeval tv;

	assert(pHandle != NULL);

	assert(pHandle->fdFifo >= 0);

	while (pHandle->fContinueEventLoop)
	{
		int r = gettimeofday(&tv, NULL);
		printLog(eLogLevelDeveloper, "Top of event loop, time is %ld s, %ld us\n", tv.tv_sec, tv.tv_usec);

		callbackPre();

		cBytes = read(pHandle->fdFifo, buffer, sizeof(MessageHeader));

		/* leni TODO: need slightly more robust error handling here.
		*/
		assert(cBytes == sizeof(MessageHeader));

		memcpy(&msgHeader, buffer, sizeof(MessageHeader));

		/* printf("Begin by reading %d bytes, telling us that size of data to follow = %d\n", cBytes, msgHeader.cBytes);
		*/

		if (msgHeader.cBytes > 0)
		{
			cBytes += read(pHandle->fdFifo, &buffer[sizeof(MessageHeader)], msgHeader.cBytes);
		}

		/* printf("%s reads %d bytes from fifo, useful byte count is: %d\n", __FILE__, cBytes, *((int *) &buffer[0]));
		*/
		
		assert(cBytes <= sizeof(Message));

		memcpy(&msg, buffer, cBytes);

		pCommand = messagePopNew(&msg);

		/* leni TODO: error checking for pCommand */

		callbackCommand(pCommand);

		free(pCommand);

		/* We used to allow more than one Command in a Message, not any more
		*/
		assert(msg.header.cBytes == 0);

#ifdef TAKENOUT
		while (pCommand != NULL)
		{
			printf("%s reads: %s\n", __FILE__, commandAsString(pCommand));
			free(pCommand);
			pCommand = messagePopNew(&msg);
		}
#endif
	}
}

void serverHandleCleanup(ServerHandle *pHandle)
{
	assert(pHandle);

	if (pHandle->fdFifo >= 0)
	{
		close(pHandle->fdFifo);
	}
}
