/**********************************************************************
 
	Copyright (C) 2003 Hirohisa MORI <joshua@nichibun.ac.jp>
 
	This program is free software; you can redistribute it 
	and/or modify it under the terms of the GLOBALBASE 
	Library General Public License (G-LGPL) as published by 

	http://www.globalbase.org/
 
	This program is distributed in the hope that it will be 
	useful, but WITHOUT ANY WARRANTY; without even the 
	implied warranty of MERCHANTABILITY or FITNESS FOR A 
	PARTICULAR PURPOSE.

**********************************************************************/


#include	"memory_debug.h"
#include	"pdb.h"
#include	"utils.h"
#include	"fa.h"
#include	"filespace.h"

FA_CACHE *		cache_list;
FA_RING			cache_ring;
int			cache_size;

void
init_ring(FA_RING * r)
{
	r->next = r;
	r->prev = r;
}

void
insert_ring(void * r1,void * r2)
{
FA_RING * rr1,*rr2;
	rr1 = r1;
	rr2 = r2;
	rr2->prev = rr1;
	rr2->next = rr1->next;
	rr2->prev->next = rr2;
	rr2->next->prev = rr2;
}

void
delete_ring(void * r)
{
FA_RING * rr;
	rr = r;
	rr->prev->next = rr->next;
	rr->next->prev = rr->prev;
}

void
init_cache()
{
	init_ring(&cache_ring);
	cache_size = 0;
}


void
test_ring()
{
FAC_HEADER * h;

	for ( h = (FAC_HEADER*)cache_ring.next;
			h != (FAC_HEADER*)&cache_ring;
			h = (FAC_HEADER*)h->r.next ) {
		if ( h->r.prev->next != (FA_RING*)h )
			er_panic("test_ring(0)");
		if ( h->r.next->prev != (FA_RING*)h )
			er_panic("test_ring(1)");
		if ( h->file->filename == 0 )
			er_panic("test_ring(2)");
	}
	for ( h = (FAC_HEADER*)cache_ring.prev;
			h != (FAC_HEADER*)&cache_ring;
			h = (FAC_HEADER*)h->r.prev ) {
		if ( h->r.prev->next != (FA_RING*)h )
			er_panic("test_ring(0)");
		if ( h->r.next->prev != (FA_RING*)h )
			er_panic("test_ring(1)");
		if ( h->file->filename == 0 )
			er_panic("test_ring(2)");
	}
}


void
delete_fac(FA_CACHE * fac)
{
FA_CACHE ** fp;
	for ( fp = &cache_list ; *fp ; fp = &(*fp)->next ) {
		if ( *fp != fac )
			continue;
		*fp = fac->next;
		d_f_ree(fac->filename);
		d_f_ree(fac);
		return;
	}
}

FA_CACHE *
new_file(char *filename)
{
FA_CACHE * fac;
	for ( fac = cache_list ; fac ;fac = fac->next ) {
		if ( strcmp(filename,fac->filename) )
			continue;
		fac->ref ++;
		return fac;
	}
	fac = d_alloc(sizeof(*fac));
	fac->filename = copy_str(filename);
	fac->ref = 1;
	fac->records = 0;
	fac->next = cache_list;
	cache_list = fac;
	return fac;
}

void
close_file(FA_CACHE * fac)
{
FA_CACHE ** fp;
	if ( fac->ref == 0 )
		er_panic("close_file(1)");
	for ( fp = &cache_list ; *fp ; fp = &(*fp)->next ) {
		if ( *fp != fac )
			continue;
		if ( fac->ref == 0 )
			return;
		fac->ref --;
		return;
	}
}

int
cmp_fa(FAC_HEADER * h1,FAC_HEADER * h2)
{
	if ( h1->offset < h2->offset )
		return -1;
	if ( h1->offset > h2->offset )
		return 1;
	return 0;
}

FAC_HEADER * 
search_cache(FA_CACHE * fac,unsigned int ofs)
{
AVT_NODE * a1;
FAC_HEADER h;
FAC_HEADER * fp;
	if ( fac->ref == 0 )
		er_panic("search_cache(1)");
	h.offset = ofs;
	a1 = avt_search(fac->records,&h,cmp_fa);
	if ( a1 == 0 )
		return 0;
	fp = a1->data;
	delete_ring(&fp->r);
	insert_ring(&cache_ring,&fp->r);
	return fp;
}

int
insert_cache(FA_CACHE * fac,FAC_HEADER * d)
{
AVT_NODE * a1, * a2;
FAC_HEADER * d1;
FA_CACHE * fa;
	if ( fac->ref == 0 )
		er_panic("insert_cache(1)");
	d->file = fac;
	a1 = d_alloc(sizeof(*a1));
	a1->data = d;
	a2 = avt_insert(&fac->records,a1,cmp_fa);
	if ( a2 != a1 ) {
		d_f_ree(a1);
		d_f_ree(d);
		return -1;
	}
	insert_ring(&cache_ring,d);

	cache_size += d->h.size;
	for ( ; cache_size >= CACHE_MAX ; ) {
		d1 = (FAC_HEADER*)cache_ring.prev;
		fa = d1->file;
		delete_ring(cache_ring.prev);
		avt_delete(&fa->records,d1,cmp_fa);
		cache_size -= d1->h.size;
		d_f_ree(d1);
		if ( fa->records == 0 && fa->ref == 0 )
			delete_fac(fa);
	}
	return 0;
}
