#include <ncbi.h>
#include <blast/blast.h>

BLAST_HitListPtr LIBCALL
BlastHitListSave(hlp)
	BLAST_HitListPtr	hlp;
{
	BLAST_HitListPtr	new_hlp;
	BLAST_StrPtr	sp;
	BLAST_StrPoolPtr	spp;
	BLAST_HSPPtr	hp;
	int	i;

	new_hlp = BlastHitListGet(&hlp->pdata);
	if (new_hlp == NULL)
		return NULL;
	*new_hlp = *hlp;
	new_hlp->hpp = &new_hlp->hplist[hlp->hpp - &hlp->hplist[0]];

	spp = new_hlp->pdata.str_pool;
	for (i = 0; i < DIM(hlp->hplist); ++i) {
		if ((hp = hlp->hplist[i]) == NULL)
			continue;
		hlp->hplist[i] = NULL;
		for (; hp != NULL; hp = hp->next) {
			if (hp->s_seg.sp == &hlp->str2 || hp->s_seg.sp->src == NULL)
				hp->s_seg.sp = &new_hlp->str2;
			else {
				sp = BlastStrPoolGet(spp);
				*sp = *(hp->s_seg.sp);
				sp->locked = FALSE;
				sp->str = sp->_str = NULL;
				sp->alloclen = 0;
				hp->s_seg.sp = sp;
				sp->src = &new_hlp->str2;
			}
		}
	}

	MemSet((CharPtr)hlp, 0, sizeof(*hlp));
	hlp->hpp = &hlp->hplist[0];
	hlp->pdata = new_hlp->pdata;
	hlp->user = new_hlp->user;

	return new_hlp;
}

BLAST_HitListPDataPtr LIBCALL
BlastHitListPDataNew(cnt)
	size_t	cnt;
{
	BLAST_HitListPDataPtr	pdp;

	if (cnt == 0)
		cnt = 100;
	cnt = MIN(cnt, 1000);
	cnt = MAX(cnt, 1);
	pdp = BlastCalloc(sizeof(*pdp));
	if (pdp == NULL)
		return NULL;

	pdp->hitlist_pool = BlastHLPoolNew(cnt);
	pdp->hsp_pool = BlastHSPPoolNew(2*cnt + 50);
	pdp->vscore_pool = BlastVScorePoolNew(2*cnt + 50);
	pdp->str_pool = BlastStrPoolNew(2*cnt + 50);
	if (pdp->hitlist_pool == NULL || pdp->hsp_pool == NULL || pdp->vscore_pool == NULL || pdp->str_pool == NULL) {
		BlastHitListPDataDestruct(pdp);
		return NULL;
	}
	return pdp;
}

void LIBCALL
BlastHitListPDataFree(pdp)
	BLAST_HitListPDataPtr	pdp;
{
	if (pdp == NULL)
		return;
	BlastHitListPDataDestruct(pdp);
	BlastFree(pdp);
}

void LIBCALL
BlastHitListPDataDestruct(pdp)
	BLAST_HitListPDataPtr	pdp;
{
	if (pdp == NULL)
		return;
	if (pdp->hitlist_pool != NULL)
		BlastHLPoolDestruct(pdp->hitlist_pool);
	if (pdp->hsp_pool != NULL)
		BlastHSPPoolDestruct(pdp->hsp_pool);
	if (pdp->vscore_pool != NULL)
		BlastVScorePoolDestruct(pdp->vscore_pool);
	if (pdp->str_pool != NULL)
		BlastStrPoolDestruct(pdp->str_pool);
}

BLAST_HitListPtr LIBCALL
BlastHitListGet(pdp)
	BLAST_HitListPDataPtr	pdp;
{
	BLAST_HitListPtr	hlp;

	hlp = (BLAST_HitListPtr)PoolGetClr((PoolBlkPtr)pdp->hitlist_pool);
	if (hlp == NULL)
		return NULL;
	MemSet((CharPtr)hlp, 0, sizeof(*hlp));
	hlp->pdata = *pdp;
	hlp->hpp = &hlp->hplist[0];
	return hlp;
}

void LIBCALL
BlastHitListPut(hlp)
	BLAST_HitListPtr	hlp;
{
	if (hlp == NULL)
		return;
	BlastHitListHSPPutAll(hlp);
	PoolPutLink((PoolBlkPtr)hlp->pdata.vscore_pool, hlp->vsp);
	PoolPut((PoolBlkPtr)hlp->pdata.hitlist_pool, hlp);
}

int LIBCALL
BlastHitListCount(hlp)
	register BLAST_HitListPtr	hlp;
{
	register int	cnt = 0;

	while (hlp != NULL) {
		++cnt;
		hlp = hlp->next;
	}
	return cnt;
}

void LIBCALL
BlastHitListHSPPutAll(hlp)
	BLAST_HitListPtr	hlp;
{
	BLAST_HSPPtr	hp;
	int		i;

	if (hlp == NULL)
		return;

	for (i = 0; i < DIM(hlp->hplist); ++i) {
		if ((hp = hlp->hplist[i]) == NULL)
			continue;
		hlp->hplist[i] = NULL;
		if (hp->vsp != NULL)
			PoolPutLink(hlp->pdata.vscore_pool, hp->vsp);
		BlastStrDestruct(hp->q_seg.sp);
		BlastStrDestruct(hp->s_seg.sp);
		PoolPutLink(hlp->pdata.hsp_pool, hp);
	}
	hlp->hpp = &hlp->hplist[0];
	return;
}

void LIBCALL
BlastHitListTruncate(hlpp, cnt, func, arg1)
	BLAST_HitListPtr	PNTR hlpp;
	int		cnt;
	void 	(*func)();
	VoidPtr	arg1;
{
	register BLAST_HitListPtr	hlp, hlp2;

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

	if (cnt <= 0)
		*hlpp = NULL;

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

	hlp2 = hlp->next;
	hlp->next = NULL;
	while (hlp2 != NULL) {
		hlp = hlp2;
		hlp2 = hlp->next;
		if (func != NULL)
			(*func)(arg1, hlp);
		BlastHitListPut(hlp);
	}
}


/* BlastHitListTruncateByEValue
   Truncate the HitList after the last satisfactory HSP
   in each frame-equivalence class.
*/
void LIBCALL
BlastHitListTruncateByEValue(hlp, cutoff)
	BLAST_HitListPtr  hlp;
	register double cutoff;
{
	register BLAST_HSPPtr  hp, hp2 = NULL, hplast;
	BLAST_HSPPtr	hpray[9];
	register int		qsign, ssign, index;
 
	if (hlp == NULL)
		return;
 
	if (hlp->best_hsp->evalue > cutoff) {
		BlastHSPPutLink(hlp);
		return;
	}

	/*
	There is one element in hpray[] for every strand combination.
	{-1, 0, +1} X {-1, 0, +1} = 9 combinations
	*/
	hpray[0] = hpray[1] = hpray[2] =
	hpray[3] = hpray[4] = hpray[5] =
	hpray[6] = hpray[7] = hpray[8] = NULL;

	for (hp = *hlp->hpp; hp != NULL; hp = hp->next) {
		if (hp->evalue > cutoff)
			continue;
		/* save ptr to last HSP that satisfied the cutoff in each frame*/
		qsign = SIGN(hp->q_seg.frame);
		ssign = SIGN(hp->s_seg.frame);
		index = (qsign + 1) * 3 + ssign + 1;
		hpray[index] = hp;
	}

	for (hp = *hlp->hpp, hplast = NULL; hp != NULL; hp = hp2) {
		hp2 = hp->next;
		qsign = SIGN(hp->q_seg.frame);
		ssign = SIGN(hp->s_seg.frame);
		index = (qsign + 1) * 3 + ssign + 1;
		if (hpray[index] == NULL) {
			BlastHSPPut(hp);
			if (hplast != NULL)
				hplast->next = hp2;
			else
				*hlp->hpp = hp2;
			continue;
		}
		hplast = hp;
		if (hpray[index] == hp)
			hpray[index] = NULL; /* all subsequent HSPs will be discarded */
	}
	return;
}

/* BlastHitListTruncateByCutoff
   Truncate the HitList after the last satisfactory HSP
   in each frame-equivalence class.
*/
void LIBCALL
BlastHitListTruncateByCutoff(hlp, which, cutoff)
	BLAST_HitListPtr  hlp;
	int		which;
	double cutoff;
{
	register BLAST_HSPPtr  hp, hp2 = NULL, hplast;
	BLAST_HSPPtr	hpray[9];
	register int	qsign, ssign, index;
 
	switch (which) {
	case BLAST_HSPSCORE_EVALUE:
		if (hlp->best_hsp->evalue <= cutoff)
			goto Truncate;
		break;
	case BLAST_HSPSCORE_PVALUE:
		if (hlp->best_hsp->pvalue <= cutoff)
			goto Truncate;
		break;
	default:
		break;
	}
	/* none satisfies... dump any and all HSPs */
	BlastHSPPutLink(hlp);
	return;
 
	/*
	There is one element in hpray[] for every strand combination.
	{-1, 0, +1} X {-1, 0, +1} = 9 combinations
	*/
Truncate:
	hpray[0] = hpray[1] = hpray[2] =
	hpray[3] = hpray[4] = hpray[5] =
	hpray[6] = hpray[7] = hpray[8] = NULL;

	for (hp = *hlp->hpp; hp != NULL; hp = hp->next) {
		switch (which) {
		case BLAST_HSPSCORE_EVALUE:
			if (hp->evalue > cutoff)
				continue;
			break;
		case BLAST_HSPSCORE_PVALUE:
			if (hp->pvalue > cutoff)
				continue;
			break;
		default:
			continue;
		}
		/* save ptr to last HSP that satisfied the cutoff in each frame*/
		qsign = SIGN(hp->q_seg.frame);
		ssign = SIGN(hp->s_seg.frame);
		index = (qsign + 1) * 3 + ssign + 1;
		hpray[index] = hp;
	}

	for (hp = *hlp->hpp, hplast = NULL; hp != NULL; hp = hp2) {
		hp2 = hp->next;
		qsign = SIGN(hp->q_seg.frame);
		ssign = SIGN(hp->s_seg.frame);
		index = (qsign + 1) * 3 + ssign + 1;
		if (hpray[index] == NULL) {
			BlastHSPPut(hp);
			if (hplast != NULL)
				hplast->next = hp2;
			else
				*hlp->hpp = hp2;
			continue;
		}
		hplast = hp;
		if (hpray[index] == hp)
			hpray[index] = NULL; /* all subsequent HSPs will be discarded */
	}
	return;
}

/* BlastHitListPruneByCutoff
   Prune away any and all HSPs that don't satisfy the cutoff.
*/
void LIBCALL
BlastHitListPruneByCutoff(hlp, which, cutoff)
	BLAST_HitListPtr  hlp;
	int		which;
	double cutoff;
{
	register BLAST_HSPPtr  hp, hp2 = NULL, hplast;

	switch (which) {
	case BLAST_HSPSCORE_EVALUE:
		if (hlp->best_hsp->evalue <= cutoff)
			goto Prune;
		break;
	case BLAST_HSPSCORE_PVALUE:
		if (hlp->best_hsp->pvalue <= cutoff)
			goto Prune;
		break;
	default:
		break;
	}
	/* none satisfies... dump any and all HSPs */
	BlastHSPPutLink(hlp);
	return;

Prune:
	for (hp = *hlp->hpp, hplast = NULL; hp != NULL; hp = hp2) {
		hp2 = hp->next;
		switch (which) {
		case BLAST_HSPSCORE_EVALUE:
			if (hp->evalue <= cutoff) {
				hplast = hp;
				continue;
			}
			break;
		case BLAST_HSPSCORE_PVALUE:
			if (hp->pvalue <= cutoff) {
				hplast = hp;
				continue;
			}
			break;
		default:
			continue;
		}
		BlastHSPPut(hp);
		if (hplast != NULL)
			hplast->next = hp2;
		else
			*hlp->hpp = hp2;
	}
	return;
}

void LIBCALL
BlastHitListPruneLinksByCutoff(hlp, which, cutoff)
	BLAST_HitListPtr  hlp;
	int		which;
	double cutoff;
{
	register BLAST_HSPPtr	hp, hp2, hplast;

	switch (which) {
	case BLAST_HSPSCORE_EVALUE:
		if (hlp->best_hsp->evalue <= cutoff)
			goto Prune;
		break;
	case BLAST_HSPSCORE_PVALUE:
		if (hlp->best_hsp->pvalue <= cutoff)
			goto Prune;
		break;
	default:
		break;
	}
	/* none satisfies... dump any and all HSPs */
	BlastHSPPutLink(hlp);
	return;

Prune:
	for (hp = *hlp->hpp; hp != NULL; hp = hp->next) {
		hp->cd.flag = FALSE;
	}

	for (hp = *hlp->hpp; hp != NULL; hp = hp->next) {
		switch (which) {
		case BLAST_HSPSCORE_EVALUE:
			if (hp->evalue > cutoff)
				continue;
			break;
		case BLAST_HSPSCORE_PVALUE:
			if (hp->pvalue > cutoff)
				continue;
			break;
		default:
			continue;
		}
		hp->cd.flag = TRUE;
		for (hp2 = hp->fwdptr; hp2 != NULL; hp2 = hp2->fwdptr)
			hp2->cd.flag = TRUE;
		for (hp2 = hp->revptr; hp2 != NULL; hp2 = hp2->revptr)
			hp2->cd.flag = TRUE;
	}

	for (hp = *hlp->hpp, hplast = NULL; hp != NULL; hp = hp2) {
		hp2 = hp->next;
		if (hp->cd.flag) {
			hplast = hp;
			continue;
		}
		BlastHSPPut(hp);
		if (hplast != NULL)
			hplast->next = hp2;
		else
			*hlp->hpp = hp2;
	}
	return;
}

BLAST_HSPPtr LIBCALL
BlastHitListHSPHighestNormScore(hlp)
	BLAST_HitListPtr	hlp;
{
	BLAST_HSPPtr	hp, besthsp;

	/*
	Return HSP having the highest normalized score
	*/
	besthsp = hlp->best_hsp;

	for (hp = *hlp->hpp; hp != NULL; hp = hp->next) {
		if (BlastHSPCmpByNormScore(&hp, &besthsp) >= 0)
			continue;
		besthsp = hp;
	}

	return besthsp;
}

BLAST_HSPPtr LIBCALL
BlastHitListHSPStrandHighestNormScore(hlp)
	BLAST_HitListPtr	hlp;
{
	BLAST_HSPPtr	hp, besthsp;
	register int	qsign, ssign;

	/*
	Return HSP having the highest normalized score
	that is on the same strand as the "best" HSP.
	*/
	besthsp = hlp->best_hsp;
	qsign = SIGN(besthsp->q_seg.frame);
	ssign = SIGN(besthsp->s_seg.frame);

	for (hp = *hlp->hpp; hp != NULL; hp = hp->next) {
		if (SIGN(hp->q_seg.frame) != qsign || SIGN(hp->s_seg.frame) != ssign)
			continue;
		if (BlastHSPCmpByNormScore(&hp, &besthsp) >= 0)
			continue;
		besthsp = hp;
	}

	return besthsp;
}

BLAST_HitListPtr LIBCALL
BlastHLPoolSave(hlpp, poolp, hlp)
	BLAST_HitListPtr	PNTR hlpp;
	BLAST_HLPoolPtr	poolp;
	BLAST_HitListPtr	hlp;
{
	BLAST_HitListPtr	hlpnew;

	hlpnew = BlastHLPoolGet(poolp);
	if (hlpnew == NULL)
		return NULL;
	*hlpnew = *hlp;
	hlpnew->next = *hlpp;
	*hlpp = hlpnew;
	return hlpnew;
}

BLAST_HLPoolPtr LIBCALL
BlastHLPoolNew(cnt)
	size_t	cnt;
{
	return (BLAST_HLPoolPtr)PoolNew(cnt, sizeof(BLAST_HitList), offsetof(BLAST_HitList,next), NULL);
}

void LIBCALL
BlastHLPoolInit(poolp)
	BLAST_HLPoolPtr	poolp;
{
	PoolInit((PoolBlkPtr)poolp);
}

void LIBCALL
BlastHLPoolDestruct(poolp)
	BLAST_HLPoolPtr	poolp;
{
	PoolDestruct((PoolBlkPtr)poolp);
}


BLAST_HitListPtr LIBCALL
BlastHLPoolGet(hitlist_pool)
	register BLAST_HLPoolPtr	hitlist_pool;
{
	register BLAST_HitListPtr	hlp;

	hlp = (BLAST_HitListPtr)PoolGetClr((PoolBlkPtr)hitlist_pool);
	return hlp;
}

/* HLPoolPut -- put hlp back on the list of available HitList storage */
void LIBCALL
BlastHLPoolPut(poolp, hlp)
	register BLAST_HLPoolPtr	poolp;
	register BLAST_HitListPtr	hlp;
{
	PoolPut((PoolBlkPtr)poolp, (VoidPtr)hlp);
}

/* HLPoolPutLink -- return an entire HitList linked list to the free pool */
void LIBCALL
BlastHLPoolPutLink(poolp, hlpp)
	register BLAST_HLPoolPtr	poolp;
	register BLAST_HitListPtr	PNTR hlpp;
{
	PoolPutLink((PoolBlkPtr)poolp, (VoidPtr)*hlpp);
	*hlpp = NULL;
}

void LIBCALL
BlastHLPoolPutLinkApply(register BLAST_HLPoolPtr poolp,
			BLAST_HitListPtr PNTR hlpp, int (LIBCALLBACK *func)() )
{
	register BLAST_HitListPtr	hlp, hlp2;

	if (func == NULL) {
		BlastHLPoolPutLink(poolp, hlpp);
		return;
	}

	for (hlp = *hlpp; hlp != NULL; hlp = hlp2) {
		hlp2 = hlp->next;
		(void) (*func)(hlp);
		PoolPut((PoolBlkPtr)poolp, (VoidPtr)hlp);
	}
	*hlpp = NULL;
}
