#include <ncbi.h>
#include <gishlib.h>

static void _cdecl
_ticknext(tp, n)
	TaskBlkPtr	tp;
	unsigned long	n;
{
	if (n >= tp->tick_cnt && tp->tick_out < tp->nticks) {
		while (n > tp->tick_cnt && tp->tick_out < tp->nticks) {
			tp->tick_cnt += tp->tick_incr;
			tp->tick_out++;
			(*tp->tickproc)(tp->userp);
		}
	}
}


TaskBlkPtr _cdecl
TaskNew(ntasks, userp, tickproc, nticks)
	long	ntasks;
	Nlm_VoidPtr	userp;
	void	(_cdecl *tickproc)();
	long	nticks;
{
	TaskBlkPtr	tp;

	tp = (TaskBlkPtr) mem_malloc(sizeof(*tp) +
			sizeof(*tp->gtick_cnt) + sizeof(*tp->gtick_out));
	if (tp == NULL)
		return NULL;

	tp->gtick_cnt = (unsigned long PNTR) (((CharPtr)tp) + sizeof(*tp));
	tp->gtick_out = tp->gtick_cnt + 1;

	tp->userp = userp;
	tp->tickproc = tickproc;
	tp->ticknext = _ticknext;
	tp->tasknext = NULL;

	tp->ntasks = ntasks;
	tp->nticks = nticks;
	*tp->gtick_cnt = tp->tick_cnt = tp->tick_incr = MAX(ntasks / nticks, 1);
	*tp->gtick_out = tp->tick_out = 0;
	tp->chunksize = ntasks;
	tp->task_max = ntasks;
	tp->nprocs = 0;
	tp->id = 0;

	return tp;
}

TaskBlkPtr _cdecl
TaskLink(tp, id)
	TaskBlkPtr	tp;
	unsigned	id;
{
	if (id == 0)
		return tp;
	tp = (TaskBlkPtr)mem_dup(tp, sizeof(*tp));
	if (tp == NULL)
		return NULL;

	tp->id = id;
	return tp;
}


void _cdecl
TaskTickNext(tp, n)
	TaskBlkPtr	tp;
	long	n;
{
	if (tp == NULL || tp->ticknext == NULL)
		return;

	(*tp->ticknext)(tp, n);
	return;
}

void _cdecl
TaskDestruct(tp)
	TaskBlkPtr	tp;
{
	if (tp != NULL) {
		Nlm_MemSet((CharPtr)tp, 0, sizeof(*tp));
		mem_free(tp);
	}
}

void _cdecl
TaskUnlink(tp)
	TaskBlkPtr	tp;
{
	if (tp == NULL)
		return;
	if (tp->id == 0)
		return;
	TaskDestruct(tp);
}

void _cdecl
TaskInit(tp, nchunks)
	TaskBlkPtr	tp;
	int		nchunks;
{
	long	i;

	nchunks = MAX(nchunks, tp->proc_max);
	nchunks = MIN(nchunks, tp->ntasks);
	if (tp->proc_max > 1)
		tp->chunksize = tp->ntasks / nchunks;
	else {
		nchunks = tp->ntasks;
		tp->chunksize = 1;
	}
	tp->nchunks = nchunks;
	tp->chunksize = MAX(tp->chunksize, 1);
	if (tp->nchunks == tp->ntasks) {
		tp->task_cur = -1 - (int)tp->id;
		tp->task_max = tp->ntasks;
	}
	else {
		tp->task_cur = 0;
		tp->task_max = 0;
	}
}

long _cdecl
TaskNext(tp)
	TaskBlkPtr	tp;
{
	if (tp->task_cur == LONG_MIN)
		return LONG_MIN;
	if (tp->nchunks == tp->ntasks) {
		tp->task_cur += tp->proc_max;
		if (tp->task_cur >= tp->ntasks)
			return tp->task_cur = LONG_MIN;
		TaskTickNext(tp, tp->task_cur);
		return tp->task_cur;
	}
	return (*tp->tasknext)(tp);
}
