//
//p-masq.c
//
//Interface to the masquerading server.
//
//
//-UserX 2001/12/07

#include <stdlib.h>
#include <string.h>

#include "base/dblock.h"
#include "base/mem.h"
#include "base/str.h"
#include "pipe/masq.h"
#include "base/logger.h"
#include "net/protocfg.h"
#include "net/noderef.h"
#include "net/sockserv.h"

PipeMasq blankpipemasq = BLANKPIPEMASQ;

Pipe *pipemasqMake(char *protocol, char *options) {
	NodeRef *nr;
	SockHandle *sh;
	PipeMasq *thispipe = memCopy(&blankpipemasq, sizeof(PipeMasq), "PipeMasq", NULL);//xiclone(&blankpipemasq, sizeof(PipeMasq));
	pipeInitFunctions((Pipe *) thispipe, 
			(PipeFuncIn) pipemasqRead, (PipeFuncOut) pipemasqWrite, 
			(PipeFuncAttach) pipemasqAttach, (PipeFuncDetach) pipemasqDetach, 
			(PipeFuncClose) pipemasqClose, (PipeFuncStatus) pipemasqStatus);
	pipeInit((Pipe *) thispipe);
	
	thispipe->protocol = stringCopy(protocol);
	//make a connection to the masquerading server
	nr = noderefMake();
	noderefInit(nr, stringCopy(protocolmasqhost), stringCopy(protocol), stringCopy(protocolmasqtansport), NULL, NULL, NULL, protocolmasqport);
	sh = sockMake();
	sockSetNodeRef(sh, nr);
	noderefFree(nr);
	if(sockOpen(sh) != SOCK_ERR_NONE) {
		sockFree(sh);
	} else {
		sockservAddOpen(sh);
		sockRegister(sh);
		thispipe->sock = sh;
		sh->IOPipe.outBuffer = dblockAppendString(sh->IOPipe.outBuffer, protocol);
		sh->IOPipe.outBuffer->size++;
	}

	return (Pipe *)thispipe;
}

void pipemasqFree(PipeMasq *thispipe) {
	pipeFree(&thispipe->IOPipe);
	sockFree(thispipe->sock);
	stringFree(thispipe->protocol);
	memFree(thispipe);//xifree(thispipe);
}

void pipemasqDoIO(PipeMasq *thispipe) {
	int v;
	if((pipeStatus((Pipe *)thispipe->sock) & PSTATUS_CLOSED) != 0) {
		thispipe->IOPipe.status |= PSTATUS_CLOSED;
	}
	do {
		if(thispipe->inwaiting == 0) {
			if(thispipe->sock->IOPipe.inBuffer->size >= 2) {
				thispipe->inwaiting = dblockReadInt(thispipe->IOPipe.backPipe->inBuffer, 2);
			} else {
				break;
			}
		}
		if(thispipe->inwaiting != 0) {
			v = abs(thispipe->inwaiting);
			if(thispipe->sock->IOPipe.inBuffer->size >= v) {
				if(thispipe->inwaiting > 0) {
					LOGDEBUGTRAFFIC(stringJoinMany(
							"pipemasqDoIO: ",
							ptrToString(thispipe),
							", to core from masq server bytes:",
							v,
						NULL));
					//traffic going towards the core
					thispipe->IOPipe.inBuffer = dblockMove(thispipe->IOPipe.inBuffer, thispipe->sock->IOPipe.inBuffer, v);
				} else {
					LOGDEBUGTRAFFIC(stringJoinMany(
							"pipemasqDoIO: ",
							ptrToString(thispipe),
							", to socket from masq server bytes:",
							v,
						NULL));
					//traffic going towards the socket
					thispipe->IOPipe.backPipe->outBuffer = dblockMove(thispipe->IOPipe.backPipe->outBuffer, thispipe->sock->IOPipe.inBuffer, v);
				}
				thispipe->inwaiting = 0;
			} else {
				break;
			}
		}
	} while(thispipe->inwaiting == 0);

	while(thispipe->IOPipe.outBuffer->size > 0) {
		//traffic coming from the core
		v = thispipe->IOPipe.outBuffer->size;
		if(v > 0x7fff) {
			v = 0x7fff;
		}
		LOGDEBUGTRAFFIC(stringJoinMany(
				"pipemasqDoIO: ",
				ptrToString(thispipe),
				", from core to masq server bytes:",
				v,
			NULL));
		thispipe->sock->IOPipe.outBuffer = dblockWriteInt(thispipe->sock->IOPipe.outBuffer, v, 2);
		thispipe->sock->IOPipe.outBuffer = dblockMove(thispipe->sock->IOPipe.outBuffer, thispipe->IOPipe.outBuffer, v);
	}

	while(thispipe->IOPipe.backPipe->inBuffer->size > 0) {
		//traffic coming from the socket
		v = thispipe->IOPipe.backPipe->inBuffer->size;
		if(v > 0x7fff) {
			v = 0x7fff;
		}
		LOGDEBUGTRAFFIC(stringJoinMany(
				"pipemasqDoIO: ",
				ptrToString(thispipe),
				", from socket to masq server bytes:",
				v,
			NULL));
		thispipe->sock->IOPipe.outBuffer = dblockWriteInt(thispipe->sock->IOPipe.outBuffer, -v, 2);
		thispipe->sock->IOPipe.outBuffer = dblockMove(thispipe->sock->IOPipe.outBuffer, thispipe->IOPipe.backPipe->inBuffer, v);
	}

}

void pipemasqRead(PipeMasq *thispipe, Pipe **errPipe) {
	*errPipe = pipeGenericRead(thispipe->IOPipe.backPipe);
	if(*errPipe == NULL) {
		//LOGDEBUGTRAFFIC(stringJoinMany(
		//		"pipemasqRead: ",
		//		ptrToString(thispipe),
		//		", read bytes:",
		//		intToHexString(thispipe->IOPipe.backPipe->inBuffer->size),
		//	NULL));
		pipemasqDoIO(thispipe);
		//thispipe->IOPipe.inBuffer = dblockMoveAll(thispipe->IOPipe.inBuffer, thispipe->IOPipe.backPipe->inBuffer);
	}
}

void pipemasqWrite(PipeMasq *thispipe, Pipe **errPipe) {
	//LOGDEBUGTRAFFIC(stringJoinMany(
	//		"pipemasqWrite: ",
	//		ptrToString(thispipe),
	//		", write bytes:",
	//		intToHexString(thispipe->IOPipe.outBuffer->size),
	//	NULL));
	pipemasqDoIO(thispipe);
	//thispipe->IOPipe.backPipe->outBuffer = dblockMoveAll(thispipe->IOPipe.backPipe->outBuffer, thispipe->IOPipe.outBuffer);
	*errPipe = pipeGenericWrite(thispipe->IOPipe.backPipe);
}


void pipemasqAttach(PipeMasq *thispipe) {

}

void pipemasqDetach(PipeMasq *thispipe) {
	pipemasqFree(thispipe);
}

void pipemasqClose(PipeMasq *thispipe) {
}

void pipemasqStatus(PipeMasq *thispipe, int *status) {
	*status |= PSTATUS_READX | PSTATUS_WRITEX;
}

