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

static void LIBCALL
_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);
		}
	}
}


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

	tp = (TaskBlkPtr) mem_calloc(1, sizeof(*tp));
	if (tp == NULL)
		return NULL;
	tp->root = tp;

	tp->pos_cnt = tp->pos_ray;
	tp->flag = tp->flag_ray;
	tp->gtick_cnt = &tp->_gtick_cnt;
	tp->gtick_out = &tp->_gtick_out;

	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 LIBCALL
TaskLink(rp, id)
	TaskBlkPtr	rp; /* points to root */
	unsigned	id;
{
	TaskBlkPtr	tp;

	if (rp == NULL || (rp = rp->root) == NULL)
		return NULL;

	mproc_lock();
	++rp->nprocs;
	rp->proc_max = MAX(rp->proc_max, rp->nprocs);
	mproc_unlock();

	if (id == 0)
		return rp;

	tp = (TaskBlkPtr)Nlm_MemDup(rp, sizeof(*rp));
	if (tp == NULL)
		return NULL;

	tp->id = id;
	tp->pos_cnt = &(rp->pos_ray[id]);
	tp->flag = rp->flag_ray;

	return tp;
}


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

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

void LIBCALL
TaskPosIncr(tp)
	TaskBlkPtr	tp;
{
	++(*tp->pos_cnt);
	return;
}

unsigned long LIBCALL
TaskPosSum(tp)
	TaskBlkPtr	tp;
{
	register int	i, proc_max;
	register unsigned long	sum = 0, PNTR pos_ray;

	if (tp == NULL)
		return 0;

	tp = tp->root;
	pos_ray = tp->pos_ray;
	proc_max = tp->proc_max;
	for (i = 0; i < proc_max; ++i)
		sum += pos_ray[i];

	return sum;
}

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

void LIBCALL
TaskUnlink(tp)
	TaskBlkPtr	tp;
{
	if (tp == NULL)
		return;
	mproc_lock();
	--tp->root->nprocs;
	mproc_unlock();
	if (tp == tp->root)
		return;
	TaskDestruct(tp);
}

void LIBCALL
TaskInit(tp, nchunks)
	TaskBlkPtr	tp;
	int		nchunks;
{
	tp->proc_max = tp->root->proc_max;
	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 LIBCALL
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);
}

void LIBCALL
TaskFlagSetAll(tp)
	TaskBlkPtr	tp;
{
	int		i;

	for (i = 0; i < tp->proc_max; ++i)
		tp->flag[i] = TRUE;
}

void LIBCALL
TaskFlagSet(tp)
	TaskBlkPtr	tp;
{
	tp->flag[tp->id] = TRUE;
}

void LIBCALL
TaskFlagClr(tp)
	TaskBlkPtr	tp;
{
	tp->flag[tp->id] = FALSE;
}

