//
//protocol.c
//
//
//-UserX 2001/11/13

#include <stdlib.h>
#include <string.h>
#include "misc/compat.h"
#include "net/protocol.h"
#include "pipe/pipe.h"
#include "pipeface/pipeface.h"
#include "base/str.h"
#include "base/strarray.h"
#include "base/logger.h"
#include "net/sock.h"
#include "net/protocfg.h"
#include "base/array.h"


PipeProtocolEntry blankpipeprotocolentry = BLANKPIPEFACEPROTOCOLENTRY;
PipeFaceProtocolEntry blankpipefaceprotocolentry = BLANKPIPEFACEPROTOCOLENTRY;

PipeProtocolArrayHandle *pparray = NULL;
PipeFaceProtocolArrayHandle *pfparray = NULL;

void protocolInit(void) {
	if(pparray == NULL) {
		pparray = (PipeProtocolArrayHandle *)arrayMake(sizeof(PipeProtocolEntry), 0, &blankpipeprotocolentry, NULL, NULL);
	}
	if(pfparray == NULL) {
		pfparray = (PipeFaceProtocolArrayHandle *)arrayMake(sizeof(PipeFaceProtocolEntry), 0, &blankpipefaceprotocolentry, NULL, NULL);
	}
}

void protocolAddPipeList(PipeProtocolEntry *ppelist) {
	int i;
	int j;
	if(ppelist == NULL) {
		return;
	}
	protocolInit();
	for(i = 0; ppelist[i].pipename != NULL; i++) {
		j = pparray->size;
		arrayInsert((ArrayHandle *)pparray, pparray->size, 1);
		pparray->data[j] = ppelist[i];
	}
}

void protocolAddPipeFaceList(PipeFaceProtocolEntry *pfpelist) {
	int i;
	int j;
	if(pfpelist == NULL) {
		return;
	}
	protocolInit();
	for(i = 0; pfpelist[i].pipename != NULL; i++) {
		j = pfparray->size;
		arrayInsert((ArrayHandle *)pfparray, pfparray->size, 1);
		pfparray->data[j] = pfpelist[i];
	}
}

void protocolClearPipeList(void) {
	protocolInit();
	arrayDelete((ArrayHandle *)pparray, 0, pparray->size);
}

void protocolClearPipeFaceList(void) {
	protocolInit();
	arrayDelete((ArrayHandle *)pfparray, 0, pfparray->size);
}

Pipe *protocolGetPipe(char *pipename, char *options) {
	int i;
	protocolInit();
	for(i = 0; i <= pparray->size; i++) {
		if(stringCaseCompare(pparray->data[i].pipename, pipename) == 0) {
			return (pparray->data[i].make)(pipename, options);
		}
	}
	LOGERROR(stringJoinMany("Pipe type \"",
			pipename,
			"\" not found.",
			NULL));
	return NULL;
/*
	switch(stringIntPairSearch(pipelist, pipename)) {
	case PL_CRYPT:
		return pipecryptMake(options);
	case PL_STEADY:
		return pipesteadyMake(options);
	case PL_SPURT:
		return pipespurtMake(options);
	case PL_DUMMY:
		return pipedummyMake(options);
	case PL_BACK:
		return pipebackwardMake(options);
	case -1:
		//todo: checking of available masquerading protocols
		if(stringIntPairSearch(pipemasqlist, pipename) != -1) {
			pipemasqMake(pipename);
		}
		LOGERROR(stringJoinMany("Pipe type \"",
				pipename,
				"\" not found.",
				NULL));
		return NULL;
	default:
		LOGERROR(stringJoinMany("Internal error, pipe type \"",
				pipename,
				"\" found but unhandled by code.",
				NULL));
		return NULL;
	}
*/
}

PipeFace *protocolGetPipeFace(char *pipefacename, char *options) {
	int i;
	protocolInit();
	for(i = 0; i <= pfparray->size; i++) {
		if(stringCaseCompare(pfparray->data[i].pipename, pipefacename) == 0) {
			return (pfparray->data[i].make)(pipefacename, options);
		}
	}
	LOGERROR(stringJoinMany("Pipeface type \"",
			pipefacename,
			"\" not found.",
			NULL));
	return NULL;
/*
	switch(stringIntPairSearch(pipefacelist, pipefacename)) {
	case PFL_CORE:
	case PFL_RAW:
		return pfcoreMake(options);
	case PFL_VIRC:
		return pfvircMake(options);
	case -1:
		LOGERROR(stringJoinMany("Pipeface type \"",
				pipefacename,
				"\" not found.",
				NULL));
		return NULL;
	default:
		LOGERROR(stringJoinMany("Internal error pipeface type \"",
				pipefacename,
				"\" found but unhandled by code.",
				NULL));
		return NULL;
	}
*/
}

//builds a pipe chain attached to an existing socket
PipeFace *protocolBuildPipe(SockHandle *sh, char *protocol) {
	char *s;
	char *p;
	Pipe *newpipe = NULL;
	Pipe *backpipe = &sh->IOPipe;
	PipeFace *pf = NULL;

	if(sh == NULL) {
		return NULL;
	}
	if(isStringBlank(protocol)) {
		LOGERROR("Missing protocol string (check for missing 'protocol' or 'networkprotocol' settings).");
		return NULL;
	}

	//todo: validate protocol string

	s = stringCopy(protocol);

	for(p = stringSplit(s, ":"); !isStringBlank(s); p = stringSplit(s, ":")) {
		//todo:error checking
		newpipe = protocolGetPipe(p, ""); //todo: extract options
		if(newpipe == NULL) {
			newpipe = backpipe;
			while(newpipe != &sh->IOPipe) {
				backpipe = newpipe->backPipe;
				pipeDetach(newpipe);
				newpipe = backpipe;
			}
			return NULL;
		}
		pipeAttach(backpipe, newpipe);
		backpipe = newpipe;
		stringFree(p);
	}
	stringFree(s);
	//todo: error checking
	pf = protocolGetPipeFace(p, ""); //todo: extract options
	pipefaceInit(pf, backpipe);
	stringFree(p);

	return pf;

}

