//
//pipeface.c
//
//These are functions for handling "PipeFace"s.
//
//PipeFaces are an interface between a chain of pipes and the core.
//
//
//-UserX 2001/11/08

#include <stdlib.h>
#include "pipeface/pipeface.h"
//#include "core.h"
#include "base/mem.h"
#include "base/array.h"

#include "base/logger.h"
#include "base/str.h"

PipeFace blankpipeface = BLANKPIPEFACE;
PipeFace *blankpipefaceptr = NULL;

void pipefaceInit(PipeFace *pf, Pipe *backpipe) {
	LOGDEBUG (stringJoinMany(
			"pipefaceInit:", 
			ptrToString(pf),
			", backpipe ",
			ptrToString(backpipe),
			NULL));
	if(pf == NULL) {
		return;
	}
	pipeInit(&pf->IOPipe);
	if(backpipe != NULL) {
		pipeAttach(backpipe, &pf->IOPipe);
	}
}

void pipefaceInitFuncs(PipeFace *pf, void (*getf)(struct PipeFace *, Message **), void (*putf)(struct PipeFace *, Message *), void (*freef)(struct PipeFace *)) {
	if(pf == NULL) {
		return;
	}
	pf->getmsgFunction = getf;
	pf->putmsgFunction = putf;
	pf->freeFunction = freef;
}


void pipefaceMakeAt(PipeFace *pf) {
	LOGDEBUG (stringJoin("pipefaceMakeAt:", ptrToString(pf)));
	pf->RegCount = 1;
	pf->iTag = itagGet();
	pipefaceInit(pf, NULL);
}

//create a blank pipeface
PipeFace *pipefaceMake() {
	PipeFace *pf = memCopy(&blankpipeface, sizeof(PipeFace), "PipeFace", NULL);//xiclone(&blankpipeface, sizeof(PipeFace));
	LOGDEBUG (stringJoin("pipefaceMake:", ptrToString(pf)));
	pipefaceMakeAt(pf);
	return pf;
}


Message *pipefaceGetMessage(PipeFace *pf) {
	Message *msg = NULL;
	if(pf->getmsgFunction == NULL) {
		//todo: error messaging
		return msg;
	}
	pf->getmsgFunction(pf, &msg);
	return msg;
}

void pipefacePutMessage(PipeFace *pf, Message *msg) {
	if(pf->putmsgFunction == NULL) {
		//todo: error messaging
		return;
	}
	pf->putmsgFunction(pf, msg);
}


void pipefaceFree(PipeFace *pf) {
	LOGDEBUG (stringJoin("pipefaceFree:", ptrToString(pf)));
	if(pf == NULL) {
		return;
	}
	if((--pf->RegCount) > 0) {
		return;
	}

	pipeDetach(pf->IOPipe.backPipe);
	pipeFree(&pf->IOPipe);
	if(pf->freeFunction != NULL) {
		pf->freeFunction(pf);
	} else {
		memFree(pf);//xifree(pf);
	}
}

//calls pipeclose on the pipechain.
//and then call pipefacefree on itself
void pipefaceClose(PipeFace *pf) {
	LOGDEBUG (stringJoin("pipefaceClose:", ptrToString(pf)));
	if(pf == NULL) {
		return;
	}
	pipeClose(&pf->IOPipe);
	pipefaceFree(pf);
}

//Replace an existing pipe with a new pipe.
//Will move many relevant structures over.
void pipefaceSwap(PipeFace *oldpf, PipeFace *newpf) {
	if(oldpf == NULL || newpf == NULL) {
		return;
	}
	
	dblockFree(newpf->IOPipe.inBuffer);
	dblockFree(newpf->IOPipe.outBuffer);
	newpf->IOPipe.inBuffer = oldpf->IOPipe.inBuffer;
	newpf->IOPipe.outBuffer = oldpf->IOPipe.outBuffer;
	oldpf->IOPipe.inBuffer = NULL;
	oldpf->IOPipe.outBuffer = NULL;
//todo: fix
	pipefaceFree(newpf->opposite);
	newpf->opposite = oldpf->opposite;
	oldpf = NULL;

	//coreSwapPipeFace(oldpf, newpf);

	
}



PipeFaceArray *pfaMake() {
	return (PipeFaceArray *) arrayMake(sizeof(PipeFace *), 0, &blankpipefaceptr, NULL, NULL);
}


//adds pf to pfa expanding pfa if needed.
void pfaAdd(PipeFaceArray *pfa, PipeFace *pf) {
	int i;
	for(i = 0; i < pfa->size;i ++) {
		if(pfa->data[i] == NULL) {
			pfa->data[i] = pf;
			return;
		}
	}
	arrayAddElements((ArrayHandle *) pfa, 1);
	pfa->data[pfa->size - 1] = pf;
}

//looks for pipeface pf in pfa. if found it will call pipefaceclose on pf and erase it from pfa
void pfaDelete(PipeFaceArray *pfa, PipeFace *pf) {
	int i;
	for(i = 0; i < pfa->size;i ++) {
		if(pfa->data[i] == pf) {
			//pipefaceClose(pfa->data[i]);
			pfa->data[i] = NULL;
			return;
		}
	}
}

//swaps an exist pipeface pointer for a new one.
//does not realease or allocate any resources.
void pfaSwap(PipeFaceArray *pfa, PipeFace *oldpf, PipeFace *newpf) {
	int i;
	for(i = 0; i < pfa->size;i ++) {
		if(pfa->data[i] == oldpf) {
			pfa->data[i] = newpf;
			return;
		}
	}
}

//returns the index in the array of the pipeface match itag
//returns -1 on failure
int pfaFindByITag(PipeFaceArray *pfa, ITag itag) {
	int i;
	for(i = 0; i < pfa->size;i ++) {
		if(pfa->data[i] != NULL && pfa->data[i]->iTag == itag) {
			return i;
		}
	}
	return -1;
}
