/*
 *
 *  AM --- AmigaMail
 *  (C) 1991, 1992 by Christian Riede
 *
 *  AM is distributed in the hope that it will be useful, but WITHOUT ANY
 *  WARRANTY.  No author or distributor accepts responsibility to anyone
 *  for the consequences of using it or for whether it serves any
 *  particular purpose or works at all, unless he says so in writing.
 *  Refer to the GNU General Public License, Version 1, for full details.
 *  
 *  Everyone is granted permission to copy, modify and redistribute AM,
 *  but only under the conditions described in the GNU General Public
 *  License, Version 1.  A copy of this license is supposed to have been 
 *  given to you along with AM so you can know your rights and responsi-
 *  bilities.  It should be in a file named COPYING.  Among other things,
 *  the copyright notice and this notice must be preserved on all copies.
 *
 *  
 *
 */

#include "am.h"
#include "server.h"

#define PREALLOC 50

/* struct Strings must be initialized with 0 or allready been used */
static int InitStrings(struct Strings *Strings)
{
	/* allocate PREALLOC strings */
	if (!(Strings->HeaderStrings = realloc(Strings->HeaderStrings,
		(Strings->StringsAllocated=PREALLOC)*sizeof(char *))))
		return(FALSE);

	/* make the first string the null string */
	Strings->HeaderStrings[0]="";

	/* one string added */
	Strings->NumberOfStrings=1;

	return(TRUE);
}


/* struct Strings must be initialized with 0 or allready been used */
int ReadStrings(BPTR File,struct Strings *Strings)
{
	int i,number;
	USHORT len;

	if (Strings->NumberOfStrings>1)
		FreeStrings(Strings);

	if (!Strings->NumberOfStrings)
		if (!(InitStrings(Strings)))
			return(FALSE);

	/* read number of strings in file */
	if (FRead(File,(APTR)&number,1,sizeof(int))!=sizeof(int))
		return(FALSE);

	/* check if allocated memory is sufficient */
	if (Strings->NumberOfStrings + number > Strings->StringsAllocated)
	{
		/* if not, allocate it and PREALLOC more */
		Strings->HeaderStrings = realloc(Strings->HeaderStrings,
			sizeof(char *) * (Strings->StringsAllocated = 
			Strings->NumberOfStrings + number + PREALLOC));
	}

	/* read the strings */
	for (i=1;i<=number;++i) /* start at position 1 in HeaderStrings */
	{
		/* get length */
		if (FRead(File,(APTR)&len,sizeof(USHORT),1)!=1)
		{
			FreeStrings(Strings);
			return(FALSE);
		}

		/* allocate memory for the string (one more for trailing 0)*/
		if (!(Strings->HeaderStrings[i] = AllocMem(len+1,MEMF_CLEAR)))
		{
			FreeStrings(Strings);
			return(FALSE);
		}
		
		/* read the string */
		if (FRead(File,(APTR)Strings->HeaderStrings[i],sizeof(char),(int)len)
			!=len)
		{
			FreeStrings(Strings);
			return(FALSE);
		}
		
		/* insert 0 at the end */
		Strings->HeaderStrings[i][len]=0;
			
		/* we've got one more string */
		Strings->NumberOfStrings++;
	}

	return(TRUE);
}

int WriteStrings(BPTR File,struct Strings *Strings)
{
	int i;
	USHORT len;

	i=Strings->NumberOfStrings-1;

	if ((FWrite(File,(APTR)&i,sizeof(int),1))!=1)
		return(FALSE);

	/* don't write the first (empty) string */
	for (i=1;i<Strings->NumberOfStrings;++i) 
	{
		len = strlen(Strings->HeaderStrings[i]);
		
		/* write length */
		if ((FWrite(File,(APTR)&len,sizeof(USHORT),1))!=1)
			return(FALSE);
		
		/* write string */
		if (FWrite(File,(APTR)Strings->HeaderStrings[i],sizeof(char),(int)len)!=len)
			return(FALSE);
	}

	return(TRUE);
}


void FreeStrings(struct Strings *Strings)
{
	int i;

	for (i=1;i<Strings->NumberOfStrings;++i) /* dont free the first string */
		FreeMem(Strings->HeaderStrings[i],strlen(Strings->HeaderStrings[i])+1);
		                                          /* one more for trailing 0 */

	Strings->NumberOfStrings = 1;

	Strings->HeaderStrings = realloc(Strings->HeaderStrings, 
		(Strings->StringsAllocated = 1)*sizeof(char *));
}


char *GetString(int Number,struct Strings *Strings)
{
	if (!Strings->NumberOfStrings) /* no strings yet recorded */
		if(!(InitStrings(Strings)))
			return("");

	if (Number>=0 && Number<Strings->NumberOfStrings)
		return(Strings->HeaderStrings[Number]);
	else
		return("");
}

/* look up number of string */ 
int GetNumber(char *s,int doinsert,struct Strings *Strings)
{
	int upper,lower,mid;
	int i,cmp;

	if (!Strings->NumberOfStrings) /* no strings yet recorded */
		if(!(InitStrings(Strings)))
			return(-1);

	if (!*s) return(0);

	lower = 0; /* first string is null string */
	upper = Strings->NumberOfStrings;

	while (lower+1!=upper)
	{
		mid = (lower + upper) / 2;

		cmp = strcmp(Strings->HeaderStrings[mid],s);

		if (cmp<0)
			lower = mid;
		else if (cmp>0)
			upper = mid;
		else /* got it */
			return(mid);
	}

	/* not found */
	if (!doinsert)
		return(-1);
	else
	{
		if (Strings->StringsAllocated<Strings->NumberOfStrings+1)
			Strings->HeaderStrings = realloc(Strings->HeaderStrings,
				(Strings->StringsAllocated+=PREALLOC)*sizeof(char *));

		/* shift up */
		for (i=Strings->NumberOfStrings;i>upper;--i)
			Strings->HeaderStrings[i]=Strings->HeaderStrings[i-1];

		/* allocate memory for new string */
		if (!(Strings->HeaderStrings[upper] = AllocMem(strlen(s)+1,MEMF_CLEAR)))
			return(-1);

		/* copy new */
		strcpy(Strings->HeaderStrings[upper],s);

		Strings->NumberOfStrings++;
	}

	return(upper);

}


void GarbageCollection(struct Strings *Strings,struct List *Mailbox)
{
	int good, current;
	int n;
	UBYTE *flags,*theflags;
	struct Mail *Scan;

	/* allocate memory for flags */
	if (!(flags = AllocMem(sizeof(UBYTE)*(n=Strings->NumberOfStrings+1),
		MEMF_CLEAR|MEMF_PUBLIC)))
		return;

	/* GetNumber returns -1 if not found! */
	theflags = flags+1;

	/* mark every used string */
	for (Scan=(struct Mail *)(Mailbox->lh_Head);
		Scan->m_Node.ln_Succ;
		Scan=(struct Mail *)(Scan->m_Node.ln_Succ))
	{
		theflags[GetNumber(Scan->From,FALSE,Strings)] = TRUE;
		theflags[GetNumber(Scan->To,FALSE,Strings)] = TRUE;
		theflags[GetNumber(Scan->Cc,FALSE,Strings)] = TRUE;
		theflags[GetNumber(Scan->ReplyTo,FALSE,Strings)] = TRUE;
		theflags[GetNumber(Scan->Subject,FALSE,Strings)] = TRUE;
		theflags[GetNumber(Scan->InReplyTo,FALSE,Strings)] = TRUE;
		theflags[GetNumber(Scan->MsgId,FALSE,Strings)] = TRUE;
	}

	/* eliminate all unused strings */
	for (current=1,good=1;
		current<Strings->NumberOfStrings;
		current++)
		if (theflags[current]) /* keep it */
			Strings->HeaderStrings[good++] = Strings->HeaderStrings[current];
		else /* throw away */
			FreeMem(Strings->HeaderStrings[current],
				strlen(Strings->HeaderStrings[current])+1); /* one more for trailing 0 */

	Strings->NumberOfStrings -= current-good;

	/* free memory */
	FreeMem(flags,sizeof(UBYTE)*n);
}
