/**********************************************************************
 
	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	"machine/include.h"
#include	"memory_debug.h"
#include	"pdb.h"
#include	"filespace.h"
#include	"favt.h"
#include	"utils.h"



extern void (*change_endian_favt_table_to_host[])();

typedef struct favt_load_work {
	FAVT_HEADER * 		ret;
	unsigned int		fofs;
	FAVT_PTR *		parent;
} FAVT_LOAD_WORK;

int favt_load_root();
int favt_load_node();

int (*favt_func_table[])() = {
	0,
	favt_load_root,
	favt_load_node,
};

int (**func_table[])() = {
	0,
	favt_func_table,
};

FAVT_HEADER	favt_h;
AVT_NODE *	fr_tree;
int		favt_count;

void
init_favt()
{
	favt_h.next = favt_h.prev = &favt_h;
	fr_tree = 0;
}

/*
void
check_favt_ring(char * inp)
{
FAVT_HEADER * h;
FAVT_NODE * n;
printf("CFR %s\n",inp);
	for ( h = favt_h.next ; h != &favt_h ; h = h->next )
		switch ( h->type ) {
		case PNT_FAVT_NODE:
			n = (FAVT_NODE*)h;
			if ( n->parent )
				if ( n->parent->ptr != (void*)n )
					er_panic("check_favt_ring");
			break;
		default:
			break;
		}
	for ( h = favt_h.prev ; h != &favt_h ; h = h->prev );
printf("CFR END %s\n",inp);
}
*/

int
cmp_favt(FAVT_HEADER * a,FAVT_HEADER * b)
{

	if ( ((int)a->p) < ((int)b->p) )
		return -1;
	if ( ((int)a->p) > ((int)b->p) )
		return 1;
	if ( a->fofs < b->fofs )
		return -1;
	if ( a->fofs > b->fofs )
		return 1;
	return 0;
}

void
insert_favt_tree(FAVT_HEADER * h)
{
AVT_NODE * a,* b;

	a = d_alloc(sizeof(AVT_NODE));
	a->data = h;
	b = avt_insert(&fr_tree,a,cmp_favt);
	if ( b != a )
		er_panic("insert_favt_tree");
}

void
delete_favt_tree(PDB * p,unsigned int fofs)
{
AVT_NODE * a;
FAVT_HEADER h;
	h.fofs = fofs;
	h.p = p;
	a = avt_delete(&fr_tree,&h,cmp_favt);
	if ( a )
		d_f_ree(a);
}



void
insert_favt_ring(FAVT_HEADER * h)
{

	h->prev = &favt_h;
	h->next = favt_h.next;
	h->prev->next = h;
	h->next->prev = h;
	favt_count ++;
}

void
delete_favt_ring(FAVT_HEADER * h)
{

	h->prev->next = h->next;
	h->next->prev = h->prev;
	favt_count --;
}

void
_touch_ring(void * h)
{
	delete_favt_ring((FAVT_HEADER*)h);
	insert_favt_ring((FAVT_HEADER*)h);
}

void
touch_ring(void * h1,void * h2)
{
	if ( h1 )
		_touch_ring(h1);
	if ( h2 )
		_touch_ring(h2);
}

FAVT_PTR
get_favt_ptr(unsigned int fofs,int type)
{
FAVT_PTR ret;
	ret.fofs = fofs;
	ret.type = type;
	ret.ptr = 0;
	ret.flags = 0;
	return ret;
}

void
get_favt_ptr2(FAVT_PTR * ptr,FAVT_NODE * n)
{
	if ( n == 0 ) {
		ptr->fofs = 0;
		ptr->type = 0;
		ptr->ptr = 0;
	}
	else {
		ptr->fofs = n->h.fofs;
		ptr->type = n->h.type;
		ptr->ptr = n;
		n->parent = ptr;
	}
	ptr->flags |= FAF_DIRTY;
}

void
set_favt_ptr(FAVT_PTR * d,FAVT_PTR s)
{
	*d = s;
	if ( d->ptr )
		((FAVT_NODE *)d->ptr)->parent = d;
	d->flags |= FAF_DIRTY;
}

FAVT_HEADER *
favt_load(PDB * p,FAVT_ROOT * rt,unsigned int fofs,FAVT_PTR * parent)
{
FAVT_LOAD_WORK w;
PN_HEADER * h;
int er;
int type,size,len;

	if ( p == 0 )
		p = rt->h.p;
	w.fofs = fofs;
	w.ret = 0;
	w.parent = parent;
	h = d_alloc(sizeof(*h));
	lseek(p->fid,fofs,SEEK_SET);
	er = u_read(p->fid,h,sizeof(*h));
	if ( er < sizeof(*h) ) {
		d_f_ree(h);
		return 0;
	}
	change_endian_header(h);
	size = h->size;
	type = h->type;
	h = d_re_alloc(h,size);
	change_endian_header(h);
	er = read(p->fid,h+1,len=size-sizeof(*h));
	if ( er < len ) {
		d_f_ree(h);
		return 0;
	}
	if ( (type&PNT_TYPE_MASK) < 1 ||
			(type&PNT_TYPE_MASK) >= 3 )
{printf("type = %i %i\n",type,h->type);
		er_panic("favt_load");
}
	(*change_endian_favt_table_to_host[type&PNT_TYPE_MASK])(h);
	(*func_table[PNT_GET_GROUP(type)][type&PNT_TYPE_MASK])(p,rt,h,&w);
	return w.ret;
}



FAVT_ROOT *
get_root(PDB * p,unsigned int fofs,void (*func)())
{
FAVT_ROOT * r;
FAVT_HEADER h;
AVT_NODE *a;

	h.fofs = fofs;
	h.p = p;
	a = avt_search(fr_tree,&h,cmp_favt);
	if ( a == 0 ) {
		r = (FAVT_ROOT*)favt_load(p,0,fofs,0);
		if ( r == 0 )
			er_panic("get_root");
		if ( r->h.type != PNT_FAVT_ROOT )
			er_panic("get_root2");
		r->endian_func = func;
		insert_favt_tree((FAVT_HEADER*)r);
	}
	else	r = a->data;
	touch_ring(r,0);
	return r;
}

int
favt_write_node(FAVT_HEADER * h)
{
FAVT_NODE * nn;
PN_FAVT_NODE * n;
int size;
PDB * p;
	nn = (FAVT_NODE*)h;

	p = h->p;
	n = d_alloc(sizeof(PN_FAVT_NODE)+nn->data_len);
	n->large = nn->large.fofs;
	n->small = nn->small.fofs;
	n->level = nn->level;
	memcpy(n+1,nn->data,nn->data_len);
	(*nn->root->endian_func)(n+1,nn->data_len);
	n->h.type = PNT_FAVT_NODE;
	size = n->h.size = nn->data_len + sizeof(PN_FAVT_NODE);
	change_endian_favt_node(n);
	u_lseek(p->fid,h->fofs,SEEK_SET);
	fa_write(p->fid,n,size);
	d_f_ree(n);
	return 0;
}

int
favt_write_root(FAVT_HEADER * h)
{
FAVT_ROOT * rr;
PN_FAVT_ROOT r;

	rr = (FAVT_ROOT*)h;
	r.node = rr->node.fofs;
	r.type = rr->type;
	r.h.type = PNT_FAVT_ROOT;
	r.h.size = sizeof(r);
	change_endian_favt_root(&r);
	u_lseek(h->p->fid,h->fofs,SEEK_SET);
	fa_write(h->p->fid,&r,sizeof(r));
	return 0;
}

int
favt_write(FAVT_HEADER * h)
{

	switch ( h->type ) {
	case PNT_FAVT_ROOT:
		return favt_write_root(h);
	case PNT_FAVT_NODE:
		return favt_write_node(h);
	}
	return -1;
}

int
check_dirty(FAVT_HEADER * h,unsigned short reset)
{
FAVT_NODE * n;
FAVT_ROOT * r;
	if ( h->flags & FAF_DIRTY ) {
		h->flags &= ~reset;
		return 1;
	}
	switch ( h->type ) {
	case PNT_FAVT_NODE:
		n = (FAVT_NODE*)h;
		if ( n->small.flags & FAF_DIRTY ) {
			n->small.flags &= ~reset;
			return 1;
		}
		if ( n->large.flags & FAF_DIRTY ) {
			n->large.flags &= ~reset;
			return 1;
		}
		return 0;
	case PNT_FAVT_ROOT:
		r = (FAVT_ROOT*)h;
		if ( r->node.flags & FAF_DIRTY ) {
			r->node.flags &= ~reset;
			return 1;
		}
		return 0;
	default:
		er_panic("check_dirty");
	}
	return 0;
}

int
free_favt_cache(FAVT_HEADER * h)
{
FAVT_NODE * n;
FAVT_ROOT * r;
int i;
char * p;

	switch ( h->type ) {
	case PNT_FAVT_NODE:
		n = (FAVT_NODE*)h;
		if ( n->small.fofs && n->small.ptr || 
				n->large.fofs && n->large.ptr )
			return -1;
		delete_favt_ring(h);
		if ( n->parent )
			n->parent->ptr = 0;
		d_f_ree(n->data);
		d_f_ree(n);
		break;
	case PNT_FAVT_ROOT:
		r = (FAVT_ROOT*)h;
		if ( r->node.fofs && r->node.ptr )
			return -1;
		delete_favt_ring(h);
		delete_favt_tree(r->h.p,r->h.fofs);
		d_f_ree(r);
		break;
	}
	return 0;
}

void
clear_cache()
{
FAVT_NODE * n;
FAVT_ROOT * r;
FAVT_HEADER * h, * h2;
	for ( ; favt_count > FAVT_CACHE_SIZE ; ) {
		for ( h = favt_h.prev ; h != &favt_h ; ) {
			if ( h->flags & FAF_LOCK ) {
				h = h->prev;
				continue;
			}
			switch ( h->type ) {
			case PNT_FAVT_ROOT:
				r = (FAVT_ROOT*)h;
				if ( r->node.ptr ) {
					h = h->prev;
					continue;
				}
				break;
			case PNT_FAVT_NODE:
				n = (FAVT_NODE*)h;
				if ( n->small.ptr ) {
					h = h->prev;
					continue;
				}
				if ( n->large.ptr ) {
					h = h->prev;
					continue;
				}
				break;
			default:
				er_panic("clear_cache");
			}
			break;
		}
		if ( h == &favt_h )
			er_panic("clear_cache");
		if ( check_dirty(h,0) ) {
			for ( h2 = favt_h.next ; h2 != &favt_h ; 
					h2 = h2->next )
				if ( check_dirty(h2,FAF_DIRTY) )
					favt_write(h2);
		}
		h2 = h->prev;
		if ( free_favt_cache(h) < 0 )
			er_panic("clear_cache(2)");
		h = h2;
	}
}

void
flush_favt_cache(PDB * p)
{
FAVT_HEADER * h1, * h2;
int f;

	for ( h1 = favt_h.next ; h1 != &favt_h ;) {
		if ( h1->p != p ) {
			h1 = h1->next;
			continue;
		}
		if ( check_dirty(h1,FAF_DIRTY) )
			favt_write(h1);
		h1 = h1->next;
	}
retry:
	f = 0;
	for ( h1 = favt_h.next ; h1 != &favt_h ; ) {
		if ( h1->p != p ) {
			h1 = h1->next;
			continue;
		}
		h2 = h1->next;
		if ( free_favt_cache(h1) < 0 )
			f = 1;
		h1 = h2;
	}
	if ( f )
		goto retry;
}

void
favt_lock(FAVT_HEADER * h)
{
	h->flags |= FAF_LOCK;
}

void
favt_unlock(FAVT_HEADER * h)
{
	h->flags &= ~FAF_LOCK;
}

void
favt_load_header(PDB * p,PN_HEADER * f,FAVT_HEADER * ret,FAVT_LOAD_WORK * w)
{
	ret->type = f->type;
	ret->fofs = w->fofs;
	ret->flags = 0;
	ret->p = p;
	insert_favt_ring(ret);
	clear_cache();
}

int
favt_load_root(PDB * p,FAVT_ROOT * rt,PN_FAVT_ROOT * f,FAVT_LOAD_WORK * w)
{
FAVT_ROOT * ret;

	ret = d_alloc(sizeof(*ret));
	ret->node = get_favt_ptr(0,PNT_FAVT_NODE);
	favt_load_header(p,&f->h,&ret->h,w);
	ret->node = get_favt_ptr(f->node,PNT_FAVT_NODE);
	ret->type = f->type;
	w->ret = (FAVT_HEADER*)ret;
	return 0;
}


int
favt_load_node(PDB * p,FAVT_ROOT * rt,PN_FAVT_NODE * f,FAVT_LOAD_WORK * w)
{
FAVT_NODE * ret;

	if ( p == 0 )
		p = rt->h.p;
	ret = d_alloc(sizeof(*ret));
	ret->small = get_favt_ptr(f->small,PNT_FAVT_NODE);
	ret->large = get_favt_ptr(f->large,PNT_FAVT_NODE);
	ret->parent = w->parent;
	ret->level = f->level;
	ret->root = rt;
	ret->data = d_alloc(f->h.size - sizeof(PN_FAVT_NODE));
	ret->data_len = f->h.size - sizeof(PN_FAVT_NODE);
	favt_load_header(p,&f->h,&ret->h,w);
	memcpy(ret->data,f+1,f->h.size - sizeof(PN_FAVT_NODE));
	(*rt->endian_func)(ret->data,ret->data_len);
	w->ret = (FAVT_HEADER*)ret;

	return 0;
}

FAVT_NODE *
g_ptr(FAVT_ROOT * rt,FAVT_PTR * ptr)
{
	if ( ptr->fofs == 0 )
		return 0;
	if ( ptr->ptr == 0 ) {
		ptr->ptr = favt_load(0,rt,ptr->fofs,ptr);
		((FAVT_NODE *)(ptr->ptr))->parent = ptr;
	}
	return ptr->ptr;
}

FAVT_NODE *
small_node(FAVT_ROOT * rt,FAVT_NODE * n)
{
FAVT_NODE * ret;
	if ( n->small.fofs == 0 )
		return 0;
	if ( n->small.ptr )
		ret = n->small.ptr;
	else {
		ret = n->small.ptr = favt_load(0,rt,n->small.fofs,&n->small);
		ret->parent = &n->small;
	}
	touch_ring(ret,0);
	return ret;
}


FAVT_NODE *
large_node(FAVT_ROOT * rt,FAVT_NODE * n)
{
FAVT_NODE * ret;

	if ( n->large.fofs == 0 )
		return 0;
	if ( n->large.ptr )
		ret = n->large.ptr;
	else {
		ret = n->large.ptr = favt_load(0,rt,n->large.fofs,&n->large);
		ret->parent = &n->large;
	}
	touch_ring(ret,0);
	return ret;
}

FAVT_NODE *
root_node(FAVT_ROOT * r)
{
FAVT_NODE * ret;

	if ( r->node.fofs == 0 )
		return 0;
	if ( r->node.ptr )
		ret = r->node.ptr;
	else	ret = r->node.ptr = favt_load(0,r,r->node.fofs,&r->node);
	touch_ring(ret,0);
	return ret;
}


void fset_level(FAVT_ROOT * rt,FAVT_NODE * avt);
FAVT_NODE * _favt_delete_small(FAVT_ROOT * rt,FAVT_PTR*);
FAVT_NODE * _favt_delete_large(FAVT_ROOT * rt,FAVT_PTR*);

void
fset_level(FAVT_ROOT * p,FAVT_NODE * favt)
{
int lev1,lev2;
	touch_ring(p,favt);
	if ( favt == 0 )
		return;
	if ( favt->large.fofs )
		lev1 = large_node(p,favt)->level;
	else	lev1 = 0;
	if ( favt->small.fofs )
		lev2 = small_node(p,favt)->level;
	else	lev2 = 0;
	if ( lev1 > lev2 )
		favt->level = lev1 + 1;
	else	favt->level = lev2 + 1;
	favt->h.flags |= FAF_DIRTY;
}

void
favt_valance(FAVT_ROOT * p,FAVT_PTR * favtp)
{
FAVT_NODE * b1, * b2, * b3, * b4, * b5;
int lev1,lev2;
FAVT_NODE * g;
	if ( favtp->fofs == 0 )
		return;
	if ( (g=g_ptr(p,favtp))->large.fofs )
		lev1 = large_node(p,g)->level;
	else	lev1 = 0;
	if ( (g=g_ptr(p,favtp))->small.fofs )
		lev2 = small_node(p,g)->level;
	else	lev2 = 0;
	touch_ring(p,g);
	if ( lev2 + 1 < lev1 ) {
		b1 = g_ptr(p,favtp);
		b2 = large_node(p,b1);
		b3 = small_node(p,b1);
		b4 = large_node(p,b2);
		b5 = small_node(p,b2);

		get_favt_ptr2(favtp,b2);
		get_favt_ptr2(&b2->small,b1);
		get_favt_ptr2(&b1->large,b5);

		fset_level(p,b1);
		fset_level(p,b2);
	}
	else if ( lev1 + 1 < lev2 ) {
		b1 = g_ptr(p,favtp);
		b2 = small_node(p,b1);
		b3 = large_node(p,b1);
		b4 = small_node(p,b2);
		b5 = large_node(p,b2);

		get_favt_ptr2(favtp,b2);
		get_favt_ptr2(&b2->large,b1);
		get_favt_ptr2(&b1->small,b5);

		fset_level(p,b1);
		fset_level(p,b2);
	}
}

FAVT_NODE *
favt_insert(FAVT_ROOT * p,FAVT_PTR * favtp,FAVT_NODE * a,int (*cmp)())
{
FAVT_NODE * ret;
FAVT_NODE * g;
	if ( favtp->fofs == 0 ) {
		if ( a->data == 0 )
			er_panic("avt_insert(1)");
		get_favt_ptr2(favtp,a);
		a->large = get_favt_ptr(0,PNT_FAVT_NODE);
		a->small = get_favt_ptr(0,PNT_FAVT_NODE);
		a->level = 1;
		a->h.flags |= FAF_DIRTY;
		return a;
	}
	g = g_ptr(p,favtp);
	favt_lock(&g->h);
	switch ( (*cmp)(g->data,a->data) ) {
	case -1:
		ret = favt_insert(p,&g->large,a,cmp);
		break;
	case 0:
		ret = g;
		favt_unlock(&g->h);
		return ret;
	case 1:
		ret = favt_insert(p,&g->small,a,cmp);
		break;
	}
	fset_level(p,g);
	favt_valance(p,favtp);
	favt_unlock(&g->h);
	return ret;
}


FAVT_NODE *
favt_search(FAVT_ROOT * p,FAVT_NODE * a,void * data,int (*cmp)())
{
FAVT_NODE * b;
	touch_ring(p,a);
	if ( a )
		favt_lock(&a->h);
	for ( ; a ; ) {
		switch ( (*cmp)(a->data,data) ) {
		case -1:
			b = large_node(p,a);
			if ( b )
				favt_lock(&b->h);
			favt_unlock(&a->h);
			a = b;
			break;
		case 0:
			favt_unlock(&a->h);
			return a;
		case 1:
			b = small_node(p,a);
			if ( b )
				favt_lock(&b->h);
			favt_unlock(&a->h);
			a = b;
			break;
		default:
			er_panic("favt_search(1)");
		}
	}
	return 0;
}


int
favt_trace_from_small(FAVT_ROOT * p,FAVT_NODE * a,int (*func)(),void * work)
{
int ret;
	touch_ring(p,a);
	if ( a == 0 )
		return 0;
	favt_lock(&a->h);
	if ( a->small.fofs ) {
		ret = favt_trace_from_small(p,small_node(p,a),func,work);
		if ( ret ) {
			favt_unlock(&a->h);
			return ret;
		}
	}
	ret = (*func)(a,work);
	if ( ret ) {
		favt_unlock(&a->h);
		return ret;
	}
	if ( a->large.fofs ) {
		ret = favt_trace_from_small(p,large_node(p,a),func,work);
		if ( ret ) {
			favt_unlock(&a->h);
			return ret;
		}
	}
	favt_unlock(&a->h);
	return 0;
}



int
favt_trace_from_large(FAVT_ROOT * p,FAVT_NODE * a,int (*func)(),void * work)
{
int ret;

	touch_ring(p,a);
	if ( a == 0 )
		return 0;
	favt_lock(&a->h);
	if ( a->large.fofs ) {
		ret = favt_trace_from_large(p,large_node(p,a),func,work);
		if ( ret ) {
			favt_unlock(&a->h);
			return ret;
		}
	}
	ret = (*func)(a,work);
	if ( ret ) {
		favt_unlock(&a->h);
		return ret;
	}
	if ( a->small.fofs ) {
		ret = favt_trace_from_large(p,small_node(p,a),func,work);
		if ( ret ) {
			favt_unlock(&a->h);
			return ret;
		}
	}
	favt_unlock(&a->h);
	return 0;
}


typedef struct bound_search_work {
	FAVT_ROOT * 	p;
	void * 		data_min;
	void * 		data_max;
	int 		(*cmp)();
	BOUND_LIST *	lst;
} BOUND_SEARCH_WORK;


void
insert_lst(BOUND_SEARCH_WORK * w,FAVT_NODE * a)
{
BOUND_LIST * lst;
	lst = d_alloc(sizeof(*lst));
	lst->data = d_alloc(a->data_len);
	memcpy(lst->data,a->data,a->data_len);
	lst->data_len = a->data_len;
	lst->next = w->lst;
	w->lst = lst;
}

void
_favt_bound_search(
	BOUND_SEARCH_WORK * w,
	FAVT_NODE * a)
{
int cmp_max,cmp_min;
	touch_ring(w->p,a);
	if ( a == 0 )
		return;
	favt_lock(&a->h);
	cmp_max = (*w->cmp)(a->data,w->data_max);
	cmp_min = (*w->cmp)(a->data,w->data_min);
	if ( cmp_max < 0 )
		_favt_bound_search(w,large_node(w->p,a));
	if ( cmp_min > 0 )
		_favt_bound_search(w,small_node(w->p,a));
	if ( cmp_max <= 0 && cmp_min >= 0 )
		insert_lst(w,a);
	favt_unlock(&a->h);
}

BOUND_LIST *
favt_bound_search(
	FAVT_ROOT * p,
	FAVT_NODE * a,
	void *	data_min,
	void *	data_max,
	int (*cmp)())
{
BOUND_SEARCH_WORK w;
	w.p = p;
	w.data_min = data_min;
	w.data_max = data_max;
	w.lst = 0;
	w.cmp = cmp;
	_favt_bound_search(&w,a);
	return w.lst;
}

void
free_bound_list(BOUND_LIST * b)
{
BOUND_LIST * bb;
	for ( ; b ; ) {
		bb = b->next;
		d_f_ree(b->data);
		d_f_ree(b);
		b = bb;
	}
}

FAVT_NODE *
_favt_delete_small(FAVT_ROOT *p,FAVT_PTR * ap)
{
FAVT_NODE * ret;
FAVT_NODE * g;
	g = g_ptr(p,ap);
	if ( g->small.fofs ) {
		ret = _favt_delete_small(p,&g->small);
		fset_level(p,g);
		return ret;
	}
	ret = g;
	set_favt_ptr(ap,g->large);
	favt_valance(p,ap);
	return ret;
}

FAVT_NODE *
_favt_delete_large(FAVT_ROOT * p,FAVT_PTR * ap)
{
FAVT_NODE * ret;
FAVT_NODE * g;
	g = g_ptr(p,ap);
	favt_lock(&g->h);

	if ( g->large.fofs ) {
		ret = _favt_delete_large(p,&g->large);
		fset_level(p,g);

		favt_unlock(&g->h);

		return ret;
	}
	ret = g;
	set_favt_ptr(ap,g->small);
	favt_valance(p,ap);

	touch_ring(0,ret);

	favt_unlock(&ret->h);

	return ret;
}




FAVT_NODE *
favt_delete(FAVT_ROOT * p,FAVT_PTR * favtp,void * data,int (*cmp)())
{
FAVT_NODE * ret,* a;
FAVT_NODE * g;

	if ( favtp->fofs == 0 )
		return 0;
	g = g_ptr(p,favtp);
	touch_ring(p,g);

	favt_lock(&g->h);

	switch ( (*cmp)(g->data,data) ) {
	case -1:
		ret = favt_delete(p,&g->large,data,cmp);
		fset_level(p,g);

		favt_unlock(&g->h);

		return ret;
	case 0:
		break;
	case 1:
		ret = favt_delete(p,&g->small,data,cmp);
		fset_level(p,g);

		favt_unlock(&g->h);

		return ret;
	default:
		er_panic("favt_delete(!)");
	}
	ret = g;
	ret->parent = 0;
	if ( g->large.fofs == 0 )
		set_favt_ptr(favtp,g->small);
	else if ( g->small.fofs == 0 )
		set_favt_ptr(favtp,g->large);
	else if ( small_node(p,g)->level < large_node(p,g)->level ) {
		a = _favt_delete_small(p,&g->large);
		set_favt_ptr(&a->large,g->large);
		set_favt_ptr(&a->small,g->small);
		get_favt_ptr2(favtp,a);
	}
	else {
		a = _favt_delete_large(p,&g->small);
		set_favt_ptr(&a->large,g->large);
		set_favt_ptr(&a->small,g->small);
		get_favt_ptr2(favtp,a);
	}
	g = g_ptr(p,favtp);
	fset_level(p,g);
	favt_valance(p,favtp);

	ret->large.ptr = 0;
	ret->small.ptr = 0;

	touch_ring(0,ret);

	favt_unlock(&ret->h);

	return ret;
}

typedef struct favt_test {
	FAVT_ROOT *	p;
	void *		data;
	int 		(*cmp)();
} FAVT_TEST;

int favt_test_func(FAVT_NODE * a,FAVT_TEST * w)
{
int lev1,lev2;

int * ptr;

	ptr = (int*)a->data;
	printf("=%i\n",*ptr);

	if ( w->data == 0 ) {
		if ( w->data )
			d_f_ree(w->data);
		w->data = d_alloc(a->data_len);
		memcpy(w->data,a->data,a->data_len);
		return 0;
	}
	if ( (*w->cmp)(a->data,w->data) < 0 )
		er_panic("favt error 1");
	if ( w->data )
		d_f_ree(w->data);
	w->data = d_alloc(a->data_len);
	memcpy(w->data,a->data,a->data_len);
	if ( a->large.fofs )
		lev1 = large_node(w->p,a)->level;
	else	lev1 = 0;
	if ( a->small.fofs )
		lev2 = small_node(w->p,a)->level;
	else lev2 = 0;
	if ( a->level <= lev1 )
		er_panic("favt error 2");
	if ( a->level <= lev2 )
		er_panic("favt error 3");
	if ( lev1+1 != a->level && lev2+1 != a->level )
		er_panic("favt error 4");
	return 0;
}


void
favt_test(FAVT_ROOT * p,FAVT_NODE * a,int (*cmp)())
{
FAVT_TEST test;
	test.data = 0;
	test.cmp = cmp;
	test.p = p;
	favt_trace_from_small(p,a,favt_test_func,&test);
}


void
favt_alloc_header(PDB * p,FAVT_HEADER * ret,PN_HEADER * h)
{
unsigned int fofs;

	fofs = alloc_filespace(p,h);

	ret->type = h->type;
	ret->fofs = fofs;
	ret->flags = FAF_DIRTY;
	ret->p = p;
	insert_favt_ring(ret);
	clear_cache(p);
}


FAVT_ROOT *
favt_alloc_root(PDB * p,int type,void (*func)())
{
FAVT_ROOT * ret;
PN_HEADER h;
	ret = d_alloc(sizeof(*ret));
	h.type = PNT_FAVT_ROOT;
	h.size = sizeof(PN_FAVT_ROOT);
	favt_alloc_header(p,&ret->h,&h);
	ret->type = type;
	ret->endian_func = func;
	ret->node = get_favt_ptr(0,PNT_FAVT_NODE);
	insert_favt_tree((FAVT_HEADER*)ret);
	return ret;
}


FAVT_NODE *
favt_alloc_node(FAVT_ROOT * rt,
	void * data,int data_size)
{
FAVT_NODE * ret;
PN_HEADER h;
PDB * p;
	p = rt->h.p;
	ret = d_alloc(sizeof(*ret));
	h.type = PNT_FAVT_NODE;
	h.size = sizeof(PN_FAVT_NODE)+data_size;
	ret->root = rt;
	ret->small = get_favt_ptr(0,PNT_FAVT_NODE);
	ret->large = get_favt_ptr(0,PNT_FAVT_NODE);
	ret->parent = 0;
	ret->level = 0;
	ret->data = d_alloc(data_size);
	memcpy(ret->data,data,data_size);
	ret->data_len = data_size;
	favt_alloc_header(p,&ret->h,&h);
	return ret;
}


void
favt_free_root(FAVT_ROOT * rt)
{
	if ( rt == 0  )
		return;
	if ( check_dirty(&rt->h,FAF_DIRTY) )
		favt_write(&rt->h);
	delete_favt_tree(rt->h.p,rt->h.fofs);
	delete_favt_ring(&rt->h);
	free_filespace(rt->h.p,rt->h.fofs);
}

void
favt_free_node(FAVT_NODE * n)
{
	if ( n == 0 )
		return;
	if ( check_dirty(&n->h,FAF_DIRTY) )
		favt_write(&n->h);
	delete_favt_ring(&n->h);
	free_filespace(n->h.p,n->h.fofs);
	if ( n->data )
		d_f_ree(n->data);
	d_f_ree(n);
}

void
cc_node(char * str,FAVT_NODE * n)
{
FAVT_NODE * n1;
	if ( n->large.ptr ) {
		n1 = n->large.ptr;
		if ( n1->parent != &n->large ) {
			printf("%s\n",str);
			er_panic("cc_node(1)");
		}
		if ( n->large.fofs != n1->h.fofs ) {
			printf("%s\n",str);
			er_panic("cc_node(1-1)");
		}
		cc_node(str,n1);
	}
	if ( n->small.ptr ) {
		n1 = n->small.ptr;
		if ( n1->parent != &n->small ) {
			printf("%s\n",str);
			er_panic("cc_node(2)");
		}
		if ( n->small.fofs != n1->h.fofs ) {
			printf("%s\n",str);
			er_panic("cc_node(2-1)");
		}
		cc_node(str,n1);
	}
	if ( n->parent->ptr != n ) {
		printf("%s\n",str);
		er_panic("cc_node(3)");
	}
}

void
cc_root(char * str,FAVT_ROOT * r)
{
FAVT_NODE * n1;
	if ( r->node.ptr == 0 )
		return;
	n1 = r->node.ptr;
	if ( r->node.fofs != n1->h.fofs ) {
		printf("%s\n",str);
		er_panic("cc_root");
	}
	cc_node(str,r->node.ptr);
}

void
check_cache(char * str)
{
FAVT_HEADER * h;
int cnt;
	if ( favt_h.next == 0 )
		return;
	cnt = 200;
	for ( h = favt_h.next ; h != &favt_h ; h = h->next , cnt -- ) {
		if ( cnt < 0 )
			er_panic("chec_cache(2)");
		if ( h->type == PNT_FAVT_ROOT )
			cc_root(str,(FAVT_ROOT*)h);
		if ( h->type != PNT_FAVT_ROOT && h->type != PNT_FAVT_NODE )
			er_panic("check_cache(3)");
		if ( h->next == 0 )
			er_panic("check_cache(1)");
	}
}
