/**********************************************************************
 
	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	<fcntl.h>
#include	<limits.h>
#include	<float.h>
#include	"change_endian.h"
#include	"long_char.h"
#include	"memory_debug.h"
#include	"pdb.h"
#include	"filespace.h"
#include	"favt.h"
#include	"pmd.h"
#include	"resource.h"
#include	"lump.h"
#include	"xl.h"

int
lock_db(PMD_WORK * w)
{
CALL_LOCK_DESCRIPTER ret;

	ret = call_lock(l_string(std_cm,w->path),CLT_WRITE_LOCK);
	if ( cl_error_check(ret) ) {
		w->err = -2;
		w->lock_id = cl_invalid();
		return -1;
	}
	w->lock_id = ret;
	return 0;
}

void
unlock_db(PMD_WORK * w)
{
	call_unlock(w->lock_id);
}



void
change_endian_pmd_header(PN_PMD_HEADER * h)
{
	change_endian_header(&h->h);
	change_endian_l_char(h->unit_path);
	change_endian_i(h->flags);
	change_endian_i(h->ix_tl_x);
	change_endian_i(h->ix_tl_y);
	change_endian_i(h->ix_br_x);
	change_endian_i(h->ix_br_y);
	change_endian_i(h->ix_content_period_from);
	change_endian_i(h->ix_content_period_to);
	change_endian_i(h->ix_resolution);
	change_endian_i(h->ix_target);
	change_endian_i(h->ix_timeout);
}

void
change_endian_pmd_node(PN_PMD_NODE * n)
{
	change_endian_header(&n->h);
	change_endian_gb_rect(&n->minrect);
	change_endian_f(n->resolution);
	change_endian_gb_time(&n->content_period_from);
	change_endian_gb_time(&n->content_period_to);
	change_endian_i(n->timeout);
	change_endian_i(n->md_fofs);
	change_endian_s(n->category);
	change_endian_s(n->target);
	change_endian_l_char(n->str);
}


void
change_endian_pmd_md(PN_PMD_MD * md)
{
	change_endian_header(&md->h);
	change_endian_i(md->next_fofs);
	change_endian_s(md->qualifier);
	change_endian_s(md->inherit);
	change_endian_s(md->type);
	change_endian_s(md->data);
}

void
change_endian_pmd_ofslist_to_net(PN_PMD_OFSLIST * ol)
{
int n,i;
	n = (ol->h.size-sizeof(PN_HEADER))/sizeof(unsigned int);
	change_endian_header(&ol->h);
	for ( i = 0 ; i < n ; i ++ ){
		change_endian_i(ol->fofs[i]);
	}
}

void
change_endian_pmd_ofslist_to_host(PN_PMD_OFSLIST * ol)
{
int n,i;
	change_endian_header(&ol->h);
	n = (ol->h.size-sizeof(PN_HEADER))/sizeof(unsigned int);
	for ( i = 0 ; i < n ; i ++ ){
		change_endian_i(ol->fofs[i]);
	}
}

int
cmp_floating(FLOATING_INDEX * f1,FLOATING_INDEX * f2)
{
	if ( f1->d < f2->d )
		return -1;
	if ( f1->d > f2->d )
		return 1;
	return 0;
}

int
cmp_timeout(TIMEOUT_INDEX * ix1,TIMEOUT_INDEX * ix2)
{
	if ( ix1->timeout < ix2->timeout )
		return -1;
	if ( ix1->timeout > ix2->timeout )
		return 1;
	return 0;
}

int
new_db(char * path,L_CHAR * unit_path)
{
PDB * p;
L_CHAR * encoding;
ACC_PN_PMD_HEADER * h;
FAVT_ROOT * rt;
int ret;
	encoding = l_string(std_cm,"UCS4");
	p = open_filespace(
		path,O_CREAT|O_RDWR|O_TRUNC,0644,
		PF_USEFREELIST,FT_ADDRESS_LUMP,encoding,0,0);
	if ( p == 0 )
		return -1;
	ret = -1;
	h = d_alloc(sizeof(*h));
	h->h.fofs = 0;
	h->d.h.size = sizeof(h->d);
	h->d.h.type = PNT_PMD_HEADER;
	h->h.fofs = alloc_filespace(p,&h->d.h);
	strcpy(h->d.version,PMD_VERSION);
	h->d.flags = 0;
	rt = favt_alloc_root(p,FAT_PMD_FLOATING,favt_floating_endian);
	if ( rt == 0 ) {
		ret = 0;
		goto end;
	}
	h->d.ix_tl_x = rt->h.fofs;
	rt = favt_alloc_root(p,FAT_PMD_FLOATING,favt_floating_endian);
	if ( rt == 0 ) {
		ret = 0;
		goto end;
	}
	h->d.ix_tl_y = rt->h.fofs;
	rt = favt_alloc_root(p,FAT_PMD_FLOATING,favt_floating_endian);
	if ( rt == 0 ) {
		ret = 0;
		goto end;
	}
	h->d.ix_br_x = rt->h.fofs;
	rt = favt_alloc_root(p,FAT_PMD_FLOATING,favt_floating_endian);
	if ( rt == 0 ) {
		ret = 0;
		goto end;
	}
	h->d.ix_br_y = rt->h.fofs;
	rt = favt_alloc_root(p,FAT_PMD_FLOATING,favt_floating_endian);
	if ( rt == 0 ) {
		ret = 0;
		goto end;
	}
	h->d.ix_resolution = rt->h.fofs;
	rt = favt_alloc_root(p,FAT_PMD_GB_TIME,favt_gb_time_endian);
	if ( rt == 0 ) {
		ret = 0;
		goto end;
	}
	h->d.ix_content_period_from = rt->h.fofs;
	rt = favt_alloc_root(p,FAT_PMD_GB_TIME,favt_gb_time_endian);
	if ( rt == 0 ) {
		ret = 0;
		goto end;
	}
	h->d.ix_content_period_to = rt->h.fofs;
	rt = favt_alloc_root(p,FAT_PMD_STRING,favt_string_endian);
	if ( rt == 0 ) {
		ret = 0;
		goto end;
	}
	h->d.ix_target = rt->h.fofs;
	rt = favt_alloc_root(p,FAT_PMD_TIMEOUT,favt_timeout_endian);
	if ( rt == 0 ) {
		ret = 0;
		goto end;
	}
	h->d.ix_timeout = rt->h.fofs;
	memcpy(h->d.unit_path,unit_path,
		(l_strlen(unit_path)+1)*sizeof(L_CHAR));

	change_endian_pmd_header(&h->d);
	write_filespace(p,h);
	ret = 0;

end:
printf("new_db 6\n");
	close_filespace(p);
	if ( h )
		d_f_ree(h);
printf("new_db 7\n");
	return ret;
}

void
free_pmd_work(PMD_WORK * w)
{
	w->p = 0;
	if ( w->path )
		d_f_ree(w->path);
	w->path = 0;
}

void
open_db(PMD_WORK * w,L_CHAR * db,L_CHAR * unit_path,int mode)
{
PN_PMD_HEADER * rh;
int fofs;
char * pp;

	w->err = 0;
	w->path = 0;
	w->p = 0;
	w->path = get_fullpath(db);
	if ( lock_db(w) < 0 )
		return;

retry:
	w->p = open_filespace(
		w->path,mode,
		0644,PF_USEFREELIST,FT_ADDRESS_LUMP,0,0,0);
	if ( w->p == 0 ) {
		log_printf(LOG_SYSTEM,"remake lump (%s) because open error",
			n_string(std_cm,db));
	remake:
		if ( unit_path == 0 ) {
			w->err = -1;
			return;
		}
		if ( new_db(w->path,unit_path) < 0 ) {
			free_pmd_work(w);
			w->err = -1;
			return;
		}
		goto retry;
	}
	else {
		fofs = 0;
		rh = get_file_record(&fofs,w->p,PNT_PMD_HEADER);
		if ( rh == 0 ) {
			close_filespace(w->p);
			log_printf(LOG_SYSTEM,
				"remake lump (%s) because invalid format",
				n_string(std_cm,db));
			goto remake;
		}
		w->pmd_h.d = *rh;
		d_f_ree(rh);
		w->pmd_h.h.fofs = fofs;
		change_endian_pmd_header(&w->pmd_h.d);
		if ( (w->pmd_h.d.flags & PMDF_WRITE) ||
				strcmp(w->pmd_h.d.version,PMD_VERSION) ||
				unit_path &&
				l_strcmp(w->pmd_h.d.unit_path,unit_path)
				 ) {
			close_filespace(w->p);
			pp = w->path;
			w->path = 0;
			free_pmd_work(w);
			w->path = pp;
			log_printf(LOG_SYSTEM,
	"remake lump (%s) because incomplete write/invalid version/invalid unit",
				n_string(std_cm,db));
			goto remake;
		}
	}
	w->pmd_h.d.flags = PMDF_WRITE;
	change_endian_pmd_header(&w->pmd_h.d);
	write_filespace(w->p,&w->pmd_h);
	change_endian_pmd_header(&w->pmd_h.d);
}

void
close_db(PMD_WORK * w)
{
	if ( w->p )
		flush_filespace(w->p);
	w->pmd_h.d.flags = 0;
	change_endian_pmd_header(&w->pmd_h.d);
	if ( w->p ) {
		write_filespace(w->p,&w->pmd_h);
		close_filespace(w->p);
	}
	unlock_db(w);
	free_pmd_work(w);
}

void
free_temp(PMD_TEMP * t)
{
	if ( t == 0 )
		return;
	if ( t->crd )
		d_f_ree(t->crd);
	if ( t->target )
		d_f_ree(t->target);
	free_bib_list(t->md_list);
	d_f_ree(t);
}

void
free_pmd_temp_list(PMD_TEMP_LIST * tl)
{
PMD_TEMP_LIST * tl1;
	for ( ; tl ; ) {
		tl1 = tl->next;
		free_temp(tl->temp);
		d_f_ree(tl);
		tl = tl1;
	}
}

BIB_LIST *
get_bib_list_db(PMD_WORK * w,unsigned int fofs)
{
BIB_LIST * ret, * bl;
ACC_PN_PMD_MD * md;
	ret = 0;
	for ( ; fofs ; ) {
		md = read_filespace(w->p,fofs);
		change_endian_pmd_md(&md->d);
		if ( md->d.h.type != PNT_PMD_MD ) {
			free_bib_list(ret);
			return 0;
		}
		bl = d_alloc(sizeof(*bl));
		bl->namespace = ll_copy_str(md->d.str);
		bl->qualifier 
		  = ll_copy_str(&md->d.str[md->d.qualifier]);
		bl->inherit = md->d.inherit;
		bl->type = ll_copy_str(&md->d.str[md->d.type]);
		bl->data = ll_copy_str(&md->d.str[md->d.data]);
		ret = insert_bib_list(ret,bl);
		fofs = md->d.next_fofs;
		d_f_ree(md);
	}
	return ret;
}

PMD_TEMP *
get_temp_db(PMD_WORK * w,unsigned int fofs)
{
ACC_PN_PMD_NODE * n;
PMD_TEMP * ret;
	if ( fofs == 0 )
		return 0;
	n = read_filespace(w->p,fofs);
	if ( n == 0 )
		return 0;
	change_endian_pmd_node(&n->d);
	if ( n->d.h.type != PNT_PMD_NODE ) {
		d_f_ree(n);
		return 0;
	}
	ret = d_alloc(sizeof(*ret));
	ret->fofs = n->h.fofs;
	ret->minrect = n->d.minrect;
	ret->resolution = n->d.resolution;
	ret->content_period_from = n->d.content_period_from;
	ret->content_period_to = n->d.content_period_to;
	ret->timeout = n->d.timeout;
	ret->md_fofs = n->d.md_fofs;
	ret->category = n->d.category;
	ret->crd = ll_copy_str(n->d.str);
	ret->target = ll_copy_str(&n->d.str[n->d.target]);
	ret->md_list = get_bib_list_db(w,n->d.md_fofs);
	d_f_ree(n);
	return ret;
}

int p_test;

int
get_content_period_w3c_dtf(
	L_CHAR * data,
	GB_TIME * from,
	GB_TIME * to)
{
char * buf;
char * ptr;
int ret;
	buf = ln_copy_str(std_cm,data);
	ret = -1;
	for ( ptr = buf ; *ptr ; ptr ++ )
		if ( *ptr == '/' ) {
			*ptr = 0;
			ptr ++;
			if ( w3c_dtf_to_gb_time(from,buf) < 0 ) {
				if ( w3c_dtf_to_gb_time(to,ptr) < 0 )
					goto end;
				from->year = FLT_MIN;
				from->month = -1;
				from->date = -1;
				from->day = -1;
				from->hour = -1;
				from->min = -1;
				from->sec = -1;
			}
			else if ( w3c_dtf_to_gb_time(to,ptr) < 0 ) {
				to->year = FLT_MAX;
				to->month = -1;
				to->date = -1;
				to->day = -1;
				to->hour = -1;
				to->min = -1;
				to->sec = -1;
			}
			ret = 0;
			goto end;
		}
	if ( w3c_dtf_to_gb_time(from,buf) < 0 )
		goto end;
	if ( w3c_dtf_to_gb_time(to,buf) < 0 )
		goto end;
	ret = 0;
end:
	d_f_ree(buf);
	return ret;
}

int
get_content_period(
	BIB_LIST * bl,
	GB_TIME * from,
	GB_TIME * to)
{
	bl = search_bib_list(bl,
		0,
		0,
		l_string(std_cm,"content.period"));
	if ( bl == 0 )
		return -1;
	if ( l_strcmp(bl->type,l_string(std_cm,"W3C-DTF")) == 0 )
		return get_content_period_w3c_dtf(bl->data,from,to);
	else	return -1;
}

PMD_TEMP *
get_temp_resource(RESOURCE * r,
	L_CHAR *crd,
	GB_RECT * minrect,
	REAL1 resolution)
{
PMD_TEMP * ret;

	if ( r->h.bib == 0 )
		return 0; 
	ret = d_alloc(sizeof(*ret));
	ret->fofs = 0;
	ret->minrect = *minrect;
	ret->resolution = resolution;
	if ( get_content_period(
		r->h.bib,
		&ret->content_period_from,
		&ret->content_period_to) < 0 ) {

		d_f_ree(ret);
		return 0;
	}
	ret->timeout = 0;
	ret->md_fofs = 0;
	ret->category = r->h.type;
	ret->crd = ll_copy_str(crd);
	ret->target = ll_copy_str(get_url_str2(&r->h.entry));
	ret->md_list = copy_bib_list(r->h.bib);
	return ret;
}

extern int test_gb_time;

int
cmp_temp(PMD_TEMP * t1,PMD_TEMP * t2)
{
	if ( t1->minrect.tl.x != t2->minrect.tl.x )
		return -1;
	if ( t1->minrect.tl.y != t2->minrect.tl.y )
		return -1;
	if ( t1->minrect.br.x != t2->minrect.br.x )
		return -1;
	if ( t1->minrect.br.y != t2->minrect.br.y )
		return -1;
	if ( t1->resolution != t2->resolution )
		return -1;
	if ( cmp_gb_time(&t1->content_period_from,&t2->content_period_from) )
		return -1;
	if ( cmp_gb_time(&t1->content_period_to,&t2->content_period_to) )
		return -1;
	if ( t1->category != t2->category )
		return -1;
	if ( l_strcmp(t1->crd,t2->crd) )
		return -1;
	if ( l_strcmp(t1->target,t2->target) )
		return -1;
	return cmp_bib_list(t1->md_list,t2->md_list);
}

unsigned int
alloc_bib_list(PMD_WORK * w,BIB_LIST * b)
{
ACC_PN_PMD_MD * md;
int q_len,type_len,data_len,ns_len;
L_CHAR * ptr;
int fofs;
	if ( b == 0 )
		return 0;
	ns_len = l_strlen(b->namespace);
	q_len = l_strlen(b->qualifier);
	type_len = l_strlen(b->type);
	data_len = l_strlen(b->data);
	md = d_alloc(PN_PMD_MD_S(q_len,ns_len,type_len,data_len)
			+ sizeof(ACC_HEADER));
	md->d.h.type = PNT_PMD_MD;
	md->d.h.size = PN_PMD_MD_S(q_len,ns_len,type_len,data_len);
	md->d.next_fofs = alloc_bib_list(w,b->next);
	ptr = md->d.str;
	l_strcpy(ptr,b->namespace);
	ptr += ns_len + 1;
	l_strcpy(ptr,b->qualifier);
	ptr += q_len + 1;
	l_strcpy(ptr,b->type);
	ptr += type_len + 1;
	l_strcpy(ptr,b->data);
	md->d.inherit = b->inherit;
	md->d.qualifier = ns_len + 1;
	md->d.type = md->d.qualifier + q_len + 1;
	md->d.data = md->d.type + type_len + 1;
	md->h.fofs = alloc_filespace(w->p,&md->d.h);
	change_endian_pmd_md(&md->d);
	write_filespace(w->p,md);
	fofs = md->h.fofs;
	d_f_ree(md);
	return fofs;
}

void
free_pmd_md(PMD_WORK * w,unsigned int fofs)
{
ACC_PN_PMD_MD * md;
	for ( ; fofs ; ) {
		md = read_filespace(w->p,fofs);
		if ( md == 0 )
			return;
		free_filespace(w->p,fofs);
		change_endian_pmd_md(&md->d);
		fofs = md->d.next_fofs;
		d_f_ree(md);
	}
}

void
free_pmd_node(PMD_WORK * w,unsigned int fofs)
{
ACC_PN_PMD_NODE * n;
	n = read_filespace(w->p,fofs);
	if ( n == 0 )
		return;
	change_endian_pmd_node(&n->d);
	free_pmd_md(w,n->d.md_fofs);
	free_filespace(w->p,fofs);
	d_f_ree(n);
}



unsigned int
write_temp(PMD_WORK * w,PMD_TEMP * t,int type)
{
ACC_PN_PMD_NODE * n;
int crd_len,target_len;
unsigned int fofs;

	crd_len = l_strlen(t->crd);
	target_len = l_strlen(t->target);
	n = d_alloc(PN_PMD_NODE_S(crd_len,target_len)
		+ sizeof(ACC_HEADER));

	switch ( type ) {
	case WRITE_TEMP_ALLOC:
		t->md_fofs = alloc_bib_list(w,t->md_list);
		break;
	case WRITE_TEMP_UPDATE:
		break;
	default:
		er_panic("write_temp(1)");
	}
	n->d.minrect = t->minrect;
	n->d.resolution = t->resolution;
	n->d.content_period_from = t->content_period_from;
	n->d.content_period_to = t->content_period_to;
	n->d.timeout = t->timeout;
	n->d.md_fofs = t->md_fofs;
	n->d.category = t->category;
	n->d.target = crd_len+1;
	l_strcpy(n->d.str,t->crd);
	l_strcpy(&n->d.str[n->d.target],t->target);
	n->d.h.type = PNT_PMD_NODE;
	n->d.h.size = PN_PMD_NODE_S(crd_len,target_len);
	switch ( type ) {
	case WRITE_TEMP_ALLOC:
		t->fofs = n->h.fofs = alloc_filespace(w->p,&n->d.h);
		break;
	case WRITE_TEMP_UPDATE:
		n->h.fofs = t->fofs;
		break;
	default:
		er_panic("write_temp(2)");
	}
	change_endian_pmd_node(&n->d);
	write_filespace(w->p,n);
	fofs = t->fofs;
	d_f_ree(n);
	return fofs;
}

unsigned int
search_db_by_target(PMD_WORK * w,L_CHAR * crd,L_CHAR * obj)
{
STRING_INDEX * ix;
int crd_len,obj_len;
FAVT_NODE * n;
FAVT_ROOT * r;

	r = get_root(w->p,w->pmd_h.d.ix_target,favt_string_endian);
	if ( r == 0 ) {
		w->err = -1;
		return 0;
	}

	crd_len = l_strlen(crd);
	obj_len = l_strlen(obj);
	ix = d_alloc(STRING_IX_LENGTH(crd_len,obj_len));
	ix->fofs = 0;
	l_strcpy(ix->d,crd);
	l_strcpy(&ix->d[crd_len+1],obj);
	n = favt_search(r,root_node(r),ix,favt_cmp_string_2);
	d_f_ree(ix);
	if ( n == 0 )
		return 0;
	ix = n->data;
	return ix->fofs;
}


int
insert_db_target(PMD_WORK * w,L_CHAR * crd,L_CHAR * obj,unsigned int fofs)
{
STRING_INDEX * ix;
int crd_len,obj_len;
FAVT_NODE * n, *nn;
FAVT_ROOT * r;
	r = get_root(w->p,w->pmd_h.d.ix_target,favt_string_endian);
	if ( r == 0 ) {
		w->err = -1;
		return -1;
	}

	crd_len = l_strlen(crd);
	obj_len = l_strlen(obj);
	ix = d_alloc(STRING_IX_LENGTH(crd_len,obj_len));
	ix->fofs = fofs;
	l_strcpy(ix->d,crd);
	l_strcpy(&ix->d[crd_len+1],obj);
	n = favt_alloc_node(r,ix,STRING_IX_LENGTH(crd_len,obj_len));
	if ( n == 0 ) {
		w->err = -1;
		d_f_ree(ix);
		return -1;
	}
	nn = favt_insert(r,&r->node,n,favt_cmp_string_2);
	if ( nn != n ) {
		w->err = -1;
		d_f_ree(ix);
		return -1;
	}
	d_f_ree(ix);
	return 0;
}

int
delete_db_target(PMD_WORK * w,L_CHAR * crd,L_CHAR * obj)
{
STRING_INDEX * ix;
int crd_len,obj_len;
FAVT_NODE * n;
FAVT_ROOT * r;
	r = get_root(w->p,w->pmd_h.d.ix_target,favt_string_endian);
	if ( r == 0 ) {
		w->err = -1;
		return -1;
	}

	crd_len = l_strlen(crd);
	obj_len = l_strlen(obj);
	ix = d_alloc(STRING_IX_LENGTH(crd_len,obj_len));
	ix->fofs = 0;
	l_strcpy(ix->d,crd);
	l_strcpy(&ix->d[crd_len+1],obj);
	n = favt_delete(r,&r->node,ix,favt_cmp_string_2);
	favt_free_node(n);
	d_f_ree(ix);
	return 0;
}

int
insert_ix(
	PMD_WORK * w,
	unsigned int root,
	void * d,
	int d_len,
	unsigned int fofs,
	void (*endian_func)(),
	int (*cmp_func)())
{
FAVT_ROOT * r;
FAVT_NODE * n,* nn;
unsigned int * ix, * ixp;
int len;
ACC_PN_PMD_OFSLIST * ol, * ol2;


	r = get_root(w->p,root,endian_func);
	if ( r == 0 ) {
		w->err = -1;
		return -1;
	}
	ix = d_alloc(sizeof(int)+d_len);
	*ix = fofs;
	memcpy(&ix[1],d,d_len);
	n = favt_search(r,root_node(r),ix,cmp_func);
	if ( n == 0 ) {
		n = favt_alloc_node(r,ix,sizeof(int)+d_len);
		nn = favt_insert(r,&r->node,n,cmp_func);
	}
	else {
		ixp = n->data;
		ol = read_filespace(w->p,*ixp);
		if ( ol == 0 ) {
			w->err = -1;
			d_f_ree(ix);
			return -1;
		}
		change_endian_header(&ol->d.h);
		if ( ol->d.h.type == PNT_PMD_OFSLIST ) {
			change_endian_header(&ol->d.h);
			change_endian_pmd_ofslist_to_host(&ol->d);
			len = (ol->d.h.size - 
				sizeof(PN_HEADER))/sizeof(unsigned int);
			ol = d_re_alloc(ol,
				PN_PMD_OFSLIST_S(len+1) +
				sizeof(ACC_HEADER));
			ol->d.h.size = PN_PMD_OFSLIST_S(len+1);
			ol->d.fofs[len] = fofs;
			free_filespace(w->p,ol->h.fofs);
		}
		else {
			ol = d_alloc(PN_PMD_OFSLIST_S(2)
				+ sizeof(ACC_HEADER));
			ol->d.h.type = PNT_PMD_OFSLIST;
			ol->d.h.size = PN_PMD_OFSLIST_S(2);
			ol->d.fofs[0] = *ixp;
			ol->d.fofs[1] = fofs;
		}
		ol->h.fofs = alloc_filespace(w->p,&ol->d.h);
		*ixp = ol->h.fofs;
		n->h.flags |= FAF_DIRTY;
		change_endian_pmd_ofslist_to_net(&ol->d);
		write_filespace(w->p,ol);
	}
	d_f_ree(ix);
	return 0;
}



int
delete_ix(
	PMD_WORK * w,
	unsigned int root,
	void * d,
	int d_len,
	unsigned int fofs,
	void (*endian_func)(),
	int (*cmp_func)())
{
FAVT_ROOT * r;
FAVT_NODE * n, * nn;
unsigned int * ix, * ixp;
int len,i,target;
ACC_PN_PMD_OFSLIST * ol, * ol2;
int ret;

	r = get_root(w->p,root,endian_func);
	if ( r == 0 ) {
		w->err = -1;
		return -1;
	}
	ix = d_alloc(sizeof(int)+d_len);
	*ix = fofs;
	memcpy(&ix[1],d,d_len);
	n = favt_search(r,root_node(r),ix,cmp_func);
	if ( n == 0 ) {
		w->err = -1;
		ret = -1;
		goto end;
	}
	ixp = n->data;
	ol = read_filespace(w->p,*ixp);
	if ( ol == 0 ) {
		w->err = -1;
		ret = -1;
		goto end;
	}
	change_endian_header(&ol->d.h);
	if ( ol->d.h.type == PNT_PMD_OFSLIST ) {
		change_endian_header(&ol->d.h);
		change_endian_pmd_ofslist_to_host(&ol->d);
		len = (ol->d.h.size - sizeof(PN_HEADER))/sizeof(int);
		for ( i = 0 ; i < len ; i ++ )
			if ( ol->d.fofs[i] == fofs )
				break;
		target = i;
		if ( target >= len ) {
			ret = 0;
			goto end2;
		}
		for ( ; i < len-1 ; i ++ )
			ol->d.fofs[i] = ol->d.fofs[i+1];
		len --;
		if ( len == 1 ) {
			*ixp = ol->d.fofs[0];
			n->h.flags |= FAF_DIRTY;
			free_filespace(w->p,ol->h.fofs);
		}
		else {
			ol->d.h.size = PN_PMD_OFSLIST_S(len);
			free_filespace(w->p,ol->h.fofs);
			ol->h.fofs = alloc_filespace(w->p,&ol->d.h);
			change_endian_pmd_ofslist_to_net(&ol->d);
			write_filespace(w->p,ol);
			*ixp = ol->h.fofs;
			n->h.flags |= FAF_DIRTY;
		}
	}
	else if ( ol->h.fofs == fofs ) {
		nn = favt_delete(r,&r->node,ix,cmp_func);
		if ( n != nn )
			er_panic("delete_ix(1)");
		favt_free_node(n);
		ret = 0;
	}
	else {
		ret = 0;
		goto end2;
	}

end2:
	d_f_ree(ol);
end:
	d_f_ree(ix);
	return ret;
}


typedef struct maxmin_work {
	void *		data;
	int		len;
} MAXMIN_WORK;

int
maxmin_func(FAVT_NODE * a,MAXMIN_WORK * w)
{
	w->data = d_alloc(a->data_len);
	memcpy(w->data,a->data,a->data_len);
	w->len = a->data_len;
	return 1;
}

void *
maxmin_ix(
	int * d_len,
	PMD_WORK * w,
	unsigned int root,
	void (*endian_func)(),
	int (*trace_func)())
{
FAVT_ROOT * r;
FAVT_NODE * n;
unsigned int * ix, * ixp;
int len;
ACC_PN_PMD_OFSLIST * ol, * ol2;
MAXMIN_WORK mmw;

	r = get_root(w->p,root,endian_func);
	if ( r == 0 ) {
		w->err = -1;
		return 0;
	}
	mmw.data = 0;
	mmw.len = 0;
	(*trace_func)(r,root_node(r),maxmin_func,&mmw);
	*d_len = mmw.len;
	return mmw.data;
}



int
insert_db(PMD_WORK * w,PMD_TEMP * t)
{
int er;

	w->err = 0;
	write_temp(w,t,WRITE_TEMP_ALLOC);
	er = insert_ix(w,
		w->pmd_h.d.ix_tl_x,
		&t->minrect.tl.x,
		sizeof(t->minrect.tl.x),
		t->fofs,
		favt_floating_endian,
		cmp_floating);
	if ( er < 0 )
		return -1;
	er = insert_ix(w,
		w->pmd_h.d.ix_tl_y,
		&t->minrect.tl.y,
		sizeof(t->minrect.tl.y),
		t->fofs,
		favt_floating_endian,
		cmp_floating);
	if ( er < 0 )
		return -1;
	er = insert_ix(w,
		w->pmd_h.d.ix_br_x,
		&t->minrect.br.x,
		sizeof(t->minrect.br.x),
		t->fofs,
		favt_floating_endian,
		cmp_floating);
	if ( er < 0 )
		return -1;
	er = insert_ix(w,
		w->pmd_h.d.ix_br_y,
		&t->minrect.br.y,
		sizeof(t->minrect.br.y),
		t->fofs,
		favt_floating_endian,
		cmp_floating);
	if ( er < 0 )
		return -1;
	er = insert_ix(w,
		w->pmd_h.d.ix_resolution,
		&t->resolution,
		sizeof(t->resolution),
		t->fofs,
		favt_floating_endian,
		cmp_floating);
	if ( er < 0 )
		return -1;
	er = insert_ix(w,
		w->pmd_h.d.ix_content_period_from,
		&t->content_period_from,
		sizeof(t->content_period_from),
		t->fofs,
		favt_gb_time_endian,
		cmp_gb_time);
	if ( er < 0 )
		return -1;
	er = insert_ix(w,
		w->pmd_h.d.ix_content_period_to,
		&t->content_period_to,
		sizeof(t->content_period_to),
		t->fofs,
		favt_gb_time_endian,
		cmp_gb_time);
	if ( er < 0 )
		return -1;
	er = insert_ix(w,
		w->pmd_h.d.ix_timeout,
		&t->timeout,
		sizeof(t->timeout),
		t->fofs,
		favt_timeout_endian,
		cmp_timeout);
	if ( er < 0 )
		return -1;
	er = insert_db_target(w,
		t->crd,
		t->target,
		t->fofs);
	if ( er < 0 )
		return -1;
	return 0;
}


int
delete_db(PMD_WORK * w,PMD_TEMP * t)
{
int er;
	w->err = 0;
	er = delete_ix(w,
		w->pmd_h.d.ix_tl_x,
		&t->minrect.tl.x,
		sizeof(t->minrect.tl.x),
		t->fofs,
		favt_floating_endian,
		cmp_floating);
	if ( er < 0 )
		return -1;
	er = delete_ix(w,
		w->pmd_h.d.ix_tl_y,
		&t->minrect.tl.y,
		sizeof(t->minrect.tl.y),
		t->fofs,
		favt_floating_endian,
		cmp_floating);
	if ( er < 0 )
		return -1;
	er = delete_ix(w,
		w->pmd_h.d.ix_br_x,
		&t->minrect.br.x,
		sizeof(t->minrect.br.x),
		t->fofs,
		favt_floating_endian,
		cmp_floating);
	if ( er < 0 )
		return -1;
	er = delete_ix(w,
		w->pmd_h.d.ix_br_y,
		&t->minrect.br.y,
		sizeof(t->minrect.br.y),
		t->fofs,
		favt_floating_endian,
		cmp_floating);
	if ( er < 0 )
		return -1;
	er = delete_ix(w,
		w->pmd_h.d.ix_resolution,
		&t->resolution,
		sizeof(t->resolution),
		t->fofs,
		favt_floating_endian,
		cmp_floating);
	if ( er < 0 )
		return -1;
	er = delete_ix(w,
		w->pmd_h.d.ix_content_period_from,
		&t->content_period_from,
		sizeof(t->content_period_from),
		t->fofs,
		favt_gb_time_endian,
		cmp_gb_time);
	if ( er < 0 )
		return -1;
	er = delete_ix(w,
		w->pmd_h.d.ix_content_period_to,
		&t->content_period_to,
		sizeof(t->content_period_to),
		t->fofs,
		favt_gb_time_endian,
		cmp_gb_time);
	if ( er < 0 )
		return -1;
	er = delete_ix(w,
		w->pmd_h.d.ix_timeout,
		&t->timeout,
		sizeof(t->timeout),
		t->fofs,
		favt_timeout_endian,
		cmp_timeout);
	if ( er < 0 )
		return -1;
	er = delete_db_target(w,
		t->crd,
		t->target);
	if ( er < 0 )
		return -1;
	free_pmd_node(w,t->fofs);
	return 0;
}

int
change_timeout(PMD_WORK * w,PMD_TEMP * t,unsigned int timeout)
{
int er;
	er = delete_ix(w,w->pmd_h.d.ix_timeout,
		&t->timeout,
		sizeof(t->timeout),
		t->fofs,
		favt_timeout_endian,
		cmp_timeout);
	if ( er < 0 )
		return -1;
	t->timeout = timeout;
	er = insert_ix(w,w->pmd_h.d.ix_timeout,
		&t->timeout,
		sizeof(t->timeout),
		t->fofs,
		favt_timeout_endian,
		cmp_timeout);
	if ( er < 0 )
		return er;
	write_temp(w,t,WRITE_TEMP_UPDATE);
	return 0;
}

int
maxmin_floating(PMD_WORK * w,REAL1 * min,REAL1 * max,unsigned int root)
{
FLOATING_INDEX * f;
int d_len;
	f = maxmin_ix(
		&d_len,
		w,
		root,
		favt_floating_endian,
		favt_trace_from_small);
	if ( f == 0 )
		return -1;
	*min = f->d;
	d_f_ree(f);
	f = maxmin_ix(
		&d_len,
		w,
		root,
		favt_floating_endian,
		favt_trace_from_large);
	if ( f == 0 )
		return -1;
	*max = f->d;
	d_f_ree(f);
	return 0;
}
int
maxmin_content_period(PMD_WORK * w,
	GB_TIME * min,GB_TIME * max,
	unsigned int root_from,
	unsigned int root_to)
{
GB_TIME_INDEX * f;
int d_len;
	f = maxmin_ix(
		&d_len,
		w,
		root_from,
		favt_gb_time_endian,
		favt_trace_from_small);
	if ( f == 0 )
		return -1;
	*min = f->d;
	d_f_ree(f);
	f = maxmin_ix(
		&d_len,
		w,
		root_to,
		favt_gb_time_endian,
		favt_trace_from_large);
	if ( f == 0 )
		return -1;
	*max = f->d;
	d_f_ree(f);
	return 0;
}

void
get_db_status(PMD_WORK * w,PMD_TEMP * min,PMD_TEMP * max)
{
	maxmin_floating(w,
		&min->minrect.tl.x,
		&max->minrect.tl.x,
		w->pmd_h.d.ix_tl_x);
	maxmin_floating(w,
		&min->minrect.tl.y,
		&max->minrect.tl.y,
		w->pmd_h.d.ix_tl_y);
	maxmin_floating(w,
		&min->minrect.br.x,
		&max->minrect.br.x,
		w->pmd_h.d.ix_br_x);
	maxmin_floating(w,
		&min->minrect.br.y,
		&max->minrect.br.y,
		w->pmd_h.d.ix_br_y);
	maxmin_floating(w,
		&min->resolution,
		&max->resolution,
		w->pmd_h.d.ix_resolution);
	maxmin_content_period(w,
		&min->content_period_from,
		&max->content_period_from,
		w->pmd_h.d.ix_content_period_from,
		w->pmd_h.d.ix_content_period_to);
}


PMD_TEMP_LIST *
insert_pmd_temp_list(PMD_WORK * w,PMD_TEMP_LIST * tl,unsigned int fofs,
		GB_RECT * r)
{
PMD_TEMP * t;
PMD_TEMP_LIST * ret;
	t = get_temp_db(w,fofs);
	if ( t == 0 )
		return tl;
	if ( r && cross_rect_rect(&t->minrect,r) == 0 ) {
		free_temp(t);
		return tl;
	}
	ret = d_alloc(sizeof(*ret));
	ret->next = tl;
	ret->temp = t;
	return ret;
}


PMD_TEMP_LIST *
query_db(PMD_WORK * 	w,
	GB_RECT	 * 	rect,
	REAL1		reso_min,
	REAL1		reso_max)
{
BOUND_LIST * b, * bb;
FAVT_ROOT * r;
FLOATING_INDEX ix_min,ix_max;
FLOATING_INDEX * ixp;
ACC_PN_PMD_NODE * n;
ACC_PN_PMD_OFSLIST * ol;
int len;
int i;
PMD_TEMP_LIST * ret;
	r = get_root(w->p,w->pmd_h.d.ix_resolution,favt_floating_endian);
	if ( r == 0 )
		return 0;
	ix_min.d = reso_min;
	ix_max.d = reso_max;
	b = favt_bound_search(r,root_node(r),
		&ix_min,&ix_max,cmp_floating);
	ret = 0;
	for ( bb = b ; bb ; bb = bb->next ) {
		ixp = bb->data;
		ol = read_filespace(w->p,ixp->fofs);
		if ( ol == 0 )
			continue;
		change_endian_header(&ol->d.h);
		switch ( ol->d.h.type ) {
		case PNT_PMD_OFSLIST:
			change_endian_header(&ol->d.h);
			change_endian_pmd_ofslist_to_host(&ol->d);
			len = (ol->d.h.size - sizeof(PN_HEADER))/sizeof(int);
			for ( i = 0 ; i < len ; i ++ )
				ret = insert_pmd_temp_list(
						w,ret,ol->d.fofs[i],
						rect);
			break;
		case PNT_PMD_NODE:
			ret = insert_pmd_temp_list(w,ret,ixp->fofs,rect);
			break;
		default:
			break;
		}
		d_f_ree(ol);
	}
	free_bound_list(b);
	return ret;
}


void
delete_timeout_temp(PMD_WORK * w)
{
BOUND_LIST * b, * bb;
FAVT_ROOT * r;
TIMEOUT_INDEX ix_min,ix_max;
TIMEOUT_INDEX * ixp;
ACC_PN_PMD_NODE * n;
ACC_PN_PMD_OFSLIST * ol;
int len;
int i;
PMD_TEMP_LIST * ret, * tl;
	r = get_root(w->p,w->pmd_h.d.ix_timeout,favt_timeout_endian);
	if ( r == 0 )
		return;
	ix_min.timeout = 0;
	ix_max.timeout = get_xltime();
	b = favt_bound_search(r,root_node(r),
		&ix_min,&ix_max,cmp_timeout);
	ret = 0;
	for ( bb = b ; bb ; bb = bb->next ) {
		ixp = bb->data;
		ol = read_filespace(w->p,ixp->fofs);
		if ( ol == 0 )
			continue;
		change_endian_header(&ol->d.h);
		switch ( ol->d.h.type ) {
		case PNT_PMD_OFSLIST:
			change_endian_header(&ol->d.h);
			change_endian_pmd_ofslist_to_host(&ol->d);
			len = (ol->d.h.size - sizeof(PN_HEADER))/sizeof(int);
			for ( i = 0 ; i < len ; i ++ )
				ret = insert_pmd_temp_list(w,ret,
					ol->d.fofs[i],0);
			break;
		case PNT_PMD_NODE:
			ret = insert_pmd_temp_list(w,ret,ixp->fofs,0);
			break;
		default:
			break;
		}
		d_f_ree(ol);
	}
	free_bound_list(b);

	for ( tl = ret ; tl ; tl = tl->next ) {
log_printf(LOG_SYSTEM,
"DELETE PMD %i %ls %ls\n",get_xltime() - tl->temp->timeout,
tl->temp->crd,
tl->temp->target);
		delete_db(w,tl->temp);
		if ( w->err < 0 )
			break;
	}
	free_pmd_temp_list(ret);
}


