/*
 * feeddb.c Copyright 1999 Christopher M Sedore. All Rights Reserved.
 * Please see the "COPYING" file for license details.
 *
 * Implements a simple mmapped circular feed database.
 */ 

#include "main.h"

struct feeddbheader {
	int cycles;
	int curoffset;
	int feedOffset[MAX_FEEDS];
	int feedCycle[MAX_FEEDS];
	unsigned int serial;
	int unused[13];
};

#define curFeedOffset feedDBHeader->curoffset

int feedDataSize=0;

char *feedDB;

struct feeddbheader *feedDBHeader;

int ffeeddb;

int
FeedDBInit()
{
	char datapath[MAX_PATH];
	
	sprintf(datapath,"%s/feed.dat",GetConfigString("FeedDataPath"));
	
	GetConfigInt("FeedDataSize",&feedDataSize);
	
	ffeeddb=open(datapath,O_RDWR|O_CREAT);
	
	if (ffeeddb<0) {
		printf("could not open feed data file %s\n",datapath);
		exit(1);
	}
	
	feedDB=mmap(NULL,feedDataSize,
			PROT_READ|PROT_WRITE,MAP_SHARED,ffeeddb,0);
	
	if (feedDB==NULL) {
		printf("could not map table file\n");
		exit(1);
	}
	
	feedDBHeader=(struct feeddbheader *)feedDB;
	
	feedDB+=sizeof(struct feeddbheader);
	
}

int
FeedDBShutdown()
{
	munmap(feedDBHeader,feedDataSize);
	close(ffeeddb);
}

int
FeedDBRecord(struct article *art,u_quad_t key)
{
	struct artent *ae;
	int x=MAX_FEEDS / 32;
	
	
	
	ae=(struct artent *) &feedDB[curFeedOffset];
	
//  printf("offset=%u,ae=%p\n",curFeedOffset,ae);
	
	bzero(ae,sizeof(struct artent));
	
	assert((void *) ae< (void *)feedDB+(feedDataSize-(sizeof(struct feeddbheader) + 255 + 50)));
	assert(*art->mid=='<');
	
	ae->magic=ARTENTRY_MAGIC;
	ae->artlen=art->len;
	ae->key=key;
	strcpy(&ae->mid[0],art->mid);
	ae->length=strlen(ae->mid);

	ae->serial=feedDBHeader->serial++;
	
	while (x--) {
		ae->siteflags[x]=art->distflags[x];
	} 
	
	assert(*ae->mid=='<');
	
	curFeedOffset+=(ae->length+sizeof(struct artent));
	if (curFeedOffset>=feedDataSize-(sizeof(struct feeddbheader) + MAX_ARTICLEID+50)) {
		curFeedOffset=0;
		feedDBHeader->cycles++;
		printf("feed db wrap\n");
	}
}

#define myoffset(x) feedDBHeader->feedOffset[x]
#define mycycles(x) feedDBHeader->feedCycle[x]

void
FeedFlagSite(unsigned int num,unsigned int *fl)
{
	int x,y;
	
	assert(num<MAX_FEEDS);
	
	x=num/32;
	y=num % 32;
	
	fl[x]|=1<<y;
}	

int
FeedCheckFlags(unsigned int num,unsigned int *fl)
{
	int x,y;
	
	assert(num<MAX_FEEDS);
	
	x=num/32;
	y=num % 32;
	
	return (fl[x] & 1<<y);
	
}

void
FeedSetOffset(int num,struct artent *ae)
{
	
	assert((char *)ae>feedDB);
	assert((char *)ae<feedDB+(feedDataSize-(sizeof(struct feeddbheader) +255 +50)));
	
	myoffset(num)=((char *)ae)-feedDB;
}

int
FeedDBGetArticles(int num,int numarticles,struct artent **pae,int maxqueue) {
	struct artent *ae;
	char *p;
	int x=0;
	
	assert(num<MAX_FEEDS);
	
	
//	printf("myoffset=%u,",myoffset(num));
	

	if (myoffset(num)>feedDataSize-(sizeof(struct feeddbheader) + 50 + MAX_ARTICLEID)) {
		mycycles(num)=feedDBHeader->cycles;
		myoffset(num)=0;
		printf("adjust wrap\n");
	}
	
	if (myoffset(num)>curFeedOffset) {
		if (mycycles(num)!=feedDBHeader->cycles-1) {
			mycycles(num)=feedDBHeader->cycles-1;
			myoffset(num)=curFeedOffset+(1024*300);
			printf("adjust gtfo/cyc!=\n");
		}
	}
	
	if ((mycycles(num)>feedDBHeader->cycles) 
		|| (mycycles(num)<feedDBHeader->cycles-1)) {
		mycycles(num)=feedDBHeader->cycles;
		myoffset(num)=curFeedOffset;
		printf("adjust cyc oor\n");
	}
	
	p=&feedDB[myoffset(num)];
	
	if (mycycles(num)==feedDBHeader->cycles) {
		if (curFeedOffset<=myoffset(num)) {
			myoffset(num)=curFeedOffset;
//		printf("feed caught up\n");
			return x;
		}
	}
	
	while (1) {
		ae=(struct artent *) p;
		if (ae->magic==ARTENTRY_MAGIC) {
			if (feedDBHeader->serial-ae->serial<maxqueue) {
				if (FeedCheckFlags(num,ae->siteflags)) {
					pae[x++]=ae;
//					printf("found %s\n",ae->mid);
					if (x==numarticles) {
						p+=ae->length + sizeof(struct artent);
						myoffset(num)=p-feedDB;
						return x;
					}
				} else {
//					putchar('!');
				}
			}
			
			p+=ae->length + sizeof(struct artent);
		} else {
			p++;
			putchar('.');
		}
		
		if (p-feedDB>
			feedDataSize-(MAX_ARTICLEID+50+sizeof(struct feeddbheader))) {
			p=feedDB;
			mycycles(num)=feedDBHeader->cycles;
			printf("feed wrap\n");
			myoffset(num)=0;
			continue;
		}
		
		if (mycycles(num)==feedDBHeader->cycles) {
			if (curFeedOffset<=p-feedDB) {
				myoffset(num)=curFeedOffset;
//			printf("feed caught up\n");
				return x;
			}
		}
		
	}
}

#ifdef STDEBUG

static int y=500;

int
GetEm() {
	char buf[300];
	u_quad_t key;
	struct artent *ae;
	
	while (y<50000) {
		
		if (!FeedDBGetArticles(10,1,&ae)) {
			printf("none to get (y=%u)\n",y);
			return;
		}
		
		sprintf(buf,"<test#%u-%u@maxwell>",y,y%123);
		
		key=0xFEFE1BAD;
		
		key=key<<32;
		
		key|=y;
		
		if (strcmp(buf,ae->mid)) {
			printf("mismatch %s should be %s\n",ae->mid,buf);
		}
		
		if (key!=ae->key) {
			printf("key mismatch\n");
		}
		
		if (ae->artlen!=y) {
			printf("length mismatch\n");
		}
		
		y++;
	}
}

int 
main(int argc, char **argv) 
{
	char buf[1024];
	struct article art;
	int x=500;
	u_quad_t key;
	
	read_config("config");
	
	FeedDBInit();
	
	while (x<50000) {
		
		art.len=x;
		
		sprintf(art.mid,"<test#%u-%u@maxwell>",x,x%123);
		
		FeedFlagSite(10,art.distflags);
		
		key=0xFEFE1BAD;
		
		key=key<<32;
		
		key|=x;
		
		FeedDBRecord(&art,key);
		
		x++;
		
		if (!(x%50)) GetEm();
		
	}
	
	
}

#endif
