/*
 * store.c Copyright 1999 Christopher M Sedore.  All Rights Reserved.
 * Please see the "COPYING" file for license information. 
 *
 * Implement cyclical article stores.
 */

#include "main.h"

#define MAX_ART_STORES 255
#define MAX_ART_STORE_SIZE 100*1024

struct artstore {
	int size;
	int metafd,storefd;
	int flags;
	off_t offset;
	unsigned char cycles;
	char *diststr;
	void *dist;
};

struct artstore storeArray[MAX_ART_STORES];

extern time_t curtime;

time_t lastsync=0;

#define STORE_INUSE 0x01

#define IS_VALIDSTORE(num) ((num>-1) && (num<MAX_ART_STORES))
#define IS_STOREINUSE(num) ((IS_VALIDSTORE(num)) && (storeArray[num].flags & STORE_INUSE))

int
CreateArtStore(int num, char *path,int size,char *dist)
{
	int f;
	char s[512];
	struct myaiocb cb,*pcb=NULL;


	if (IS_STOREINUSE(num)) 
		return -1;

	if (size>MAX_ART_STORE_SIZE) {
		return -1;
	}

	sprintf(s,"%s/%u.meta",path,num);
	f=open(s,O_RDWR|O_CREAT,0700);
	
	if (f<0) {
		printf("Error opening %s/%u.meta",path,num);
		return -1;
	}

	bzero(&cb,sizeof(struct myaiocb));
	cb.cb.aio_fildes=f;
	cb.cb.aio_buf=&storeArray[num];
        cb.cb.aio_nbytes=sizeof(struct artstore);
	cb.cb.aio_offset=0;
	cb.callback=NULL;

	if (aio_read((struct aiocb *)&cb)<0) {
		perror("read");
		exit(1);
	}

	if (aio_waitcomplete((struct aiocb **)&pcb,NULL)<0) {
		perror("susp");
		exit(1);
	}

	storeArray[num].metafd=f;
	storeArray[num].size=size;

	sprintf(s,"%s/%u.data",path,num);
	f=open(s,O_RDWR|O_CREAT,0700);

	if (f<0) {
		close(storeArray[num].metafd);
		printf("Error opening %s/%u.data",path,num);
		return -1;
	}

	storeArray[num].storefd=f;

	storeArray[num].flags|=STORE_INUSE;

	if ((dist) && (strlen(dist))) {
		storeArray[num].dist=CompileMatchString(dist);
	} else {
		storeArray[num].dist=NULL;
	}
	
	return 0;
}

u_quad_t
GetStoreKey(int store)
{
	u_quad_t key;
	int base;

	if (!IS_STOREINUSE(store)) {
		return 0;
	}

	base=storeArray[store].cycles<<8;
	base|=store&0xFF;
	key=base;
	key=key<<40;
	key|=storeArray[store].offset;

	return key;	
}	


void
StoreSync()
{

	int x=0;

	while (x<MAX_ART_STORES) {
		if (IS_STOREINUSE(x)) {
			lseek(storeArray[x].metafd,0,SEEK_SET);
			write(storeArray[x].metafd,&storeArray[x],sizeof(struct artstore));
		}
		x++;
	}
}

void
StoreShutdown()
{
	int x=0;

	while (x<MAX_ART_STORES) {
		if (IS_STOREINUSE(x)) {
			lseek(storeArray[x].metafd,0,SEEK_SET);
			write(storeArray[x].metafd,&storeArray[x],sizeof(struct artstore));
			close(storeArray[x].metafd);
			close(storeArray[x].storefd);
		}
		x++;
	}
}

void
StoreCheckAndWrap(int store)
{
	u_quad_t max;

	max=storeArray[store].size;

	max=max*1024*1024;

//	printf("max=%qu off=%qu\n",max,storeArray[store].offset);

	if (storeArray[store].offset+(MAX_ARTSIZE*2)>max) {
		storeArray[store].offset=1;
		storeArray[store].cycles++;
		if (storeArray[store].cycles>255)
			storeArray[store].cycles=0;
		printf("store wrap\n");
	}

	time(&curtime);

	if (curtime>lastsync+30) {
		StoreSync();
		lastsync=curtime;
	}
}

int
WriteToStore(int store,u_quad_t *key,struct myaiocb *cb) 
{
	u_quad_t mkey;

	mkey=GetStoreKey(store);

	if (!mkey)
		return -1;

	*key=mkey;

	cb->cb.aio_fildes=storeArray[store].storefd;
	cb->cb.aio_offset=storeArray[store].offset;
	storeArray[store].offset+=cb->cb.aio_nbytes;

	if (aio_write((struct aiocb *)cb)) {
		return -1;
	}
	
	return 0;
}

int
DecipherKey(u_quad_t key, int *store, off_t *offset)
{
	int cycles;

	*offset=key&0xFFFFFFFFFFLL;
	key=key>>40;
	
	cycles=key&0xFFFF;
	cycles=cycles>>8;

	*store=key&0xFF;

	if (IS_STOREINUSE(*store)) {
		if (storeArray[*store].cycles<cycles)
			return -1;
		if (storeArray[*store].cycles>cycles+1)
			return -1;
		if ((storeArray[*store].cycles==((cycles+1)&0xFF)) && 
			((storeArray[*store].offset+50*1024*1024)>*offset))
			return -1;
		if ((storeArray[*store].cycles==((cycles+1)&0xFF)) && 
			((storeArray[*store].offset+50*1024*1024)<*offset))
			return 0;
		if (storeArray[*store].offset>*offset)
			return 0;
	}

	return -1;
}

int
GetFromStore(u_quad_t key,struct myaiocb *cb)
{
	int store;
	off_t off;

	if (DecipherKey(key,&store,&off)) {
		return -1;
	}

	
	cb->cb.aio_fildes=storeArray[store].storefd;
	cb->cb.aio_offset=off;

	if (aio_read((struct aiocb *)cb)) {
		perror("GetFromStore");
		return -1;
	}

	return 0;
}

void *
MapFromStore(u_quad_t key,int len) 
{
	int store;
	off_t off;
	void *addr;

	if (DecipherKey(key,&store,&off)) {
		return NULL;
	}

	addr=mmap(NULL,len,PROT_READ,0,storeArray[store].storefd,off);

	return addr;
}

int
UnMapStore(void *addr, int len)
{
	munmap(addr,len);
}

int
FindStore(struct article *art)
{
  int x;

  for (x=0;x<MAX_ART_STORES;x++) {
	if (IS_STOREINUSE(x)) {
		if (storeArray[x].dist) {
			if (CheckMatch(art,storeArray[x].dist)) {
				return x;
			} else {
				continue;
			}
			return x;
		} else {
			return x;
		}
	}
  }

  return -1;

}
			
