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

static AlarmBlkPtr	abp;
static volatile time_t	lasttick;
static int	lineticks, goodticks;
static unsigned long	incr;
static TaskBlkPtr	tp;

static void
tickproc(userp)
	Nlm_VoidPtr	userp;
{
	BlastIoPtr	biop = &b_out;
	FILE	*fp;

	critical_begin();
	mproc_lock();
	lasttick = time(NULL);
	++goodticks;
	if ((fp = biop->fp) != NULL) {
		putc_unlocked('.', fp);
		if (++lineticks >= 65) {
			lineticks = 0;
			fputs("\n         ", fp);
		}
		fflush(fp);
	}

	if (biop->aip != NULL) {
		Bio_JobProgressAsnWrite(biop, goodticks * incr, TaskPosSum(tp));
	}
	mproc_unlock();
	critical_end();
}

static void
waitproc(userp)
	Nlm_VoidPtr	userp;
{
	BlastIoPtr	biop = &b_out;
	FILE	*fp;
	time_t	now;
	double	since;

	now = time(NULL);
	since = difftime(now, lasttick);
	if (since < (double)progress_period - 1) {
		AlarmReset(abp, MAX(5, progress_period - since));
		return;
	}

	mproc_lock();

	if ((fp = biop->fp) != NULL) {
		putc_unlocked('*', fp);
		if (++lineticks >= 65) {
			lineticks = 0;
			fputs("\n         ", fp);
		}
		fflush(fp);
	}

	if (biop->aip != NULL)
		Bio_JobProgressAsnWrite(biop, goodticks * incr, TaskPosSum(tp));

	mproc_unlock();
}

void
job_start(jobid, desc, size)
	int		jobid;
	CharPtr	desc;
	unsigned long	size;
{
	goodticks = lineticks = 0;

	incr = MAX(1, size / NTICKS);

	if (b_out.fp != NULL) {
		fprintf(b_out.fp, "%s", desc);
		fflush(b_out.fp);
	}
	Bio_JobStartAsnWrite(&b_out, jobid, desc, size);

	AlarmEvery(&abp, progress_period, (FnPtr)waitproc, NULL);
}


void
job_done(done, pos)
	unsigned long	done;
	unsigned long	pos;
{
	AlarmClr(abp);
	if (b_out.fp != NULL) {
		fprintf(b_out.fp, "done\n");
		fflush(b_out.fp);
	}
	Bio_JobDoneAsnWrite(&b_out, done, pos);
}


void
RunWild(jobid, desc, n, func)
	int		jobid;
	char	*desc; /* Human readable name for the task to perform */
	unsigned long	n; /* Number of discrete subtasks */
	void	(*func) PROTO(());
{
	job_start(jobid, desc, n);

#ifdef MPROC_AVAIL
#define run_wild mrun_wild
#endif
	tp = run_wild(n, func, NULL, &tp, tickproc, NTICKS, numprocs, &nprocs, SigTerm);
	if (tp != NULL && tp->proc_max == 0 && n != 0)
		bfatal(ERR_MPFORK, "Could not fork for multiprocessing; Is your OS current?");

	numprocs = tp->proc_max;

	job_done(n, TaskPosSum(tp));
	TaskDestruct(tp);
}
