//
//core.c
//
//
//
//
//-UserX 2001/11/09

//#include <stdio.h>

#include "base/str.h"
#include "base/logger.h"
#include "msgcore/core.h"
#include "net/sock.h"
#include "net/sockserv.h"
//todo: remove this dependence on this include
#include "iip/wingui.h"
#include "msgcore/messageq.h"
#include "pipeface/raw.h"
#include "misc/global.h"
#include "net/protocol.h"
//todo: remove this dependence on this include
#include "iip/iip.h"
#include "misc/global.h"
#include "ui/ui.h"

PipeFaceArray *PFA = NULL;
MessageQueue *MQ = NULL;

void coreInit(void) {
	if(PFA == NULL) {
		PFA = pfaMake();
	}
	if(MQ == NULL) {
		MQ = msgqMake();
	}
}

//start connection of an inbound socket to the core
void coreAddInSock(SockHandle *sh, int *result) {
	SockHandle *nsh = NULL;
	PipeFace *pf;
	PipeFace *opf;
	int i;

	if(sh == NULL) {
		*result = 1;
		return;
	}
	for(i = 0; i <= retries; i++) {
		LOGDEBUG(stringJoinMany(
			"coreAddInSock: try (", intToString(i + 1), ")",
			" of (", intToString(retries + 1),")",
			NULL));
		nsh = sockservConnect();
		if(nsh != NULL) {
			break;
		}
	}
	if(nsh == NULL) {
		LOGDEBUG("coreAddInSock: failed");
		sockClose(sh);
		sockFree(sh);
		*result = 1;
		return;
	}

	//pf = pipefaceMake();
	//opf = pipefaceMake();
	//todo: check sh for what protocol this new connection is for.
	//pipefaceInit(pf, &sh->IOPipe);
	//pipefaceInitFuncs(pf, pfGetMsg, pfPutMsg, NULL);
	//pf->PipeFaceType = PFT_RAW;
	//pfaAdd(PFA, pf);

	//pipefaceInit(opf, &nsh->IOPipe);
	///pipefaceInitFuncs(opf, pfGetMsg, pfPutMsg, NULL);
	//opf->PipeFaceType = PFT_RAW;
	//pfaAdd(PFA, opf);

	//todo: handle the case where no pipeface is returned.
	pf = protocolBuildPipe(sh, sh->localnoderef->Protocol);
	if(pf == NULL) {
		sockClose(sh);
		sockFree(sh);
		*result = 1;
		return;
	}
	pf->retried = i;

	opf = protocolBuildPipe(nsh, nsh->noderef->Protocol);
	if(opf == NULL) {
		sockClose(sh);
		sockFree(sh);
		pipefaceClose(pf);
		*result = 1;
		return;
	}
	opf->retried = -1;

	pfaAdd(PFA, pf);
	pfaAdd(PFA, opf);

	pf->opposite = opf;
	opf->opposite = pf;

	*result = 0;
	return;
}


PipeFace *coreMakeConnection(int retries) {
	SockHandle *nsh = NULL;
	PipeFace *pf;
	int i;

	for(i = 0; i <= retries; i++) {
		LOGDEBUG(stringJoinMany(
			"coreMakeConnection: try (", intToString(i + 1), ")",
			" of (", intToString(retries + 1),")",
			NULL));
		nsh = sockservConnect();
		if(nsh != NULL) {
			break;
		}
	}
	if(nsh == NULL) {
		LOGDEBUG("coreMakeConnection: failed");
		return NULL;
	}

	pf = protocolBuildPipe(nsh, nsh->noderef->Protocol);
	if(pf == NULL) {
		sockClose(nsh);
		sockFree(nsh);
		return NULL;
	}
	pf->retried = i;

	return pf;
}



void coreSwapPipeFace(PipeFace *oldpf, PipeFace *newpf) {
	pfaSwap(PFA, oldpf, newpf);
}



void coreProcessPipeFace(void) {
	int i;
	Message *msg;
	PipeFace *opf;
	msgqCompact(MQ);
	for(i = 0; i < PFA->size; i++) {
		if(PFA->data[i] != NULL) {
			pipeGenericRead(PFA->data[i]->IOPipe.backPipe);
			pipeGenericWrite(PFA->data[i]->IOPipe.backPipe);
			while((msg = pipefaceGetMessage(PFA->data[i])) != NULL) {
				msgqAdd(MQ, msg);
			}
			if(pipeStatus(PFA->data[i]->IOPipe.backPipe) & PSTATUS_CLOSED) {
				
				LOGDEBUG("coreProcessPipeFace: connection closed");
				if(PFA->data[i]->opposite != NULL) {
					opf = PFA->data[i]->opposite;

					//retry kludge (this is ugly)
					if(PFA->data[i]->retried == -1) {
						//only retry if retry count is still below the max
						//and the pipeface hasn't been active yet.
						if(opf->retried <= retries && opf->active == 0) {
							LOGDEBUG("coreProcessPipeFace: connection closed: retrying");
							pipefaceClose(PFA->data[i]);
							PFA->data[i] = coreMakeConnection(retries - opf->retried);
							if(PFA->data[i] == NULL) {
								pfaDelete(PFA, opf); 
								pipefaceClose(opf);
								LOGDEBUG("coreProcessPipeFace: connection closed: retry failed");
								continue;
							}
							LOGDEBUG("coreProcessPipeFace: connection closed: retry successful");
							PFA->data[i]->opposite = opf;
							opf->opposite = PFA->data[i];
							opf->retried += PFA->data[i]->retried + 1;
							PFA->data[i]->retried = -1;
							continue;
						}
					}
					LOGDEBUG("coreProcessPipeFace: connection closed: no retry");
					pfaDelete(PFA, opf);
					pipefaceClose(opf);
				}
				pipefaceClose(PFA->data[i]);
				PFA->data[i] = NULL;
				
			}
		}
	}
}

void coreProcessMessageQueue(void) {
	int i;
	int j;
	for(i = 0; i < MQ->size; i++) {
		//todo: better delegation when real messages get added.
		if(MQ->data[i] != NULL) {
			j = pfaFindByITag(PFA, MQ->data[i]->fromitag);
			if(j == -1) {
				messageFree(MQ->data[i]);
			} else {
				pipefacePutMessage(PFA->data[j]->opposite, MQ->data[i]);
			}
			MQ->data[i] = NULL;
		}
	}
}

int coreStart(void) {
//	NodeRef *nr = noderefMake();

	sockservInit(coreAddInSock);
	coreInit();

	if(sockservStartListen()) {
#ifdef _WINDOZE_
		if(uiDialog(_("Run IIP anyway?"), _("Unable to open one or more sockets.\nDo you wish to run IIP anyway?"), UIDIALOG_YESNO, UIDIALOG_NO) == UIDIALOG_NO) {
			return -1;
		}
#else
		return -1;
#endif
	}
//	noderefInit(nr, "0.0.0.0", "plain", localPort);
//	sockservListen(nr);
//	noderefFree(nr);

	return 0;
}

int coreMainLoop(void) {
	//int i = 0;
	if(coreStart()) {
		return 1;
	}
	while(1) {
#ifdef _WINDOZE_
		if(dowindows()) {
			break;
		}
#endif
		coreMainPoll(0);
	}
	return 0;
}

void coreMainPoll(int shortdelay) {
	if(reloadsettings != 0) {
		iipReload();
	}
	sockservProcess(shortdelay);
	if(nextdhdelay >= 0) {
		nextdhdelay -= 1;
	}
	if(PFA != NULL) {
		coreProcessPipeFace();
		coreProcessMessageQueue();
	}
	//i = (i++) % 8;
	//printf("%c", "-<{(|)}>"[i]);
	//printf("\b");
}

