//
//p-spurt.c
//
//A pipe that generates random spurts.
//
//
//-UserX 2001/11/30



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

#include "base/dblock.h"
#include "base/mem.h"
#include "pipe/spurt.h"
#include "base/logger.h"
#include "crypt/salt.h"
#include "crypt/random.h"

PipeSpurt blankpipespurt = BLANKPIPESPURT;

#define PSPURT_SIZE 1 //number of bytes in the spurt pipe header

#define PSPURT_MIN 11
#define PSPURT_MAX 52


Pipe *pipespurtMake(char *pipename, char *options) {
	PipeSpurt *ps = memCopy(&blankpipespurt, sizeof(PipeSpurt), "PipeSpurt", NULL);//xiclone(&blankpipespurt, sizeof(PipeSpurt));
	pipeInitFunctions((Pipe *) ps, 
			(PipeFuncIn) pipespurtRead, (PipeFuncOut) pipespurtWrite, 
			(PipeFuncAttach) pipespurtAttach, (PipeFuncDetach) pipespurtDetach, 
			(PipeFuncClose) pipespurtClose, (PipeFuncStatus) pipespurtStatus);
	pipeInit((Pipe *) ps);
	ps->IOPipe.status |= PSTATUS_READY;
	ps->nextSpurt = time(NULL) + 6;	
	ps->nextSpurtLength = 12;
	return (Pipe *)ps;
}

void pipespurtFree(PipeSpurt *ps) {
	pipeFree(&ps->IOPipe);
	memFree(ps);//xifree(ps);
}

void pipespurtRead(PipeSpurt *thispipe, Pipe **errPipe) {
	int v;
	*errPipe = pipeGenericRead(thispipe->IOPipe.backPipe);
	if(*errPipe != NULL) {
		return;
	}
	while(thispipe->IOPipe.backPipe->inBuffer->size > 0) {
		if(thispipe->readSpurt == 0) {
			if(thispipe->IOPipe.backPipe->inBuffer->size >= PSPURT_SIZE) {
				thispipe->readSpurt = (int8) thispipe->IOPipe.backPipe->inBuffer->data[0];
				thispipe->IOPipe.backPipe->inBuffer = dblockDelete(thispipe->IOPipe.backPipe->inBuffer, 0 ,PSPURT_SIZE);
			} else {
				break;
			}
		}
		if(thispipe->readSpurt != 0) {
			v = thispipe->readSpurt;
			if(v < 0) {
				v = -v;
			}
			LOGDEBUGTRAFFIC(stringJoinMany(
					"pipespurtRead: ",
					ptrToString(thispipe),
					", read bytes:",
					intToHexString(thispipe->readSpurt),
				NULL));
			if(thispipe->IOPipe.backPipe->inBuffer->size >= v) {
				if(thispipe->readSpurt > 0) {
					thispipe->IOPipe.inBuffer = dblockMove(thispipe->IOPipe.inBuffer, thispipe->IOPipe.backPipe->inBuffer, v);
				} else {
					thispipe->IOPipe.backPipe->inBuffer = dblockDelete(thispipe->IOPipe.backPipe->inBuffer, 0, v);
				}
				thispipe->readSpurt = 0;
			} else {
				break;
			}
		}
	}
}

void pipespurtWrite(PipeSpurt *thispipe, Pipe **errPipe) {
	time_t now;
	int v;


	if(thispipe->IOPipe.outBuffer->size > 0) {
		v = thispipe->IOPipe.outBuffer->size;
		if(v > PSPURT_MAX) {
			v = PSPURT_MAX;
		}
		if(v > PSPURT_MIN) {
			v = PSPURT_MIN + randomint(v - PSPURT_MIN + 1);
		}
		thispipe->IOPipe.backPipe->outBuffer = dblockExpand(thispipe->IOPipe.backPipe->outBuffer, thispipe->IOPipe.backPipe->outBuffer->size + PSPURT_SIZE);
		thispipe->IOPipe.backPipe->outBuffer->data[thispipe->IOPipe.backPipe->outBuffer->size] = (int8) v;
		thispipe->IOPipe.backPipe->outBuffer->size++;
		
		thispipe->IOPipe.backPipe->outBuffer = dblockMove(thispipe->IOPipe.backPipe->outBuffer, thispipe->IOPipe.outBuffer, v);

		return;
	}
	now = time(NULL);
	if(now >= thispipe->nextSpurt) {
		v = thispipe->nextSpurtLength;
		thispipe->IOPipe.backPipe->outBuffer = dblockExpand(thispipe->IOPipe.backPipe->outBuffer, thispipe->IOPipe.backPipe->outBuffer->size + PSPURT_SIZE + v);
		
		thispipe->IOPipe.backPipe->outBuffer->data[thispipe->IOPipe.backPipe->outBuffer->size] = (uint8) -v;

		saltBuffer(thispipe->IOPipe.backPipe->outBuffer->data + thispipe->IOPipe.backPipe->outBuffer->size + PSPURT_SIZE, v);

		thispipe->IOPipe.backPipe->outBuffer->size += PSPURT_SIZE + v;

		thispipe->nextSpurtLength = PSPURT_MIN + randomint(PSPURT_MAX - PSPURT_MIN + 1);
		
		thispipe->nextSpurt = now + 2 + randomint(10 + thispipe->nextSpurtLength / 3);
	}

	*errPipe = pipeGenericWrite(thispipe->IOPipe.backPipe);
}


void pipespurtAttach(PipeSpurt *thispipe) {

}

void pipespurtDetach(PipeSpurt *thispipe) {
	pipespurtFree(thispipe);
}

void pipespurtClose(PipeSpurt *thispipe) {
}

//the data pending maybe all be unused garbage so clear the READ bit
void pipespurtStatus(PipeSpurt *thispipe, int *status) {
	*status &= ~PSTATUS_READ;
}
