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

HitListPoolPtr
HitListPoolNew()
{
	return (HitListPoolPtr) PoolNew(200, sizeof(HitList), offsetof(HitList,next), NULL);
}

size_t
hsp_count(hlp)
	HitListPtr	hlp;
{
	return LinkCount((VoidPtr)hlp->hp, offsetof(HSP,next));
}

size_t
hitlist_count(hlp)
	register HitListPtr	hlp;
{
	return LinkCount((VoidPtr)hlp, offsetof(HitList,next));
}

void
hitlist_truncate(hlpp, cnt)
	HitListPtr PNTR	hlpp;
	unsigned	cnt;
{
	HitListPtr	hlp;

	if (hlpp == NULL || (hlp = *hlpp) == NULL)
		return;

	while (cnt-- > 1 && hlp != NULL)
		hlp = hlp->next;
	if (hlp != NULL)
		hlp->next = NULL;
}


/* hitlist_save -- firsthl points to the head
of the hitlist to which hlp is to be appended. */
void
hitlist_save(pbp, firsthl, hlp)
	HitListPoolPtr	pbp;
	register HitListPtr  PNTR firsthl;
	register HitListPtr  hlp;
{
	register HitListPtr	new;

	if (hlp == NULL)
		return;

	new = (HitListPtr) PoolGet((PoolBlkPtr)pbp);
	if (new == NULL)
		bfatal(ERR_MEM, "No more memory for HitList pool");
	*new = *hlp;

	new->next = *firsthl;
	*firsthl = new;
	return;
}
 
/* hitlist_prune_by_score -- weed out HSPs not satisfying a cutoff score */
void
hitlist_prune_by_score(hlp, poolp, cutoff)
	HitListPtr  hlp;
	register HSPPoolPtr	poolp;
	register Score_t cutoff;
{
	register HSPPtr  hp, hp2;
	register unsigned    pruned = 0;
 
	if (hlp == NULL)
		return;
 
	for (hp = hlp->hp, hp2 = NULL; hp != NULL; ) {
		if (hp->score < cutoff) {
			++pruned;
			/* remove hp from the chain */
			if (hp2 == NULL)
				hlp->hp = hp->next;
			else
				hp2->next = hp->next;
			HSPPoolReturn(poolp, hp);
			if (hp2 == NULL)
				hp = hlp->hp;
			else
				hp = hp2->next;
			continue;
		}
		hp2 = hp;
		hp = hp->next;
	}
	hlp->hspcnt -= pruned;
	return;
}

/* hitlist_prune_by_pval -- weed out HSPs not satisfying a cutoff pval */
void
hitlist_prune_by_cutoff(hlp, poolp, which, cutoff)
	HitListPtr  hlp;
	register HSPPoolPtr	poolp;
	register enum score_type	which;
	register double cutoff;
{
	register HSPPtr  hp, hp2, hplast;
	unsigned    npruned;
	HSPPtr	hpray[3];
	register int		sgn;
 
	switch (which) {
	case SCORET_EXPECT:
		if (hlp->signifhsp->expect <= cutoff)
			goto Prune;
		break;
	case SCORET_PVALUE:
		if (hlp->signifhsp->P_val <= cutoff)
			goto Prune;
		break;
	default:
		break;
	}
	HSPPoolReturnList(poolp, hlp->hp);
	hlp->hp = hlp->signifhsp = NULL;
	hlp->hspcnt = 0;
	return;
 
Prune:
	hpray[0] = hpray[1] = hpray[2] = NULL;
	for (hp = hlp->hp; hp != NULL; hp = hp->next) {
		sgn = SIGN(hp->frame) + 1;
		switch (which) {
		case SCORET_EXPECT:
			if (hp->expect > cutoff)
				continue;
			break;
		case SCORET_PVALUE:
			if (hp->P_val > cutoff)
				continue;
			break;
		}
		/* save ptr to last HSP that satisfied the cutoff in each frame*/
		hpray[sgn] = hp;
	}
	if (hpray[0] == NULL && hpray[1] == NULL && hpray[2] == NULL) {
		HSPPoolReturnList(poolp, hlp->hp);
		hlp->hp = hlp->signifhsp = NULL;
		hlp->hspcnt = 0;
		return;
	}

	npruned = 0;
	for (hp = hlp->hp, hp2 = hplast = NULL; hp != NULL; hp = hp2) {
		hp2 = hp->next;
		sgn = SIGN(hp->frame) + 1;
		if (hpray[sgn] == NULL) {
			++npruned;
			HSPPoolReturn(poolp, hp);
			if (hplast != NULL)
				hplast->next = hp2;
			else
				hlp->hp = hp2;
		}
		else {
			hplast = hp;
			if (hpray[sgn] == hp)
				hpray[sgn] = NULL; /* all subsequent HSPs will be discarded */
		}
	}
	hlp->hspcnt -= npruned;
	return;
}
