#include <u.h>
#include <libc.h>

/* iosrv [-p] startnumber
 *starts an input/output handler for multiplexing i/o via pipefiles
 */

int fdrczero;
int fdrcone;
int fdrctwo;
int fdctl;
int fdclientin;
int fdclientout[128];
int fderrout[128];

char history[512000];
char logtext[4096000];
char ctlmessage[2048];
char clientrequest[256000];
char rcreply[512000];
char rcreplycopy[512000];
char rcerr[64000];
char rcerrcopy[64000];
char prompt[16];

int activeclients;
int deadclients[128];
int requestflag;
int replyflag;
int errflag;
int promptflag;
int sendhist;
int go;

void openfiles(void);
void checkctl(void);
void getrequest(void);
void geterr(void);
void sendcommand(void);
void getreply(void);
void replyserver(void);
void errserver(void);
void newclient(int newfd);

/* connects to pre-existing pipe files; only called at start up */
void
openfiles(void){
	fdrczero = open("fdrczero/data", OWRITE);
	if(fdrczero < 0)
		sysfatal("cant open needed pipefiles\n");
	fdrcone = open("fdrcone/data1", OREAD);
	if(fdrcone < 0)
		sysfatal("cant open needed pipefiles\n");
	fdrctwo = open("fdrctwo/data1", OREAD);
	if(fdrctwo < 0)
		sysfatal("cant open needed pipefiles\n");
	fdctl = open("fdctl/data1", OREAD);
	if(fdctl < 0)
		sysfatal("cant open needed pipefiles\n");
	fdclientin = open("fdclientin/data1", OREAD);
	if(fdclientin < 0)
		sysfatal("cant open needed pipefiles\n");
	for (int i = 0; i < activeclients; i++) {
		char filename[32];
		sprint(filename, "out%d/data", i);
		fdclientout[i] = open(filename, OWRITE);
	}
	for (int i = 0; i < activeclients; i++) {
		char filename[32];
		sprint(filename, "err%d/data", i);
		fderrout[i] = open(filename, OWRITE);
	}
	return;
}

/* connects a new client to previously unused but pre-existing pipe(3) files */
void
newclient(int newfd){
	char filename[32];
	char fileerrname[32];
	sprint(filename, "out%d/data", newfd);
	fdclientout[newfd] = open(filename, OWRITE);
	sprint(fileerrname, "err%d/data", newfd);
	fderrout[newfd] = open(fileerrname, OWRITE);
	write(fdclientout[newfd], logtext, sizeof(logtext));
	if(promptflag == 1){
		write(fdclientout[newfd], prompt, 7);
	}
	activeclients++;
	return;
}

/* everything from here until main is a forked server process that never returns */

/* control process accepts various messages */
void
checkctl(void){
	int target;
	while(go > 0){
		read(fdctl, ctlmessage, sizeof(ctlmessage));
		if ((strcmp(ctlmessage, "quit\n")) == 0) {
			print("exiting\n");
			strcat(rcreply, "*** IO SERVER TERMINATING ***\n");
			replyflag =1;
			errflag = 1;
			requestflag =1;
			go = -1;
		}
		if ((strcmp(ctlmessage, "stop\n")) == 0) {
			print("pausing message passing\n");
			go = 2;
		}
		if ((strcmp(ctlmessage, "go\n")) == 0) {
			print("resuming message passing\n");
			go = 3;
		}
		if ((strcmp(ctlmessage, "new\n")) == 0) {
			print("new\n");
			newclient(activeclients);
			print("new added\n");
		}
		if ((strncmp(ctlmessage, "kill", 4)) == 0) {
			target = atoi(ctlmessage+5);
			deadclients[target] = 1;
			print("marked client %d as dead\n", target);
		}
		if ((strcmp(ctlmessage, "prompt\n")) == 0) {
			promptflag = 1;
			print("fake prompt on\n");
		}
		if ((strcmp(ctlmessage, "promptoff\n")) == 0) {
			promptflag = 0;
			print("fake prompt off\n");
		}
		if ((strcmp(ctlmessage, "help\n")) == 0) {
			print("commands:\n");
			print("quit, stop, go, new kill #, prompt, promptoff\n");
		}
		memset(ctlmessage, 0, sizeof(ctlmessage));
		sleep(1000);
	}
	if (go == -1){
		print("checkctl exiting\n");
		exits(nil);
	}
	exits(nil);
}

/* takes command requests from the user input clients */
void
getrequest(void){
	while(go == 1){
		while(requestflag == 1) {
			sleep(100);
		}
		read(fdclientin, clientrequest, sizeof(clientrequest));
		requestflag = 1;
		print("requestflag up\n");
		sleep(100);
	}
	if(go == -1){
		print("getrequest exiting\n");
		exits(nil);
	}
	exits(nil);
}

/* passes received commands to the listening process pipe, usually rc */
void
sendcommand(void){
	while (go == 1){
		while (requestflag == 0) {
			sleep(100);
		}
		if((strcmp(clientrequest, "hist\n") == 0)) {
			sendhist = 1;
			replyflag = 1;
			print("client requested history, reply flag up\n");	
		}
		if((strcmp(clientrequest, "\n") == 0) && (promptflag == 1)) {
			errflag =1;
		}
		write(fdrczero, clientrequest, strlen(clientrequest));
		strcat(history, clientrequest);
		strcat(logtext, clientrequest);
		memset(clientrequest, 0, sizeof(clientrequest));
		print("sendcommand complete setting requestflag 0\n");
		requestflag = 0;
	}
	if(go == -1){
		print("sendcommand exiting\n");
		exits(nil);
	}
	exits(nil);
}

/* listens for replies from the application process pipe */
void
getreply(void){
	while(go == 1){
		while( replyflag == 1){
			sleep(100);
		}
		read(fdrcone, rcreply, sizeof(rcreply));
		if((strlen(rcreply) > 0)) {
			print("read reply flag up\n");
			replyflag = 1;
		}
		sleep(100);
	}
	if(go == -1){
		print("getreply exiting\n");
		exits(nil);
	}
	exits(nil);
}

/* listens on standard err for application output */
void
geterr(void){
	while(go == 1){
		while( errflag == 1){
			sleep(100);
		}
		read(fdrctwo, rcerr, sizeof(rcerr));
		if((strlen(rcerr) > 0)) {
			print("read stderror reply flag up\n");
			errflag = 1;
		}
		sleep(100);
	}
	if(go == -1){
		print("geterr exiting\n");
		exits(nil);
	}
	exits(nil);
}

/* sends application replies to the client output pipes */
void
replyserver(void){
	while(go == 1){
		while (replyflag == 0){
			sleep(100);
		}
		memcpy(rcreplycopy, rcreply, 512000);
		if (sendhist == 1) {
			memcpy(rcreplycopy, history, sizeof(history));
			sendhist = 0;
			print("copied history to reply\n");
		}
		for (int i =0; i < activeclients; i++) {
			if (deadclients[i] == 1) {
				print("skipping dead client %d\n", i);
				goto skipped;
			}
			if(rfork(RFPROC|RFMEM) > 0){
				deadclients[i] = 1;
				write(fdclientout[i], rcreplycopy, strlen(rcreplycopy));
				print("sent reply to client %d\n", i);
				deadclients[i] = 0;
				exits(nil);
			}
skipped:
		print("-next client-");
		}
		strcat(logtext, rcreply);
		memset(rcreply, 0, sizeof(rcreply));
		replyflag = 0;
		print("replies finished, flag down\n");
		if(promptflag == 1){
			errflag = 1;	/*print prompt after output */
			print("promptflag up, printing prompt\n");
		}
	}
	if(go == -1){
		print("replyserver exiting\n");
		exits(nil);
	}
	exits(nil);
}

/* sends application standard err to the client standard error pipes */
void
errserver(void){
	while(go == 1){
		while (errflag == 0){
			sleep(100);
		}
		memcpy(rcerrcopy, rcerr, 64000);
		for (int i =0; i < activeclients; i++) {
			if (deadclients[i] == 1) {
				print("skipping dead client %d\n", i);
				goto skipped;
			}
			if(rfork(RFPROC|RFMEM) > 0){
				deadclients[i] = 1;
				if (strlen(rcerrcopy) > 0) {
					write(fderrout[i], rcerrcopy, strlen(rcerrcopy));
				}
				if (promptflag == 1) {
					write(fderrout[i], prompt, 7);
				}
				print("sent stderr reply to client %d\n", i);
				deadclients[i] = 0;
				exits(nil);
			}
skipped:
		print("-next client-");
		}
		strcat(logtext, rcerr);
		memset(rcerr, 0, sizeof(rcerr));
		errflag = 0;
		print("replies finished, flag down\n");	
	}
	if(go == -1){
		print("errserver exiting\n");
		exits(nil);
	}
	exits(nil);
}

void
main(int argc, char *argv[])
{
	if(argc < 2){
		sysfatal("usage: iosrv [-p] initialclients");
	}
	sendhist = 0;
	promptflag = 0;
	ARGBEGIN {
	case 'p' :
		promptflag = 1;
		break;
	default :
		print(" badflag");
	} ARGEND
	activeclients = atoi(*argv);
	memset(deadclients, 0, sizeof(deadclients));
	strcpy(prompt, "iosrv% ");
	print("starting %d clients\n", activeclients);
	go = 1;
	openfiles();
	if(rfork(RFPROC|RFNOTEG|RFMEM) > 0) {
		checkctl();
	}
startpiping:
	go = 1;
	if(rfork(RFPROC|RFNOTEG|RFMEM) > 0) {
		getrequest();
	}
	if(rfork(RFPROC|RFNOTEG|RFMEM) > 0) {
		sendcommand();
	}
	if(rfork(RFPROC|RFNOTEG|RFMEM) > 0) {
		getreply();
	}
	if(rfork(RFPROC|RFNOTEG|RFMEM) > 0) {
		geterr();
	}
	if(rfork(RFPROC|RFNOTEG|RFMEM) > 0) {
		replyserver();
	}
	if(rfork(RFPROC|RFNOTEG|RFMEM) > 0) {
		errserver();
	}
/* We arrive here after forking all the servers off and sit and spin forever
 * if go is set to state 3 we resume message passsing by reforking everything
 */
	while (go >0){
		if (go == 3) {
			print("If you want to go to somewhere...\n");	
			goto startpiping;
		}
		sleep(5000);
	}
	print ("main exiting\n");
	return;
}

