#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <exec/types.h>
#include <exec/memory.h>
#include <exec/nodes.h>
#include <exec/lists.h>
#include <proto/dos.h>
#include <proto/exec.h>

#include "rn.h"
#include "rnlanguage.h"

void write_newsrc(char *name,struct List *grouplist)
{	long handle;
	struct groupnode *node=(struct groupnode *)grouplist->lh_Head;
	struct Range *cur;
	char temp[20];

	if(handle=Open(name,MODE_NEWFILE))
	{
		while(node->Node.ln_Succ)
		{
			cur=(struct Range *)node->ReadRanges.lh_Head;
			Write(handle,node->name,strlen(node->name));
			strcpy(temp,": ");
			if(node->flags & FLAG_UNSUBSCRIBED)
			{
				strcpy(temp,"! ");
			}
			if(!cur->node.ln_Succ)
				Write(handle,temp,2);
			while(cur->node.ln_Succ)
			{
				Write(handle,temp,strlen(temp));	/* write : or , */
				if(cur->low==cur->high)
					sprintf(temp,"%ld",cur->low);
				else
					sprintf(temp,"%ld-%ld",cur->low,cur->high);
				Write(handle,temp,strlen(temp));	/* write <num> or <num>-<num> */
				strcpy(temp,",");
				cur=(struct Range *)cur->node.ln_Succ;
			}
			Write(handle,"\n",1);
			node=(struct groupnode *)node->Node.ln_Succ;
		}
		Close(handle);
	}
	else
		printf(printout(MSG_NOT_WRITTEN),name);
}


void read_newsrc(char *name,struct List *grouplist)
{	FILE *fp;
	int i,j;
	long low,high,highest;
	struct groupnode *node;
	struct Range *cur;
	char temp[256];

	free_newsrc(grouplist);
	if(fp=fopen(name,"rb"))
	{
		while(!feof(fp))
		{
			if(fgets(temp,250,fp)==0)
			{
				break;
			}

			i=0;
			while(temp[i] && temp[i]!=':' && temp[i]!='!')
				i++;
			if(temp[i] && (node=(struct groupnode *)AllocMem(sizeof(struct groupnode),MEMF_CLEAR)))
			{
				NewList(&node->ReadRanges);
				strncpy(node->name,temp,i);
				node->name[i]='\0';
				node->Node.ln_Name=node->name;	/* link name to node */
				if(temp[i]=='!')
					node->flags=FLAG_UNSUBSCRIBED;

				highest=-1;
				i++;
				while(temp[i])
				{
					if(temp[i]==',')
						i++;
					if(!temp[i])
						break;

					low=high=atol(temp+i);
					while(temp[i] && temp[i]!=',' && temp[i]!='-' && temp[i]!='\n')
						i++;

/* see if the line is cut and we are in the last "," in that line */
					if(temp[i]==',' && temp[j=strlen(temp)-1]!='\n')
					{
						while(j && temp[j]!=',')
							j--;
						if(i==j)
						{
							strcpy(temp,temp+j);
							j=strlen(temp);
							i=0;
							if(fgets(temp+j,250-j,fp)==0)
							{
								break;
							}
							continue;
						}
					}

					if(temp[i]=='\n')
					{
						if(cur=(struct Range *)AllocMem(sizeof(struct Range),MEMF_CLEAR))
						{
							cur->high=cur->low=low;
							AddTail(&node->ReadRanges,(struct Node *)cur);
						}
						else
							printf(printout(MSG_MEM_ALLOC_ERR_2));
						break;
					}
					if(temp[i]=='-')
					{
						i++;
						high=atol(temp+i);
						while(temp[i] && temp[i]!=',' && temp[i]!='\n')
							i++;

/* see if the line is cut and we are in the last "," in that line */
						if(temp[i]==',' && temp[j=strlen(temp)-1]!='\n')
						{
							while(j && temp[j]!=',')
								j--;
							if(i==j)
							{
								strcpy(temp,temp+j);
								j=strlen(temp);
								i=0;
								if(fgets(temp+j,250-j,fp)==0)
								{
									break;
								}
								continue;
							}
						}
					}

					if(low>highest && high>=low)
					{
						if(cur=(struct Range *)AllocMem(sizeof(struct Range),MEMF_CLEAR))
						{
							cur->low=low;
							cur->high=high;
							AddTail(&node->ReadRanges,(struct Node *)cur);
						}
						else
							printf(printout(MSG_MEM_ALLOC_ERR_2));
						highest=cur->high;
					}
					else
						printf("Warning! Numbers in %s are not sorted!\n",name);
					if(temp[i]=='\n')
						break;
				}
				AddTail(grouplist,(struct Node *)node);
			}
		}
		fclose(fp);
	}
	else
		printf(printout(MSG_CANT_OPEN),name);
}


void free_newsrc(struct List *grouplist)
{	struct Node *node;

    while(node=RemTail(grouplist))
    {
		Freebits((struct groupnode *)node);
		FreeMem((char *)node,sizeof(struct groupnode));
    }
}


void check_unread(char *Active,char *Newsgroupfile,struct List *grouplist,struct RnPrefs *prefs)
{	int i,j;
	struct groupnode *n,*node;
	ULONG expire,flags,low,high;
	FILE *fp;
	char temp[100],groupname[80];
	LONG lock;

	if(!strcmp(Active,"none"))
		Active=NULL;

	if(Active)
	{
		if(fp=fopen(Active,"rb"))
		{
			/* First check new groups and update message numbers */

			while(!feof(fp))
			{
				if(fgets(temp,80,fp)==0)
					break;

				if((i=strlen(temp))<2)
					continue;	/* skip empty lines */

				temp[i-1]=0;
				i=0;
				while(temp[i] && temp[i]!=' ' && temp[i]!='\t')
					i++;

				strncpy(groupname,temp,i);
				groupname[i]=0;

				while(temp[i] && (temp[i]=='\t' || temp[i]==' '))
					i++;
				high=atol(temp+i);

				while(temp[i] && temp[i]!=' ' && temp[i]!='\t')
					i++;
				while(temp[i] && (temp[i]=='\t' || temp[i]==' '))
					i++;
				low=atol(temp+i);
				if(low > high)
				{
					low = high-1;
				}

				if(!(node=search_group(grouplist,groupname)))
				{
					if(high && (low!=high))
					{
						if(node=(struct groupnode *)AllocMem(sizeof(struct groupnode),MEMF_CLEAR))
						{
							NewList(&node->ReadRanges);
							AddTail(grouplist,(struct Node *)node);
							node->expire=0;
							node->flags=FLAG_CHECKED;
							node->Node.ln_Name=node->name;	/* link node to name */
							strcpy(node->name,groupname);

							printf(printout(MSG_NEW_GROUP_ADDED),prefs->Groupname,node->name,prefs->Normal);

							printf("Checking: %s\n",groupname);
							node->lowest=findlowest(groupname,high,low);
							node->highest=high;
						}
						else
							printf(printout(MSG_MEM_ALLOC_ERR),sizeof(struct groupnode));
					}
				}
				else
				{
					if(!low && high-node->lowest>510)
					{
						printf("Checking: %s\n",groupname);
						node->lowest=findlowest(groupname,high,low);
					}
					/* If we don't have to check, do not check, use the old lowest */
					node->highest=high;

					node->expire=0;
					node->flags |= FLAG_CHECKED;
				}
			}
			fclose(fp);
		}

		/* Then check that all the groups exist */

		node=(struct groupnode *)grouplist->lh_Head;
		while(node->Node.ln_Succ)
		{
			if(!(node->flags & FLAG_CHECKED))
			{
				articlegroup(temp,node->name);
				if(lock=Lock(temp,ACCESS_READ))
				{
					UnLock(lock);
					node->flags &= ~FLAG_CHECKED;
					node=(struct groupnode *)node->Node.ln_Succ;
				}
				else
				{
					printf(printout(MSG_NON_EXISTENT_REMOVED),prefs->Groupname,node->name,prefs->Normal);
					n=(struct groupnode *)node->Node.ln_Succ;
					Remove((struct Node *)node);
					FreeMem((struct Node *)node,sizeof(struct groupnode));
					node=n;
				}
			}
			else
			{
				node->flags&=~FLAG_CHECKED;
				node=(struct groupnode *)node->Node.ln_Succ;
			}
		}
	}
	else
	{
		if(fp=fopen(Newsgroupfile,"rb"))
		{
			/* First check new groups and update message numbers */

			while(!feof(fp))
			{
				if(fgets(temp,80,fp)==0)
					break;

				if((i=strlen(temp))<2)
					continue;	/* skip empty lines */

				temp[i-1]=0;
				i=0;
				while(temp[i] && temp[i]!=' ' && temp[i]!='\t')
					i++;

				strncpy(groupname,temp,i);
				groupname[i]=0;

				expire=60;
				flags=0;
				if(temp[i])
				{
					while(temp[i]=='\t' || temp[i]==' ')
						i++;
					expire=atol(temp+i);

					while(temp[i] && temp[i]!=' ')
						i++;

					while(temp[i])
					{
						switch(temp[i++])
						{
						case 'm':
							flags|=FLAG_MODERATED;
							break;
						case 'r':
							flags|=FLAG_READ_ONLY;
							break;
						default:
							;
							break;
						}
					}
				}

				if(!(node=search_group(grouplist,groupname)))
				{
					if(is_group(groupname))
					{
						if(node=(struct groupnode *)AllocMem(sizeof(struct groupnode),MEMF_CLEAR))
						{
							NewList(&node->ReadRanges);
							AddTail(grouplist,(struct Node *)node);
							node->expire=expire;
							node->flags=flags;
							strcpy(node->name,groupname);
							node->Node.ln_Name=node->name;	/* link node to name */

							node->highest=get_next(node->name)-1;
							node->lowest=get_lowest(node->name,node->highest);
							Setbit(node,node->lowest);
							node->flags|=FLAG_CHECKED;

							printf(printout(MSG_NEW_GROUP_ADDED),prefs->Groupname,node->name,prefs->Normal);
						}
						else
							printf(printout(MSG_MEM_ALLOC_ERR),sizeof(struct groupnode));
					}
				}
				else
				{
					node->expire=expire;
					node->flags=(node->flags & FLAG_UNSUBSCRIBED) | flags | FLAG_CHECKED;
					if(!(node->flags & FLAG_UNSUBSCRIBED))
					{
						node->highest=get_next(node->name)-1;
						if(!node->lowest || node->highest-node->lowest>500)
							node->lowest=get_lowest(node->name,node->highest);
					}
				}
			}
			fclose(fp);
		}

		/* Then remove groups that does not exist */

		node=(struct groupnode *)grouplist->lh_Head;
		while(node->Node.ln_Succ)
		{
			if(!(node->flags & FLAG_CHECKED))
			{
				printf(printout(MSG_NON_EXISTENT_REMOVED),prefs->Groupname,node->name,prefs->Normal);
				n=(struct groupnode *)node->Node.ln_Succ;
				Remove((struct Node *)node);
				FreeMem((struct Node *)node,sizeof(struct groupnode));
				node=n;
			}
			else
				node=(struct groupnode *)node->Node.ln_Succ;
		}
	}

	printf("\n");

	/* Count unread messages from subscribed groups and show them */

	j=0;
	node=(struct groupnode *)grouplist->lh_Head;
	while(node->Node.ln_Succ)
	{
		if(!(node->flags & FLAG_UNSUBSCRIBED))
		{
			if(node->unread=Countbits(node))
			{
				if(++j<6)
					printf(printout(MSG_UNREAD_ARTICLES_IN),node->unread,prefs->Groupname,node->name,prefs->Normal);
			}
		}
		node=(struct groupnode *)node->Node.ln_Succ;
	}
	if(j>5)
		printf(printout(MSG_ETC));

	printf("\n");
}

int checkgroup(char *Active,struct groupnode *node)
{	FILE *fp;
	char temp[100];

	if(strcmp(Active,"none"))
	{
		return 0;
	}

	articlegroup(temp,node->name);
	strcat(temp,"/.next");
	if(fp=fopen(temp,"rb"))
	{
		fgets(temp,80,fp);
		fclose(fp);
		node->highest=atol(temp)-1;
		node->lowest=get_lowest(node->name,node->highest);

		if(node->highest<node->lowest)
		{
			node->highest=node->lowest;
		}
		node->unread=Countbits(node);
		return 0;
	}
	else
		return -1;
}


void sort_newsrc(struct List *grouplist)	/* this is an unefficient sorter, but no big deal */
{	int changed=-1;
	struct groupnode *node;
	struct Node *n;

	printf(printout(MSG_SORTING_NEWSGROUPS));

	while(changed)
	{
		changed=0;

		node=(struct groupnode *)grouplist->lh_Head;
		if(node && node->Node.ln_Succ)
		{
			while(node->Node.ln_Succ->ln_Succ)
			{
				if(strcmp(node->name,((struct groupnode *)(node->Node.ln_Succ))->name)>0)
				{
					n=node->Node.ln_Succ;
					(void)Remove((struct Node *)node);
					Insert(NULL,(struct Node *)node,n);
					changed=-1;
				}
				else
					node=(struct groupnode *)node->Node.ln_Succ;
			}
		}
	}
}

struct groupnode *search_group(struct List *grouplist,char *name)
{
	return (struct groupnode *)FindName(grouplist,name);
}


struct groupnode *get_group(int direction,int force,struct groupnode *start)
{	struct groupnode *node=start;

	if(direction>0)
	{
		if(force)
		{
			if(!node->Node.ln_Succ->ln_Succ)
				return NULL;

			node=(struct groupnode *)node->Node.ln_Succ;
			while(node->Node.ln_Succ)
			{
				if(!(node->flags & FLAG_UNSUBSCRIBED))
					return node;
				node=(struct groupnode *)node->Node.ln_Succ;
			}
			return NULL;
		}

		while(node->Node.ln_Succ)
		{
			if(!(node->flags & FLAG_UNSUBSCRIBED) && node->unread)
				return node;
			node=(struct groupnode *)node->Node.ln_Succ;
		}
		return NULL;
	}
	else
	{
		if(force)
		{
			if(!node->Node.ln_Pred->ln_Pred)
				return NULL;

			node=(struct groupnode *)node->Node.ln_Pred;
			while(node->Node.ln_Pred)
			{
				if(!(node->flags & FLAG_UNSUBSCRIBED))
					return node;
				node=(struct groupnode *)node->Node.ln_Pred;
			}
			return NULL;
		}

		while(node->Node.ln_Pred)
		{
			if(!(node->flags & FLAG_UNSUBSCRIBED) && node->unread)
				return node;
			node=(struct groupnode *)node->Node.ln_Pred;
		}
		return NULL;
	}
}

