#include <exec/memory.h>
#include <exec/nodes.h>
#include <exec/lists.h>
#include <proto/exec.h>
#include <proto/dos.h>

#include "rn.h"

int Setbit(struct groupnode *group,int number)
{
	struct Range *cur=(struct Range *)(group->ReadRanges.lh_Head),*tmp;

	if(!cur->node.ln_Succ)
	{
		/* No nodes */

		if(tmp=(struct Range *)AllocMem(sizeof(struct Range), MEMF_CLEAR))
		{
			/* Add new node */
			tmp->low = tmp->high = 0;
			if(number>0)
				tmp->high = number;
			AddHead(&group->ReadRanges,(struct Node *)tmp);
			if(group->unread && number>=group->lowest && number<=group->highest)
			{
				group->unread--;
			}
			return -1;	/* Yes, it was set */
		}
		return 0;
	}

	if(number>group->highest || number<group->lowest)
		return 0;

	if(number==cur->low-1)
	{
		cur->low--;
		if(group->unread && number>=group->lowest)
		{
			group->unread--;
		}
		return -1;
	}

	if(number<cur->low-1)
	{
		if(tmp=(struct Range *)AllocMem(sizeof(struct Range),MEMF_CLEAR))
		{
			/* Add new node */
			tmp->low=number;
			tmp->high=number;
			AddHead(&group->ReadRanges,(struct Node *)tmp);
			if(group->unread)
			{
				group->unread--;
			}
			return -1;
		}
		return 0;
	}

	while(cur->node.ln_Succ->ln_Succ)
	{
		if(number>=cur->low && number<=cur->high)
		{
			return 0;
		}

		if(number==cur->high+1)	/* XXx */
		{
			cur->high++;
			if(((struct Range *)(cur->node.ln_Succ))->low==cur->high+1) /* XXxXX */
			{
				/* Merge two nodes */
				cur->high=((struct Range *)(cur->node.ln_Succ))->high;
				tmp=(struct Range *)(cur->node.ln_Succ);
				Remove((struct Node *)tmp);
				FreeMem(tmp,sizeof(struct Range));
			}
			if(group->unread)
				group->unread--;
			return -1;
		}
		if(number==((struct Range *)(cur->node.ln_Succ))->low-1) /* xXX */
		{
			((struct Range *)(cur->node.ln_Succ))->low--;
			if(group->unread)
				group->unread--;
			return -1;
		}
		if(number>cur->high+1 && number<((struct Range *)(cur->node.ln_Succ))->low-1) /* XX x XX */
		{
			if(tmp=(struct Range *)AllocMem(sizeof(struct Range),MEMF_CLEAR))
			{
				/* Add new node */
				tmp->low=number;
				tmp->high=number;
				Insert(&group->ReadRanges,(struct Node *)tmp,(struct Node *)cur);
				if(group->unread)
					group->unread--;
				return -1;
			}
			return 0;
		}
		cur=(struct Range *)(cur->node.ln_Succ);
	}
	if(number>=cur->low && number<=cur->high)
	{
		return 0;
	}
	if(number==cur->high+1)
	{
		cur->high++;
		if(group->unread)
			group->unread--;
		return -1;
	}

	if(tmp=(struct Range *)AllocMem(sizeof(struct Range),MEMF_CLEAR))
	{
		/* Add new node */
		tmp->low=number;
		tmp->high=number;
		AddTail(&group->ReadRanges,(struct Node *)tmp);
		if(group->unread)
			group->unread--;
		return -1;
	}
	return 0;
}

int Clearbit(struct groupnode *group,int number)
{	struct Range *cur=(struct Range *)(group->ReadRanges.lh_Head),*tmp;

	/* No ranges */
	if(!cur->node.ln_Succ)
		return 0;

	if(number<group->lowest || number>group->highest || number<cur->low)
		return 0;

	while(cur->node.ln_Succ)
	{
		if(cur->low==cur->high && number==cur->low)
		{
			Remove((struct Node *)cur);
			FreeMem(cur,sizeof(struct Range));
			group->unread++;
			return -1;
		}

		if(number==cur->low)
		{
			cur->low++;
			group->unread++;
			return -1;
		}
		if(number==cur->high)
		{
			cur->high--;
			group->unread++;
			return -1;
		}
		if(number>cur->low && number<cur->high)
		{
			if(tmp=(struct Range *)AllocMem(sizeof(struct Range),MEMF_CLEAR))
			{
				/* Seperate the range */
				tmp->low=number+1;
				tmp->high=cur->high;
				cur->high=number-1;
				Insert(&group->ReadRanges,(struct Node *)tmp,(struct Node *)cur);
				group->unread++;
				return -1;
			}
			return 0;
		}
		cur=(struct Range *)(cur->node.ln_Succ);
	}
	return 0;
}


int Testbit(struct groupnode *group,int number)
{	struct Range *cur=(struct Range *)(group->ReadRanges.lh_Head);

	/* No ranges */
	if(!cur->node.ln_Succ)
		return 0;

	if(number<group->lowest || number>group->highest || number<cur->low)
		return 0;

	while(cur->node.ln_Succ)
	{
		if(number>=cur->low && number<=cur->high)
		{
			return -1;
		}
		cur=(struct Range *)(cur->node.ln_Succ);
	}
	return 0;
}


void Setallbits(struct groupnode *group,int leave)
{	struct Range *cur,*tmp;

	/*
		Strategy:
			free all ranges except one (if available) and then
			use low=lowest, high=highest
	 */

	cur=(struct Range *)RemHead(&group->ReadRanges);
	while(tmp=(struct Range *)RemHead(&group->ReadRanges))
	{
		FreeMem(tmp,sizeof(struct Range));
	}
	if(cur || (cur=(struct Range *)AllocMem(sizeof(struct Range),MEMF_CLEAR)))
	{
		cur->low=0;
		cur->high=group->highest-leave;
		if(cur->high < group->lowest)
			cur->high = group->lowest-1;
		AddHead(&group->ReadRanges,(struct Node *)cur);
		group->unread = group->highest - cur->high;
	}
}

int Countbits(struct groupnode *group)
{	struct Range *cur=(struct Range *)(group->ReadRanges.lh_Head),*tmp;
	int unset=0;

	/* No ranges */
	if(!cur->node.ln_Succ)
	{
		unset = group->highest - group->lowest + 1;
		if(unset < 0)
		{
			unset = 0;
		}
		return unset;
	}

	if(group->lowest>=group->highest)
		return 0;

	/* AH! some cleanup possible */
	while(cur->high<group->lowest-1)
	{
		if(cur->node.ln_Succ->ln_Succ && ((struct Range *)cur->node.ln_Succ)->low<=group->lowest)
		{	/* merge two lowest nodes */
			cur->high = ((struct Range *)(cur->node.ln_Succ))->high;
			tmp = (struct Range *)(cur->node.ln_Succ);
			Remove((struct Node *)tmp);
			FreeMem(tmp,sizeof(struct Range));
		}
		else
		{
			cur->high = group->lowest-1;
		}
	}

	if(group->lowest < cur->low)
		group->lowest = cur->low;

	if(cur->low > group->lowest)
		unset = cur->low - group->lowest;
	while(cur->node.ln_Succ->ln_Succ)
	{
		unset += ((struct Range *)cur->node.ln_Succ)->low - cur->high - 1;
		cur = (struct Range *)(cur->node.ln_Succ);
	}
	unset += group->highest - cur->high;

	if(unset<0)
	{
		Write(Output(),"Strange: negative unread articles\n", 34);
		unset = 0;
	}
	return unset;
}


void Freebits(struct groupnode *group)
{	struct Range *cur;

	while(cur=(struct Range *)RemHead(&group->ReadRanges))
	{
		FreeMem(cur,sizeof(struct Range));
	}
}


int Findnextbit(struct groupnode *group,int number)
{	struct Range *cur=(struct Range *)(group->ReadRanges.lh_Head);

	if(number>group->highest)
		return Findnextbit(group,group->lowest);

	if(!cur->node.ln_Succ)
	{
		if(number<=group->highest)
			return number;
		else
			return group->lowest;
	}

	if(number<cur->low)
	{
		if(number>=group->lowest)
			return number;
		else
			return group->lowest;
	}

	while(cur->node.ln_Succ)
	{
		if(number>=cur->low && number<=cur->high)
		{
			if(cur->high<group->highest)
				return cur->high+1;
			else
			{
				if(group->unread)
					return Findnextbit(group,group->lowest);
				else
					return group->highest+1;
			}
			/* No unread after "number" */
		}
		cur=(struct Range *)(cur->node.ln_Succ);
	}
	return number;
}


int Findprevbit(struct groupnode *group,int number)
{	struct Range *cur=(struct Range *)(group->ReadRanges.lh_TailPred);

	if(number<group->lowest)
		return Findprevbit(group,group->highest);

	if(!cur->node.ln_Pred)
	{
		if(number>=group->lowest)
			return number;
		else
			return group->highest;
	}

	if(number-1>cur->high)
	{
		if(number<=group->highest)
			return number;
		else
			return group->highest;
	}

	while(cur->node.ln_Pred)
	{
		if(number>=cur->low && number<=cur->high)
		{
			if(cur->low>group->lowest)
				return cur->low-1;
			else
			{
				if(group->unread)
					return Findprevbit(group,group->highest);
				else
					return group->lowest-1;
			}
			/* No unread after "number" */
		}
		cur=(struct Range *)(cur->node.ln_Pred);
	}
	return number;
}

