/**********************************************************************
 
	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	"change_endian.h"
#include	"filespace64.h"
#include	"favt64.h"
#include	"utils.h"
#include	"avt.h"


extern void (*change_endian_favt64_table_to_host[])();

typedef struct favt64_load_work {
	FAVT64_HEADER *		ret;
	U_INTEGER64		fofs;
	FAVT64_PTR *		parent;
	int			err;
} FAVT64_LOAD_WORK;

void favt64_load_header(PDB64 * p,
	PN64_HEADER * f,
	FAVT64_HEADER * ret,FAVT64_LOAD_WORK * w);

int favt64_load_root();
int favt64_load_node();
int cmp_favt64(FAVT64_HEADER * a,FAVT64_HEADER * b);
void insert_favt64_tree(FAVT64_HEADER * h);
void delete_favt64_tree(PDB64 * p,U_INTEGER64 fofs);
void insert_favt64_ring(FAVT64_HEADER * h);
void delete_favt64_ring(FAVT64_HEADER * h);
void _touch_ring64(void * h);
void touch_ring64(void * h1,void * h2);
FAVT64_PTR
get_favt64_ptr(U_INTEGER64 fofs,int type);
void get_favt64_ptr2(FAVT64_PTR * ptr,FAVT64_NODE * n);
void set_favt64_ptr(FAVT64_PTR * d,FAVT64_PTR s);
FAVT64_HEADER *
favt64_load(int * errp,
	PDB64 * p,FAVT64_ROOT * rt,
	U_INTEGER64 fofs,FAVT64_PTR * parent);
int favt64_write_node(FAVT64_HEADER * h);
int favt64_write_root(FAVT64_HEADER * h);
int favt64_write(FAVT64_HEADER * h);
int check_dirty64(FAVT64_HEADER * h,unsigned short reset);
int free_favt64_cache(FAVT64_HEADER * h);
void clear_cache64();
FAVT64_NODE *
g_ptr64(int * errp,FAVT64_ROOT * rt,FAVT64_PTR * ptr);
FAVT64_NODE *
small_node64(int * errp,FAVT64_ROOT * rt,FAVT64_NODE * n);
FAVT64_NODE *
large_node64(int * errp,FAVT64_ROOT * rt,FAVT64_NODE * n);
void
favt64_valance(int * errp,FAVT64_ROOT * p,FAVT64_PTR * favtp);
void free_bound_list64(BOUND64_LIST * b);
void favt64_alloc_header(PDB64 * p,FAVT64_HEADER * ret,PN64_HEADER * h);
void cc_node64(char * str,FAVT64_NODE * n);
void cc_root64(char * str,FAVT64_ROOT * r);


int (*favt64_func_table[])() = {
	0,
	favt64_load_root,
	favt64_load_node,
};

int (**func_table64[])() = {
	0,
	favt64_func_table,
};

FAVT64_HEADER	favt64_h;
AVT_NODE *	fr64_tree;
int		favt64_count;

void
init_favt64()
{
	favt64_h.next = favt64_h.prev = &favt64_h;
	fr64_tree = 0;
}

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

int
cmp_favt64(FAVT64_HEADER * a,FAVT64_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_favt64_tree(FAVT64_HEADER * h)
{
AVT_NODE * a,* b;

	a = d_alloc(sizeof(AVT_NODE));
	a->data = h;
	b = avt_insert(&fr64_tree,a,cmp_favt64);
	if ( b != a )
		er_panic("insert_favt64_tree");
}

void
delete_favt64_tree(PDB64 * p,U_INTEGER64 fofs)
{
AVT_NODE * a;
FAVT64_HEADER h;
	h.fofs = fofs;
	h.p = p;
	a = avt_delete(&fr64_tree,&h,cmp_favt64);
	if ( a )
		d_f_ree(a);
}



void
insert_favt64_ring(FAVT64_HEADER * h)
{

	h->prev = &favt64_h;
	h->next = favt64_h.next;
	h->prev->next = h;
	h->next->prev = h;
	favt64_count ++;
}

void
delete_favt64_ring(FAVT64_HEADER * h)
{

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

void
_touch_ring64(void * h)
{
	delete_favt64_ring((FAVT64_HEADER*)h);
	insert_favt64_ring((FAVT64_HEADER*)h);
}

void
touch_ring64(void * h1,void * h2)
{
	if ( h1 )
		_touch_ring64(h1);
	if ( h2 )
		_touch_ring64(h2);
}

FAVT64_PTR
get_favt64_ptr(U_INTEGER64 fofs,int type)
{
FAVT64_PTR ret;
	ret.fofs = fofs;
	ret.type = type;
	ret.ptr = 0;
	ret.flags = 0;
	return ret;
}

void
get_favt64_ptr2(FAVT64_PTR * ptr,FAVT64_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_favt64_ptr(FAVT64_PTR * d,FAVT64_PTR s)
{
	*d = s;
	if ( d->ptr )
		((FAVT64_NODE *)d->ptr)->parent = d;
	d->flags |= FAF_DIRTY;
}

FAVT64_HEADER *
favt64_load(int * errp,
	PDB64 * p,FAVT64_ROOT * rt,
	U_INTEGER64 fofs,FAVT64_PTR * parent)
{
FAVT64_LOAD_WORK w;
PN64_HEADER * h;
INTEGER64 size;
short type;

	if ( p == 0 )
		p = rt->h.p;
	w.fofs = fofs;
	w.ret = 0;
	w.parent = parent;
	w.err = 0;

	h = get_file_record64(&fofs,p,0,0);
	if ( h == 0 ) {
		if ( errp )
			*errp = AVT_E_LOAD_ERROR_1;
		return 0;
	}
	size = h->size;
	type = h->type;
	change_endian(size);
	change_endian(type);

	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_favt64_table_to_host[type&PNT_TYPE_MASK])(h);
	(*func_table64[PNT_GET_GROUP(type)][type&PNT_TYPE_MASK])
			(p,rt,h,&w);
	if ( errp && w.err )
		*errp = w.err;
	d_f_ree(h);
	return w.ret;
}



FAVT64_ROOT *
get_root64(int * errp,PDB64 * p,U_INTEGER64 fofs,void (*func)())
{
FAVT64_ROOT * r;
FAVT64_HEADER h;
AVT_NODE *a;

	h.fofs = fofs;
	h.p = p;
	a = avt_search(fr64_tree,&h,cmp_favt64);
	if ( a == 0 ) {
		r = (FAVT64_ROOT*)favt64_load(errp,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_favt64_tree((FAVT64_HEADER*)r);
	}
	else	r = a->data;
	touch_ring64(r,0);
	return r;
}

int
favt64_write_node(FAVT64_HEADER * h)
{
FAVT64_NODE * nn;
PN64_FAVT_NODE * n;
int size;
PDB64 * p;
	nn = (FAVT64_NODE*)h;

	p = h->p;
	n = d_alloc(sizeof(PN64_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(PN64_FAVT_NODE);
	change_endian_favt64_node(n);
	u_lseek64(p->fid,h->fofs,SEEK_SET);
	fa_write64_header(p->fid,n,size);
	d_f_ree(n);
	return 0;
}

int
favt64_write_root(FAVT64_HEADER * h)
{
FAVT64_ROOT * rr;
PN64_FAVT_ROOT r;

	rr = (FAVT64_ROOT*)h;
	r.node = rr->node.fofs;
	r.type = rr->type;
	r.h.type = PNT_FAVT_ROOT;
	r.h.size = sizeof(r);
	change_endian_favt64_root(&r);
	u_lseek64(h->p->fid,h->fofs,SEEK_SET);
	fa_write64_header(h->p->fid,&r,sizeof(r));
	return 0;
}

int
favt64_write(FAVT64_HEADER * h)
{

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

int
check_dirty64(FAVT64_HEADER * h,unsigned short reset)
{
FAVT64_NODE * n;
FAVT64_ROOT * r;
	if ( h->flags & FAF_DIRTY ) {
		h->flags &= ~reset;
		return 1;
	}
	switch ( h->type ) {
	case PNT_FAVT_NODE:
		n = (FAVT64_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 = (FAVT64_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_favt64_cache(FAVT64_HEADER * h)
{
FAVT64_NODE * n;
FAVT64_ROOT * r;

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

void
clear_cache64()
{
FAVT64_NODE * n;
FAVT64_ROOT * r;
FAVT64_HEADER * h, * h2;
	for ( ; favt64_count > FAVT_CACHE_SIZE ; ) {
		for ( h = favt64_h.prev ; h != &favt64_h ; ) {
			if ( h->flags & FAF_LOCK ) {
				h = h->prev;
				continue;
			}
			switch ( h->type ) {
			case PNT_FAVT_ROOT:
				r = (FAVT64_ROOT*)h;
				if ( r->node.ptr ) {
					h = h->prev;
					continue;
				}
				break;
			case PNT_FAVT_NODE:
				n = (FAVT64_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 == &favt64_h )
			er_panic("clear_cache");
		if ( check_dirty64(h,0) ) {
			for ( h2 = favt64_h.next ; h2 != &favt64_h ; 
					h2 = h2->next )
				if ( check_dirty64(h2,FAF_DIRTY) )
					favt64_write(h2);
		}
		h2 = h->prev;
		if ( free_favt64_cache(h) < 0 )
			er_panic("clear_cache(2)");
		h = h2;
	}
}

void
flush_favt64_cache(PDB64 * p)
{
FAVT64_HEADER * h1, * h2;
int f;

	for ( h1 = favt64_h.next ; h1 != &favt64_h ;) {
		if ( h1->p != p ) {
			h1 = h1->next;
			continue;
		}
		if ( check_dirty64(h1,FAF_DIRTY) )
			favt64_write(h1);
		h1 = h1->next;
	}
retry:
	f = 0;
	for ( h1 = favt64_h.next ; h1 != &favt64_h ; ) {
		if ( h1->p != p ) {
			h1 = h1->next;
			continue;
		}
		h2 = h1->next;
		if ( free_favt64_cache(h1) < 0 )
			f = 1;
		h1 = h2;
	}
	if ( f )
		goto retry;
}

void
favt64_lock(FAVT64_HEADER * h)
{
	h->flags |= FAF_LOCK;
}

void
favt64_unlock(FAVT64_HEADER * h)
{
	h->flags &= ~FAF_LOCK;
}

void
favt64_load_header(PDB64 * p,
	PN64_HEADER * f,FAVT64_HEADER * ret,FAVT64_LOAD_WORK * w)
{
	ret->type = f->type;
	ret->fofs = w->fofs;
	ret->flags = 0;
	ret->p = p;
	insert_favt64_ring(ret);
	clear_cache64();
}

int
favt64_load_root(
	PDB64 * p,FAVT64_ROOT * rt,PN64_FAVT_ROOT * f,
	FAVT64_LOAD_WORK * w)
{
FAVT64_ROOT * ret;

	ret = d_alloc(sizeof(*ret));
	ret->node = get_favt64_ptr(0,PNT_FAVT_NODE);
	favt64_load_header(p,&f->h,&ret->h,w);
	ret->node = get_favt64_ptr(f->node,PNT_FAVT_NODE);
	ret->type = f->type;
	w->ret = (FAVT64_HEADER*)ret;
	return 0;
}


int
favt64_load_node(
	PDB64 * p,FAVT64_ROOT * rt,PN64_FAVT_NODE * f,
	FAVT64_LOAD_WORK * w)
{
FAVT64_NODE * ret;

	if ( p == 0 )
		p = rt->h.p;
	ret = d_alloc(sizeof(*ret));
	ret->small = get_favt64_ptr(f->small,PNT_FAVT_NODE);
	ret->large = get_favt64_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(PN64_FAVT_NODE));
	ret->data_len = f->h.size - sizeof(PN64_FAVT_NODE);
	favt64_load_header(p,&f->h,&ret->h,w);
	memcpy(ret->data,f+1,f->h.size - sizeof(PN64_FAVT_NODE));
	(*rt->endian_func)(ret->data,ret->data_len);
	w->ret = (FAVT64_HEADER*)ret;

	return 0;
}

FAVT64_NODE *
g_ptr64(int * errp,FAVT64_ROOT * rt,FAVT64_PTR * ptr)
{
	if ( ptr->fofs == 0 )
		return 0;
	if ( ptr->ptr == 0 ) {
		ptr->ptr = favt64_load(errp,0,rt,ptr->fofs,ptr);
		((FAVT64_NODE *)(ptr->ptr))->parent = ptr;
	}
	return ptr->ptr;
}

FAVT64_NODE *
small_node64(int * errp,FAVT64_ROOT * rt,FAVT64_NODE * n)
{
FAVT64_NODE * ret;
	if ( n->small.fofs == 0 )
		return 0;
	if ( n->small.ptr )
		ret = n->small.ptr;
	else {
		ret = n->small.ptr = favt64_load(errp,
				0,rt,n->small.fofs,&n->small);
		ret->parent = &n->small;
	}
	touch_ring64(ret,0);
	return ret;
}


FAVT64_NODE *
large_node64(int * errp,FAVT64_ROOT * rt,FAVT64_NODE * n)
{
FAVT64_NODE * ret;

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

FAVT64_NODE *
root_node64(int * errp,FAVT64_ROOT * r)
{
FAVT64_NODE * ret;

	if ( r->node.fofs == 0 )
		return 0;
	if ( r->node.ptr )
		ret = r->node.ptr;
	else	ret = r->node.ptr = favt64_load(errp,
				0,r,r->node.fofs,&r->node);
	touch_ring64(ret,0);
	return ret;
}


void fset_level64(int * errp,FAVT64_ROOT * rt,FAVT64_NODE * avt);
FAVT64_NODE * _favt64_delete_small
	(int * errp,FAVT64_ROOT * rt,FAVT64_PTR*);
FAVT64_NODE * _favt64_delete_large
	(int * errp,FAVT64_ROOT * rt,FAVT64_PTR*);

void
fset_level64(int * errp,FAVT64_ROOT * p,FAVT64_NODE * favt)
{
int lev1,lev2;
	touch_ring64(p,favt);
	if ( favt == 0 )
		return;
	if ( favt->large.fofs )
		lev1 = large_node64(errp,p,favt)->level;
	else	lev1 = 0;
	if ( favt->small.fofs )
		lev2 = small_node64(errp,p,favt)->level;
	else	lev2 = 0;
	if ( lev1 > lev2 )
		favt->level = lev1 + 1;
	else	favt->level = lev2 + 1;
	favt->h.flags |= FAF_DIRTY;
}

void
favt64_valance(int * errp,FAVT64_ROOT * p,FAVT64_PTR * favtp)
{
FAVT64_NODE * b1, * b2, * b3, * b4, * b5;
int lev1,lev2;
FAVT64_NODE * g;
	if ( favtp->fofs == 0 )
		return;
	if ( (g=g_ptr64(errp,p,favtp))->large.fofs )
		lev1 = large_node64(errp,p,g)->level;
	else	lev1 = 0;
	if ( (g=g_ptr64(errp,p,favtp))->small.fofs )
		lev2 = small_node64(errp,p,g)->level;
	else	lev2 = 0;
	touch_ring64(p,g);
	if ( lev2 + 1 < lev1 ) {
		b1 = g_ptr64(errp,p,favtp);
		b2 = large_node64(errp,p,b1);
		b3 = small_node64(errp,p,b1);
		b4 = large_node64(errp,p,b2);
		b5 = small_node64(errp,p,b2);

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

		fset_level64(errp,p,b1);
		fset_level64(errp,p,b2);
	}
	else if ( lev1 + 1 < lev2 ) {
		b1 = g_ptr64(errp,p,favtp);
		b2 = small_node64(errp,p,b1);
		b3 = large_node64(errp,p,b1);
		b4 = small_node64(errp,p,b2);
		b5 = large_node64(errp,p,b2);

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

		fset_level64(errp,p,b1);
		fset_level64(errp,p,b2);
	}
}

FAVT64_NODE *
favt64_insert(int * errp,
	FAVT64_ROOT * p,FAVT64_PTR * favtp,FAVT64_NODE * a,int (*cmp)(),void * w)
{
FAVT64_NODE * ret;
FAVT64_NODE * g;
	if ( favtp->fofs == 0 ) {
		if ( a->data == 0 )
			er_panic("avt_insert(1)");
		get_favt64_ptr2(favtp,a);
		a->large = get_favt64_ptr(0,PNT_FAVT_NODE);
		a->small = get_favt64_ptr(0,PNT_FAVT_NODE);
		a->level = 1;
		a->h.flags |= FAF_DIRTY;
		return a;
	}
	g = g_ptr64(errp,p,favtp);
	favt64_lock(&g->h);
	switch ( (*cmp)(g->data,a->data,w) ) {
	case -1:
		ret = favt64_insert(errp,p,&g->large,a,cmp,w);
		break;
	case 0:
		ret = g;
		favt64_unlock(&g->h);
		return ret;
	case 1:
		ret = favt64_insert(errp,p,&g->small,a,cmp,w);
		break;
	}
	fset_level64(errp,p,g);
	favt64_valance(errp,p,favtp);
	favt64_unlock(&g->h);
	return ret;
}


FAVT64_NODE *
favt64_search(int * errp,
	FAVT64_ROOT * p,FAVT64_NODE * a,void * data,int (*cmp)(),void * w)
{
FAVT64_NODE * b;
	touch_ring64(p,a);
	if ( a )
		favt64_lock(&a->h);
	for ( ; a ; ) {
		switch ( (*cmp)(a->data,data,w) ) {
		case -1:
			b = large_node64(errp,p,a);
			if ( b )
				favt64_lock(&b->h);
			favt64_unlock(&a->h);
			a = b;
			break;
		case 0:
			favt64_unlock(&a->h);
			return a;
		case 1:
			b = small_node64(errp,p,a);
			if ( b )
				favt64_lock(&b->h);
			favt64_unlock(&a->h);
			a = b;
			break;
		default:
			er_panic("favt_search(1)");
		}
	}
	if ( errp )
		*errp = AVT_E_NO_OBJ;
	return 0;
}


int
favt64_trace_from_small(int * errp,
		FAVT64_ROOT * p,FAVT64_NODE * a,int (*func)(),void * work)
{
int ret;
	touch_ring64(p,a);
	if ( a == 0 )
		return 0;
	favt64_lock(&a->h);
	if ( a->small.fofs ) {
		ret = favt64_trace_from_small(
				errp,p,small_node64(errp,p,a),
					func,work);
		if ( ret ) {
			favt64_unlock(&a->h);
			return ret;
		}
	}
	ret = (*func)(a,work);
	if ( ret ) {
		favt64_unlock(&a->h);
		return ret;
	}
	if ( a->large.fofs ) {
		ret = favt64_trace_from_small(
				errp,p,large_node64(errp,p,a),func,work);
		if ( ret ) {
			favt64_unlock(&a->h);
			return ret;
		}
	}
	favt64_unlock(&a->h);
	return 0;
}



int
favt64_trace_from_large(int * errp,
		FAVT64_ROOT * p,FAVT64_NODE * a,int (*func)(),void * work)
{
int ret;

	touch_ring64(p,a);
	if ( a == 0 )
		return 0;
	favt64_lock(&a->h);
	if ( a->large.fofs ) {
		ret = favt64_trace_from_large(
				errp,p,large_node64(errp,p,a),
				func,work);
		if ( ret ) {
			favt64_unlock(&a->h);
			return ret;
		}
	}
	ret = (*func)(a,work);
	if ( ret ) {
		favt64_unlock(&a->h);
		return ret;
	}
	if ( a->small.fofs ) {
		ret = favt64_trace_from_large(
				errp,p,small_node64(errp,p,a),
				func,work);
		if ( ret ) {
			favt64_unlock(&a->h);
			return ret;
		}
	}
	favt64_unlock(&a->h);
	return 0;
}


typedef struct bound64_search_work {
	FAVT64_ROOT * 	p;
	void * 		data_min;
	void * 		data_max;
	int 		(*cmp)();
	BOUND64_LIST *	head;
	BOUND64_LIST *	tail;
	void *		cmp_work;
	int		limit;
	int		count;
} BOUND64_SEARCH_WORK;

int insert_lst64(int *,BOUND64_SEARCH_WORK * w,FAVT64_NODE * a);
int _favt64_bound_search(
	int * errp,
	BOUND64_SEARCH_WORK * w,
	FAVT64_NODE * a);

int
insert_lst64(int * errp,BOUND64_SEARCH_WORK * w,FAVT64_NODE * a)
{
BOUND64_LIST * lst;
	if ( w->limit >= 0 ) {
		if ( w->limit <= w->count ) {
			if ( errp )
				*errp = AVT_E_BOUND;
			return -1;
		}
	}
	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 = 0;

	if ( w->head == 0 )
		w->head = w->tail = lst;
	else {
		w->tail->next = lst;
		w->tail = lst;
	}

	w->count ++;
	return 0;
}

int
_favt64_bound_search(
	int * errp,
	BOUND64_SEARCH_WORK * w,
	FAVT64_NODE * a)
{
int cmp_max,cmp_min;
	touch_ring64(w->p,a);
	if ( a == 0 )
		return 0;
	favt64_lock(&a->h);
	cmp_max = (*w->cmp)(a->data,w->data_max,w->cmp_work);
	cmp_min = (*w->cmp)(a->data,w->data_min,w->cmp_work);
	if ( cmp_min > 0 ) {
		if ( _favt64_bound_search(errp,w,
				small_node64(errp,w->p,a)) < 0 )
			return -1;
	}
	if ( cmp_max <= 0 && cmp_min >= 0 ) {
		if ( insert_lst64(errp,w,a) < 0 )
			return -1;
	}
	if ( cmp_max < 0 ) {
		if( _favt64_bound_search(errp,w,
				large_node64(errp,w->p,a)) < 0 )
			return -1;
	}
	favt64_unlock(&a->h);
	return 0;
}

BOUND64_LIST *
favt64_bound_search(
	int * errp,
	FAVT64_ROOT * p,
	FAVT64_NODE * a,
	void *	data_min,
	void *	data_max,
	int	limit,
	int (*cmp)(),
	void * cw)
{
BOUND64_SEARCH_WORK w;
	w.p = p;
	w.data_min = data_min;
	w.data_max = data_max;
	w.head = w.tail = 0;
	w.cmp = cmp;
	w.cmp_work = cw;
	w.limit = limit;
	w.count = 0;
	_favt64_bound_search(errp,&w,a);
	return w.head;
}

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

FAVT64_NODE *
_favt64_delete_small(int * errp,FAVT64_ROOT *p,FAVT64_PTR * ap)
{
FAVT64_NODE * ret;
FAVT64_NODE * g;
	g = g_ptr64(errp,p,ap);
	if ( g->small.fofs ) {
		ret = _favt64_delete_small(errp,p,&g->small);
		fset_level64(errp,p,g);
		return ret;
	}
	ret = g;
	set_favt64_ptr(ap,g->large);
	favt64_valance(errp,p,ap);
	return ret;
}

FAVT64_NODE *
_favt64_delete_large(int * errp,FAVT64_ROOT * p,FAVT64_PTR * ap)
{
FAVT64_NODE * ret;
FAVT64_NODE * g;
	g = g_ptr64(errp,p,ap);
	favt64_lock(&g->h);

	if ( g->large.fofs ) {
		ret = _favt64_delete_large(errp,p,&g->large);
		fset_level64(errp,p,g);

		favt64_unlock(&g->h);

		return ret;
	}
	ret = g;
	set_favt64_ptr(ap,g->small);
	favt64_valance(errp,p,ap);

	touch_ring64(0,ret);

	favt64_unlock(&ret->h);

	return ret;
}




FAVT64_NODE *
favt64_delete(int * errp,
	FAVT64_ROOT * p,FAVT64_PTR * favtp,void * data,int (*cmp)(),void * w)
{
FAVT64_NODE * ret,* a;
FAVT64_NODE * g;

	if ( favtp->fofs == 0 ) {
		if ( errp )
			*errp = AVT_E_NO_OBJ;
		return 0;
	}
	g = g_ptr64(errp,p,favtp);
	touch_ring64(p,g);

	favt64_lock(&g->h);

	switch ( (*cmp)(g->data,data,w) ) {
	case -1:
		ret = favt64_delete(errp,p,&g->large,data,cmp,w);
		fset_level64(errp,p,g);

		favt64_unlock(&g->h);

		return ret;
	case 0:
		break;
	case 1:
		ret = favt64_delete(errp,p,&g->small,data,cmp,w);
		fset_level64(errp,p,g);

		favt64_unlock(&g->h);

		return ret;
	default:
		er_panic("favt_delete(!)");
	}
	ret = g;
	ret->parent = 0;
	if ( g->large.fofs == 0 )
		set_favt64_ptr(favtp,g->small);
	else if ( g->small.fofs == 0 )
		set_favt64_ptr(favtp,g->large);
	else if ( small_node64(errp,p,g)->level 
			< large_node64(errp,p,g)->level ) {
		a = _favt64_delete_small(errp,p,&g->large);
		set_favt64_ptr(&a->large,g->large);
		set_favt64_ptr(&a->small,g->small);
		get_favt64_ptr2(favtp,a);
	}
	else {
		a = _favt64_delete_large(errp,p,&g->small);
		set_favt64_ptr(&a->large,g->large);
		set_favt64_ptr(&a->small,g->small);
		get_favt64_ptr2(favtp,a);
	}
	g = g_ptr64(errp,p,favtp);
	fset_level64(errp,p,g);
	favt64_valance(errp,p,favtp);

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

	touch_ring64(0,ret);

	favt64_unlock(&ret->h);

	return ret;
}

typedef struct favt64_test {
	FAVT64_ROOT *	p;
	void *		data;
	int 		(*cmp)();
} FAVT64_TEST;

int favt64_test_func(int * errp,FAVT64_NODE * a,FAVT64_TEST * w,void * cmp_work);

int favt64_test_func(int * errp,FAVT64_NODE * a,FAVT64_TEST * w,void * cmp_work)
{
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,cmp_work) < 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_node64(errp,w->p,a)->level;
	else	lev1 = 0;
	if ( a->small.fofs )
		lev2 = small_node64(errp,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
favt64_test(int * errp,FAVT64_ROOT * p,FAVT64_NODE * a,int (*cmp)())
{
FAVT64_TEST test;
	test.data = 0;
	test.cmp = cmp;
	test.p = p;
	favt64_trace_from_small(errp,p,a,favt64_test_func,&test);
}


void
favt64_alloc_header(PDB64 * p,FAVT64_HEADER * ret,PN64_HEADER * h)
{
U_INTEGER64 fofs;

	fofs = alloc_filespace64(p,h);

	ret->type = h->type;
	ret->fofs = fofs;
	ret->flags = FAF_DIRTY;
	ret->p = p;
	insert_favt64_ring(ret);
	clear_cache64(p);
}


FAVT64_ROOT *
favt64_alloc_root(PDB64 * p,int type,void (*func)())
{
FAVT64_ROOT * ret;
PN64_HEADER h;
	ret = d_alloc(sizeof(*ret));
	h.type = PNT_FAVT_ROOT;
	h.size = sizeof(PN64_FAVT_ROOT);
	favt64_alloc_header(p,&ret->h,&h);
	ret->type = type;
	ret->endian_func = func;
	ret->node = get_favt64_ptr(0,PNT_FAVT_NODE);
	insert_favt64_tree((FAVT64_HEADER*)ret);
	return ret;
}


FAVT64_NODE *
favt64_alloc_node(FAVT64_ROOT * rt,
	void * data,int data_size)
{
FAVT64_NODE * ret;
PN64_HEADER h;
PDB64 * p;
	p = rt->h.p;
	ret = d_alloc(sizeof(*ret));
	h.type = PNT_FAVT_NODE;
	h.size = sizeof(PN64_FAVT_NODE)+data_size;
	ret->root = rt;
	ret->small = get_favt64_ptr(0,PNT_FAVT_NODE);
	ret->large = get_favt64_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;
	favt64_alloc_header(p,&ret->h,&h);
	return ret;
}


void
favt64_free_root(FAVT64_ROOT * rt)
{
	if ( rt == 0  )
		return;
	if ( check_dirty64(&rt->h,FAF_DIRTY) )
		favt64_write(&rt->h);
	delete_favt64_tree(rt->h.p,rt->h.fofs);
	delete_favt64_ring(&rt->h);
	free_filespace64(rt->h.p,rt->h.fofs);
}

void
favt64_free_node(FAVT64_NODE * n)
{
	if ( n == 0 )
		return;
	if ( check_dirty64(&n->h,FAF_DIRTY) )
		favt64_write(&n->h);
	delete_favt64_ring(&n->h);
	free_filespace64(n->h.p,n->h.fofs);
	if ( n->data )
		d_f_ree(n->data);
	d_f_ree(n);
}

void
cc_node64(char * str,FAVT64_NODE * n)
{
FAVT64_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_node64(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_node64(str,n1);
	}
	if ( n->parent->ptr != n ) {
		printf("%s\n",str);
		er_panic("cc_node(3)");
	}
}

void
cc_root64(char * str,FAVT64_ROOT * r)
{
FAVT64_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_node64(str,r->node.ptr);
}

void
check_cache64(char * str)
{
FAVT64_HEADER * h;
int cnt;
	if ( favt64_h.next == 0 )
		return;
	cnt = 200;
	for ( h = favt64_h.next ; h != &favt64_h ;
			h = h->next , cnt -- ) {
		if ( cnt < 0 )
			er_panic("chec_cache(2)");
		if ( h->type == PNT_FAVT_ROOT )
			cc_root64(str,(FAVT64_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)");
	}
}
