//////////////////////////////////////////////////////////////////////
//
//                             Pixie
//
// Copyright © 1999 - 2003, Okan Arikan
//
// Contact: okan@cs.berkeley.edu
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
// 
// This library 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 library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
//
//  File				:	rndr.cpp
//  Classes				:	-
//  Description			:	rib parser
//
////////////////////////////////////////////////////////////////////////
#include <stdlib.h>

#include "common/global.h"
#include "common/os.h"
#include "common/containers.h"
#include "ri/ri.h"


#ifdef WIN32
#include <process.h>
#else
#endif

#define	BUFFERSIZE		1024
#define DEFAULT_PORT	24666

// Prototype for main
		int		main(int argc, char* argv[]);
static	int		gargc;
static	char	**gargv;
static	int		deamon;
static	int		silent;
static	int		noRestart;



///////////////////////////////////////////////////////////////////////
// Function				:	exitFunction
// Description			:	This function is called before exitting so that if
//							we're running a server, it can spawn another one
// Return Value			:	-
// Comments				:
// Date last edited		:	12/01/2001
void	exitFunction() {
	if (deamon == TRUE) {
		if (noRestart == FALSE) {
			char	**argv	=	new char*[gargc+2];
			int		i;


			for (i=0;i<gargc;i++) {
				argv[i]	=	gargv[i];
			}
			argv[i++]	=	"-silent";
			argv[i]		=	NULL;

#ifdef WIN32
			_execv(argv[0],argv);
#else
			execv(argv[0],argv);
#endif
			delete [] argv;
		}
	}
}

///////////////////////////////////////////////////////////////////////
// Function				:	printUsage
// Description			:	Print the stinking usage
// Return Value			:	-
// Comments				:
// Date last edited		:	12/01/2001
void	printUsage() {
	fprintf(stdout,"***Rib renderer***\n");
	fprintf(stdout,"To render a RIB file:\n");
	fprintf(stdout,"       rndr <rib_file>\n");

	fprintf(stdout,"To start a net rendering deamon:\n");
	fprintf(stdout,"       rndr -d <port> <-silent>\n");
	fprintf(stdout,"       *** WARNING: The deamon will restart itself after each frame\n");

	fprintf(stdout,"To kill a net rendering deamon:\n");
	fprintf(stdout,"       rndr -k <port>\n");

	fprintf(stdout,"To render a RIB file on network:\n");
	fprintf(stdout,"       rndr <rib_file> -s <server1:port1,server2:port2...>\n");

	fprintf(stdout,"Additional command-line options:\n");
	fprintf(stdout,"       -f <frame_range>: Render only a subsequence of frames\n");
	fprintf(stdout,"       -silent         : Silent mode (errors/warning ignored)\n");


	fprintf(stdout,"...\n");
}


///////////////////////////////////////////////////////////////////////
// Function				:	riThread
// Description			:	The main rendering thread
// Return Value			:
// Comments				:
// Date last edited		:	11/28/2001
void	riThread(void *w) {
	T32		*buffer	=	(T32 *) w;
	char	managerString[1024];
	T32		a;

	a.integer	=	0;
	send((SOCKET) buffer[0].integer,(char *) &a,sizeof(T32),0);

	sprintf(managerString,"#%s#client=%d",(char *) &buffer[1].character,buffer[0].integer);

	// I may want to do this in a seperate process
	RiBegin(managerString);
	RiReadArchive((char *) &buffer[1].character,NULL,NULL);
	RiEnd();
}


///////////////////////////////////////////////////////////////////////
// Function				:	rndrd
// Description			:	Run the network deamon
// Return Value			:	-
// Comments				:
// Date last edited		:	12/01/2001
void	rndrd(int port) {
	SOCKET		sock;
	struct		sockaddr_in	me;
	socklen_t	myLength	=	sizeof(me);
	T32			buffer[BUFFERSIZE];
	int			running	=	TRUE;

#ifdef WIN32
	WSADATA wsaData;

	// Init the winsock
	if (WSAStartup(0x202,&wsaData) == SOCKET_ERROR) {
		WSACleanup();
		if (silent == FALSE)	fprintf(stderr,"Winsock init error\n");
	}
#endif

	// Create the socket
	sock = socket(AF_INET, SOCK_STREAM, 0);
	if (sock == INVALID_SOCKET) {
		if (silent == FALSE)	fprintf(stderr,"Socket error\n");
		return;
	}

	// Bind to the port
	me.sin_family		= AF_INET;
	me.sin_addr.s_addr	= htonl(INADDR_ANY);
	me.sin_port			= htons(port);
	if (bind(sock, (struct sockaddr *) &me, sizeof(me)) < 0) {
		if (silent == FALSE)	fprintf(stderr,"Bind error\n");
		return;
	}

	// Display the information
	{
		char	hostName[128];
		hostent *hostinfo;

		if(gethostname(hostName,sizeof(hostName)) == 0) {
			if((hostinfo = gethostbyname(hostName)) != NULL) {
				if (silent == FALSE)	fprintf(stdout,"Active at %s:%d\n",inet_ntoa (*(struct in_addr *)*hostinfo->h_addr_list),ntohs(me.sin_port));
			}
		}
	}

	// We're running a deamon
	deamon	=	TRUE;

	// Listen to incoming connections
	listen(sock,SOMAXCONN);
	while(running == TRUE) {
		SOCKET		peer;
		socklen_t	servLen	=	sizeof(sockaddr_in);
		sockaddr_in	serv;
		
		peer	=	accept(sock,(sockaddr *) &serv,&servLen);

		if (peer != INVALID_SOCKET) {
			recv(peer,(char *) &buffer[1].character,(BUFFERSIZE-1)*sizeof(T32),0);

			if (strcmp((char *) &buffer[1].character," quit") == 0) {
				noRestart	=	TRUE;
				running		=	FALSE;
			} else {
				buffer[0].integer	=	peer;

				int	i	=	closesocket(sock);

				riThread((void *) buffer);

				running	=	FALSE;
			}
		}
	}
}



///////////////////////////////////////////////////////////////////////
// Function				:	main
// Description			:	The god
// Return Value			:	-
// Comments				:
// Date last edited		:	12/01/2001
int main(int argc, char* argv[]) {
	int				i;
	char			managerString[1024];
	char			managerString2[1024];
	int				server		=	FALSE;
	int				client		=	FALSE;
	char			*source		=	NULL;
	int				port		=	0;
	const	char	*frameRange	=	NULL;
	char			*target		=	NULL;

#ifndef USE_MEMORY_MANAGER
		// Enable memory leak detection
#ifndef		__INTEL_COMPILER
#ifdef		WIN32
#ifdef		_DEBUG
	_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
#endif
#endif
#endif
#endif

	// Init the memory manager
	memoryManagerInit();

	// Register the exit stuff
	sprintf(managerString,"");
	gargc	=	argc;
	gargv	=	argv;
	deamon	=	FALSE;
	silent	=	FALSE;
	atexit(exitFunction);

	for (i=1;i<argc;i++) {
		if ((strcmp(argv[i],"-h") == 0) || (strcmp(argv[i],"--help") == 0)) {
			printUsage();
			exit(0);
		} else if (strcmp(argv[i],"-d") == 0) {
			i++;

			if ((i < argc) && (sscanf(argv[i],"%d",&port) == 1)) {
			} else {
				port	=	DEFAULT_PORT;
				i--;
			}
		} else if (strcmp(argv[i],"-silent") == 0) {
			silent		=	TRUE;
		} else if (strcmp(argv[i],"-c") == 0) {
			client		=	TRUE;

			i++;
			if (i >= argc) {
				if (silent == FALSE)	fprintf(stderr,"Was expecting client name\n");
				exit(0);
			}

			sprintf(managerString,argv[i]);
		} else if (strcmp(argv[i],"-t") == 0) {
			i++;
			if (i >= argc) {
				if (silent == FALSE)	fprintf(stderr,"Was expecting a target RIB file\n");
				exit(0);
			}

			target	=	argv[i];
		} else if (strcmp(argv[i],"-s") == 0) {
			server	=	TRUE;

			i++;
			if (i >= argc) {
				if (silent == FALSE)	fprintf(stderr,"Was expecting list of servers\n");
				exit(0);
			}
			sprintf(managerString,argv[i]);
		} else if (strcmp(argv[i],"-f") == 0) {
			i++;
			if (i >= argc) {
				if (silent == FALSE)	fprintf(stderr,"Was expecting the frame range\n");
				exit(0);
			}

			frameRange	=	argv[i];
		} else {
			source	=	argv[i];
		}
	}


	if (port != 0) {
		noRestart	=	FALSE;
		rndrd(port);
		exit(0);
	}

	if (source != NULL) {
		if (client == TRUE) {
			sprintf(managerString2,"#%s#client=%s",source,managerString);

			RiBegin(managerString2);

			if (silent		== TRUE)	RiErrorHandler(RiErrorIgnore);
			if (frameRange	!= NULL)	RiOption(RI_LIMITS,RI_FRAMERANGE,frameRange);
			RiReadArchive(source,NULL,NULL);
			RiEnd();

			exit(0);
		} else {
			sprintf(managerString2,"#%s#servers=%s",source,managerString);

			if (server == TRUE)	RiBegin(managerString2);
			else				RiBegin(target);

			if (silent		== TRUE)	RiErrorHandler(RiErrorIgnore);
			if (frameRange	!= NULL)	RiOption(RI_LIMITS,RI_FRAMERANGE,frameRange);
			RiReadArchive(source,NULL,NULL);
			RiEnd();

			exit(0);
		}
	} else {
		if (silent == FALSE)	fprintf(stderr,"How about giving some rib files as arguments ?\n");
	}

	memoryManagerTini();

	return 0;
}

