/**********************************************************************
 
	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	<errno.h>
#include	"memory_debug.h"
#include	"change_endian.h"
#include	"pdb64.h"
#include	"filespace64.h"
#include	"favt64.h"
#include	"dtree64.h"

typedef struct acc_pn_dtree_data64 {
	ACC_HEADER64	a;
	PN_DTREE_DATA64	d;
} ACC_PN_DTREE_DATA64;

void
change_endian_dtree_data_to_host64(PN_DTREE_DATA64 * d)
{
int size;
int len;
int i;
	change_endian_header64(&d->h);
	len = (d->h.size - sizeof(d->h))/sizeof(unsigned int);
	for ( i = 0 ; i < len ; i ++ ) {
		change_endian_i(d->data[i]);
	}
}

void
change_endian_dtree_data_to_net64(PN_DTREE_DATA64 * d)
{
int size;
int len;
int i;
	len = (d->h.size - sizeof(d->h))/sizeof(unsigned int);
	for ( i = 0 ; i < len ; i ++ ) {
		change_endian_i(d->data[i]);
	}
	change_endian_header64(&d->h);
}

void
dtree_endian64(DTREE_NODE64 * n)
{
	change_endian_i(n->tree);
	change_endian_i(n->data);
}

int
dtree_cmp64(DTREE_NODE64 * n1,DTREE_NODE64 * n2)
{

	if ( n1->ch < n2->ch )
		return -1;
	if ( n1->ch > n2->ch )
		return 1;
	return 0;
}


FAVT_ROOT64 *
alloc_dtree64(PDB64 * p,int type)
{
	return favt_alloc_root64(p,type|FAST_DTREE_ROOT,dtree_endian64);
}

int
data_segment64(FAVT_ROOT64 * r,FAVT_NODE64 * n,unsigned int old,unsigned int data)
{
DTREE_NODE64 * dp;
ACC_PN_DTREE_DATA64 * dd;
int len,i;
	dp = n->data;
	dd = read_filespace64(r->h.p,dp->data);
	if ( dd == 0 )
		return -1;
	change_endian_dtree_data_to_host64(&dd->d);
	if ( dd->d.h.type != PNT_DTREE_DATA ) {
		n->h.flags |= FAF_DIRTY;
		if ( old == 0 ) {
			dd = d_re_alloc(dd,PN_DTREE_DATA_S64(2)
				+sizeof(ACC_HEADER64));
			dd->d.h.type = PNT_DTREE_DATA;
			dd->d.h.size = PN_DTREE_DATA_S64(2);
			dd->d.data[0] = dp->data;
			dd->d.data[1] = data;
		}
		else if ( old == dp->data ) {
			dp->data = data;
			return 0;
		}
		else
			return -1;
	}
	else if ( old ) {
		len = (dd->d.h.size - sizeof(dd->d.h))/sizeof(unsigned int);
		for ( i = 0 ; i < len ; i ++ ) {
			if ( dd->d.data[i] == old )
				break;
		}
		if ( i == len )
			return -1;
		dd->d.data[i] = data;
		change_endian_dtree_data_to_net64(&dd->d);
		write_filespace64(r->h.p,dd);
		d_f_ree(dd);
		return 0;
	}
	else {
		len = (dd->d.h.size - sizeof(dd->d.h))/sizeof(int);
		dd = d_re_alloc(dd,PN_DTREE_DATA_S64(len+1)+sizeof(ACC_HEADER64));
		dd->d.h.size = PN_DTREE_DATA_S64(len+1);
		dd->d.data[len] = data;
		free_filespace64(r->h.p,dp->data);
		n->h.flags |= FAF_DIRTY;
	}
	dd->a.fofs = alloc_filespace64(r->h.p,&dd->d.h);
	change_endian_dtree_data_to_net64(&dd->d);
	write_filespace64(r->h.p,dd);
	d_f_ree(dd);
	return 0;
}

int
insert_dtree64(FAVT_ROOT64 * r,L_CHAR * str,unsigned int old,unsigned int data)
{
FAVT_NODE64 * n;
DTREE_NODE64 dn;
DTREE_NODE64 * dp;

	for ( ; ; str ++ ) {
		dn.ch = *str;

		n = favt_search64(r,root_node64(r),&dn,dtree_cmp64);
		if ( n == 0 ) {
			dn.data = 0;
			dn.tree = 0;
			n = favt_alloc_node64(r,&dn,sizeof(dn));
			favt_insert64(r,&r->node,n,dtree_cmp64);
		}
		dp = n->data;
		if ( str[1] == 0 ) {
			if ( dp->data )
				return data_segment64(r,n,old,data);
			if ( old && old != dp->data )
				return -1;
			dp->data = data;
			n->h.flags |= FAF_DIRTY;
			return 0;
		}
		if ( dp->tree ) {
			r = get_root64(r->h.p,dp->tree,dtree_endian64);
			if ( r == 0 )
				er_panic("insert_dtree(1)");
		}
		else {
			r = favt_alloc_root64(
				r->h.p,FAT_DTREE_NODE,dtree_endian64);
			if ( r == 0 )
				er_panic("insert_dtree(2)");
			dp->tree = r->h.fofs;
			n->h.flags |= FAF_DIRTY;
		}
	}
}

unsigned int
search_dtree64(int * sizep,FAVT_ROOT64 * r,L_CHAR * str,int perfect_flag)
{
DTREE_NODE64 dn;
DTREE_NODE64 * dp;
FAVT_NODE64 * n;
int size;
unsigned int ret;
	size = 0;
	ret = 0;
	for ( ; ; str ++ , size ++ ) {
		dn.ch = *str;
		n = favt_search64(r,root_node64(r),&dn,dtree_cmp64);
		if ( n == 0 ) {
			if ( perfect_flag )
				return 0;
			if ( sizep )
				*sizep = size;
			return ret;
		}
		dp = n->data;
		if ( str[1] == 0 ) {
			if ( sizep )
				*sizep = size+1;
		 	return dp->data;
		}
		if ( dp->tree == 0 ) {
			if ( perfect_flag )
				return 0;
			if ( sizep )
				*sizep = size+1;
			return dp->data;
		}
		ret = dp->data;
		r = get_root64(r->h.p,dp->tree,dtree_endian64);
		if ( r == 0 )
			er_panic("search_dtree");
	}
}


int
dtree_trace_func64(FAVT_NODE64 * a,DTREE_TRACE_WORK64 * w)
{
DTREE_NODE64 * dp;
ACC_PN_DTREE_DATA64 * dd;
int len;
int ret;
int i;
FAVT_ROOT64 * r;

L_CHAR ch[2];
check_cache64("dtree_trace_func");

	dp = a->data;

	len = l_strlen(w->target);
	w->target = d_re_alloc(w->target,sizeof(L_CHAR)*(len+2));
	w->target[len] = dp->ch;
	w->target[len+1] = 0;
	if ( dp->data ) {
		dd = read_filespace64(w->p,dp->data);
		if ( dd == 0 )
			return -1;
		change_endian_dtree_data_to_host64(&dd->d);
		if ( dd->d.h.type != PNT_DTREE_DATA ) {
			change_endian_dtree_data_to_host64(&dd->d);
			ret = (*w->func)(dp->data,dd,w);
			if ( ret )
				goto end;
		}
		else {
			len = (dd->d.h.size - PN_DTREE_DATA_S64(0))/sizeof(int);
			for ( i = 0 ; i < len ; i ++ ) {
				ret = (*w->func)(dd->d.data[i],0,w);
				if ( ret )
					goto end;
			}
		}
		d_f_ree(dd);
	}
	if ( dp->tree ) {
		r = get_root64(w->p,dp->tree,dtree_endian64);
		if ( r == 0 )
			er_panic("search_dtree");
		ret = (*w->trace)(r,root_node64(r),dtree_trace_func64,w);
	}
end:

	w->target[len] = 0;
 	return ret;
}

int
dtree_trace_from_small64(FAVT_ROOT64 * r,int (*func)(),void * work)
{
DTREE_TRACE_WORK64 w;
int ret;
	w.func = func;
	w.w = work;
	w.p = r->h.p;
	w.trace = favt_trace_from_small64;
	w.target = d_alloc(sizeof(L_CHAR));
	w.target[0] = 0;
	ret = favt_trace_from_small64(r,root_node64(r),dtree_trace_func64,&w);
	d_f_ree(w.target);
	return ret;
}



int
dtree_trace_from_large64(FAVT_ROOT64 * r,int (*func)(),void * work)
{
DTREE_TRACE_WORK64 w;

check_cache64("dtree_trace_from_large...");
	w.func = func;
	w.w = work;
	w.p = r->h.p;
	w.trace = favt_trace_from_large64;
	w.target = d_alloc(sizeof(L_CHAR));
	w.target[0] = 0;
check_cache64("dtree_trace_from_large...2");
	return favt_trace_from_large64(r,root_node64(r),dtree_trace_func64,&w);
}


