/**********************************************************************
 
	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	"memory_debug.h"
#include	"memory_routine.h"
#include	"xlerror.h"
#include	"pdb.h"
#include	"utils.h"
#include	"task.h"
#include	"search.h"
#include	"gbs_trailer.h"
#include	"xl_zlib.h"


int	pdbp_polygon2d_t();
int	pdbp_l_poly_point_t();
int	pdbp_s_poly_point_t();
int	pdbp_poly_end_t();
int	pdbp_relay_t();
int 	pdbp_lod_list();

int (*pdbp_table_t[])() = {
	0,
	pdbp_file_header,
	pdbp_2d_draw_header,
	pdbp_lod_list,
	pdbp_tree_node,
	pdbp_polygon2d_t,
	pdbp_s_poly_point_t,
	pdbp_poly_end_t,
	pdbp_l_poly_point_t
};

void
free_trailer_list(P2D_T_LIST * lst)
{
P2D_T_LIST * lp;
	for ( ; lst ; ) {
		lp = lst->h.next;
		d_f_ree(lst);
		lst = lp;
	}
}

void
free_p2d_s_pt_t_list(P2D_S_PT_T_LIST * ptl)
{
P2D_S_PT_T_LIST * ptl2;
	for ( ; ptl ; ) {
		ptl2 = ptl->next;
		d_f_ree(ptl);
		ptl = ptl2;
	}
}

void
free_p2d_l_pt_t_list(P2D_L_PT_T_LIST * ptl)
{
P2D_L_PT_T_LIST * ptl2;
	for ( ; ptl ; ) {
		ptl2 = ptl->next;
		d_f_ree(ptl);
		ptl = ptl2;
	}
}

void
free_p2d_col_t_list(P2D_COL_T_LIST * pcl)
{
P2D_COL_T_LIST * pcl2;
	for ( ; pcl ; ) {
		pcl2 = pcl->next;
		d_f_ree(pcl);
		pcl = pcl2;
	}
}


int
pdbp_file_header(PDB * p,
	unsigned int nextofs,
	REQUEST * opt,
	PN_FILE_HEADER * f)
{
REQUEST * opt2;
	if ( f->type != FT_2D_DRAW )
		return -1;
	if ( strcmp(f->version,VERSION) != 0 )
		return -1;
	opt2 = d_alloc(sizeof(*opt2),5);
	memcpy(opt2,opt,sizeof(*opt2));
	insert_request(p,nextofs,opt2);
	return 0;
}

int
pdbp_2d_draw_header(PDB * p,
	unsigned int nextofs,
	REQUEST * opt,
	PN_2D_DRAW_HEADER * d)
{
REQUEST * opt2;
	opt2 = d_alloc(sizeof(*opt2),1);
	memcpy(opt2,opt,sizeof(*opt2));
	insert_request(p,d->fofs,opt2);
	return 0;
}

int
pdbp_lod_list(PDB * p,
	unsigned int nextofs,
	REQUEST * opt,
	PN_LOD_LIST * d)
{
int lod;
	lod = opt->cond->lod;
	if ( lod < 0 || lod >= d->max )
		return 0;
	if ( d->fofs[lod] == 0 )
		return 0;
	insert_request(p,d->fofs[lod],opt);
	return 1;
}

int
cmp_tree_node(
	int index,
	int min_effect,
	int max_effect,
	int equ_max,
	int c_min,
	int c_max)
	/* db < cond --> c = -1;
	   db = cond --> c = 0;
	   db > cond --> c = 1;
	*/
{
/*
printf("c_min = %i c_max = %i\n",c_min,c_max);
printf("index = %x max_effect = %x equ_max = %x min_effect = %x\n",
index,max_effect,equ_max,min_effect);
*/
	if ( index ) {
		/* large destrict */
		if ( max_effect == 0 )
			return 1;
		if ( equ_max ) {
			if ( c_max <= 0 )
				return 1;
			return 0;
		}
		else {
			if ( c_max < 0 )
				return 1;
			return 0;
		}
	}
	else {
		/* small destrict */
		if ( min_effect == 0 )
			return 1;
		if ( c_min > 0 )
			return 1;
		return 0;
	}
}


#define CALL_TREE_NODE_COND(db,cond_min,cond_max,mask) \
if ( (db) < (cond_min) )				\
	c_min = -1;					\
else if ( (db) > (cond_min) )				\
	c_min = 1;					\
else	c_min = 0;					\
if ( (db) < (cond_max) )				\
	c_max = -1;					\
else if ( (db) > (cond_max) )				\
	c_max = 1;					\
else	c_max = 0;					\
if ( cmp_tree_node(					\
		index&(mask),				\
		opt->cond->min_effect&(mask),		\
		opt->cond->max_effect&(mask),		\
		opt->cond->max_equ&(mask),		\
		c_min,c_max) == 0 )			\
	goto end;

int
call_tree_node(PDB * p,
	PN_TREE_NODE * tn,
	int index,
	unsigned int fofs,
	REQUEST * opt)
{
REQUEST * opt2;
int c_min,c_max;

	CALL_TREE_NODE_COND(
		tn->r.tl.x,
		opt->cond->min.r.tl.x,
		opt->cond->max.r.tl.x,
		TLX_L);	
	CALL_TREE_NODE_COND(
		tn->r.tl.y,
		opt->cond->min.r.tl.y,
		opt->cond->max.r.tl.y,
		TLY_L);
	CALL_TREE_NODE_COND(
		tn->r.br.x,
		opt->cond->min.r.br.x,
		opt->cond->max.r.br.x,
		BRX_L);
	CALL_TREE_NODE_COND(
		tn->r.br.y,
		opt->cond->min.r.br.y,
		opt->cond->max.r.br.y,
		BRY_L);
/*
printf("index = %x ",index);
*/
/*
printf("ok(%i)-",fofs);
*/
	opt2 = d_alloc(sizeof(*opt2),1);
	memcpy(opt2,opt,sizeof(*opt2));
	insert_request(p,fofs,opt2);
end:
/*
printf("no\n");
*/
	return 0;
}

int
pdbp_tree_node(PDB * p,
	unsigned int nextofs,
	REQUEST * opt,
	PN_TREE_NODE * tn)
{
int i;

/*
printf("tn (%f %f)-(%f %f) %i %i\n",
tn->r.tl.x,
tn->r.tl.y,
tn->r.br.x,
tn->r.br.y,
tn->lod_min,
tn->lod_max);
*/

	if ( tn->fofs_size < PTR_MAX/2 ) {
		for ( i = 0 ; i < tn->fofs_size ; i ++ )
			call_tree_node(
				p,tn,
				tn->fo.lst[i].index,
				tn->fo.lst[i].fofs,
				opt);
	}
	else {
		for ( i = 0 ; i < PTR_MAX ; i ++ ) {
			if ( tn->fo.fofs[i] == 0 )
				continue;
			call_tree_node(p,tn,i,tn->fo.fofs[i],opt);
		}
	}
	return 0;
}

int
pdbp_polygon2d_t(PDB * p,
	unsigned int nextofs,
	REQUEST * opt,
	PN_POLYGON2D * pn_pol)
{
/*
if ( pn_pol->code == 2048 )
printf("lod %i-%i\n",pn_pol->lod_min,pn_pol->lod_max);
*/
	if ( cross_rect_rect(&pn_pol->minrect,&opt->cond->r) == 0 )
		return 0;
/*
if ( pn_pol->code == 2048 )
printf("ok\n");
*/
	opt->code = pn_pol->code;
	opt->line_color = pn_pol->line_color;
	opt->padding_color = pn_pol->padding_color;
	opt->type = pn_pol->type;
	opt->flags |= RF_POLYGON;
	insert_request(p,nextofs,opt);
	return 1;
}


int
pdbp_s_poly_point_t(PDB * p,
	unsigned int nextofs,
	REQUEST * opt,
	PN_POLY_POINT * pt)
{
int size;
P2D_S_PT_T_LIST * ptl;


	if ( !(opt->flags & RF_POLYGON) ){
		fprintf(stderr,"pdbp_poly_point\n");
	}
	if ( inside_rect(&opt->cond->r,pt->p) == 0 )
		goto end;
	opt->s_pnos ++;
	opt->flags &= ~RF_EMPTY;
	ptl = d_alloc(sizeof(*ptl),1652);
	ptl->d.lod_min = pt->lod_min;
	ptl->d.lod_max = pt->lod_max;
	ptl->d.no = pt->no;
	ptl->d.p = pt->p;
	ptl->next = opt->pt_s_trailer;
	opt->pt_s_trailer = ptl;

	insert_request(p,nextofs,opt);
	return 1;
end:
	insert_request(p,nextofs,opt);
	return 1;
}

int
pdbp_l_poly_point_t(PDB * p,
	unsigned int nextofs,
	REQUEST * opt,
	PN_LNO_POLY_POINT * pt)
{
int size;
P2D_L_PT_T_LIST * ptl;

	if ( !(opt->flags & RF_POLYGON) ){
		fprintf(stderr,"pdbp_poly_point\n");
	}
	if ( inside_rect(&opt->cond->r,pt->p) == 0 )
		goto end;
	opt->l_pnos ++;
	opt->flags &= ~RF_EMPTY;
	ptl = d_alloc(sizeof(*ptl),1652);
	ptl->d.lod_min = pt->lod_min;
	ptl->d.lod_max = pt->lod_max;
	ptl->d.no = pt->no;
	ptl->d.p = pt->p;
	ptl->next = opt->pt_l_trailer;
	opt->pt_l_trailer = ptl;

	insert_request(p,nextofs,opt);
	return 1;
end:
	insert_request(p,nextofs,opt);
	return 1;
}


int
search_pdb_color(SEARCH_WORK * sw,GB_COLOR * col)
{
P2D_COL_T_LIST * pcl;
	for ( pcl = sw->color ; pcl ; pcl = pcl->next ) {
		if ( memcmp(&pcl->c.col,col,sizeof(*col)) == 0 )
			return pcl->c.id;
	}
	pcl = d_alloc(sizeof(*pcl),24);
	pcl->c.t_type = TT_COLOR;
	pcl->c.col = *col;
	if ( sw->color == 0 )
		pcl->c.id = 1;
	else	pcl->c.id = sw->color->c.id + 1;
	pcl->next = sw->color;
	sw->color = pcl;
	return pcl->c.id;
}

int
pdbp_poly_end_t(PDB * p,
	unsigned int nextofs,
	REQUEST * opt,
	PN_POLY_POINT * pt,
	SEARCH_WORK * sw)
{
P2D_T_LIST * lst;
P2D_S_PT_T_LIST * ptr;
P2D_L_PT_T_LIST * ptr_l;
char * name,* subname;
int i;
P2D_T_SET set;
int tt;

	if ( opt->s_pnos ) {
		set.trailer = 0;
		set_s_p2d_trailer(
			&set,
			opt->s_pnos);
		lst = d_alloc(set.size+sizeof(lst->h),234);
		lst->h.length = set.size;
		lst->d.s.line_id = search_pdb_color(
			sw,&opt->line_color);
		lst->d.s.padding_id = search_pdb_color(
			sw,&opt->padding_color);
		lst->d.s.code = opt->code;
		lst->d.s.t_type = TT_P2D;
		lst->d.s.pnos = opt->s_pnos;
		lst->d.s.type = opt->type;
		set.trailer = (TRAILER*)&lst->d;
		set_s_p2d_trailer(
			&set,
			opt->s_pnos);
		ptr = opt->pt_s_trailer;
		for ( i = 0 ; i < opt->s_pnos ; i ++ ) {
			lst->d.s.p[i] = ptr->d;
			ptr = ptr->next;
		}
		lst->h.next = sw->t_list;
		sw->t_list = lst;
	}
	if ( opt->l_pnos ) {
		set.trailer = 0;
		set_l_p2d_trailer(
			&set,
			opt->l_pnos);
		lst = d_alloc(set.size+sizeof(lst->h),234);
		lst->h.length = set.size;
		lst->d.l.line_id = search_pdb_color(
			sw,&opt->line_color);
		lst->d.l.padding_id = search_pdb_color(
			sw,&opt->padding_color);
		lst->d.l.code = opt->code;
		lst->d.l.t_type = TT_L_P2D;
		lst->d.l.pnos = opt->l_pnos;
		lst->d.l.type = opt->type;
		lst->d.l.dummy = 0;
		set.trailer = (TRAILER*)&lst->d;
		set_l_p2d_trailer(
			&set,
			opt->l_pnos);
		ptr_l = opt->pt_l_trailer;
		for ( i = 0 ; i < opt->l_pnos ; i ++ ) {
			lst->d.l.p[i] = ptr_l->d;
			lst->d.l.p[i].dummy = 0;
			ptr_l = ptr_l->next;
		}
		lst->h.next = sw->t_list;
		sw->t_list = lst;
	}
	free_p2d_s_pt_t_list(opt->pt_s_trailer);
	free_p2d_l_pt_t_list(opt->pt_l_trailer);

	return 0;
}

int
pdbp_relay_t(PDB * p,
	unsigned int nextofs,
	REQUEST * opt,
	void * inf,
	XL_SEXP * s)
{
int size;
REQUEST * opt2;
	opt2 = d_alloc(size=sizeof(*opt2),124);
	memcpy(opt2,opt,size);
	insert_request(p,nextofs,opt2);
	return 0;
}

XL_SEXP *
make_binary(SEARCH_WORK * sw)
{
int size;
P2D_T_LIST * lp;
P2D_COL_T_LIST * pcl1;
void * data;
void * ret_data;
int ret_size;
P2D_T_SET set;
XL_SEXP * ret;
	size = 0;
	for ( pcl1 = sw->color ; pcl1 ; pcl1 = pcl1->next )
		size += sizeof(pcl1->c);
	for ( lp = sw->t_list ; lp ; lp = lp->h.next )
		size += lp->h.length;
	if ( size == 0 )
		return 0;
	data = d_alloc(size,35);
	set.trailer = data;
	for ( pcl1 = sw->color ; pcl1 ; pcl1 = pcl1->next ) {
		memcpy(set.trailer,&pcl1->c,sizeof(pcl1->c));
		get_trailer(&set);
		change_endian_trailer_to_net(set.trailer);
		set.trailer = set.next;
	}
	for ( lp = sw->t_list ; lp ; lp = lp->h.next ) {
		memcpy(set.trailer,&lp->d,lp->h.length);
		get_trailer(&set);
		change_endian_trailer_to_net(set.trailer);
		set.trailer = set.next;
	}
	ret_data = zlib_compress(
		&ret_size,
		data,
		size,
		7);
	if ( ret_data == 0 ) {
		d_f_ree(data);
		return 0;
	}
	ret = get_raw(ret_data,ret_size);
	d_f_ree(data);
	d_f_ree(ret_data);
	return ret;
}

XL_SEXP *
search_pdbp_t(char * filename,GB_RECT * r,int lod,
	XL_FILE * f,int line)
{
PDB * p;
CONDITION cond;
REQUEST * req;
XL_SEXP * ret;
int er;
SEARCH_WORK sw;
XL_SEXP * bin;



	req = d_alloc(sizeof(*req),303);
	req->cond = &cond;
	cond.min.r.tl.x = 0;
	cond.max.r.tl.x = r->br.x;
	cond.min.r.tl.y = 0;
	cond.max.r.tl.y = r->br.y;
	cond.min.r.br.x = r->tl.x;
	cond.max.r.br.x = 0;
	cond.min.r.br.y = r->tl.y;
	cond.max.r.br.y = 0;
	cond.lod = lod;

	cond.min_effect = BRX_L|BRY_L;
	cond.max_effect = TLX_L|TLY_L;
	cond.min_equ = 0;
	cond.max_equ = 0;

	cond.r = *r;
	req->s_pnos = 0;
	req->l_pnos = 0;
	req->pt_s_trailer = 0;
	req->pt_l_trailer = 0;
	req->flags = 0;

	sw.t_list = 0;
	sw.color = 0;
	p = open_pdb(filename,O_RDONLY,0,0);
	insert_request(p,0,req);
	for ( ; p->request || p->next_request ; ) {
		er = delete_request(p,pdbp_table_t,&sw);
		if ( er < 0 ) {
			if ( er == E_DR_NO_REQUEST ) {
				break;
			}
			return get_error(
				0,0,
				XLE_PROTO_INV_RESOURCE,
				l_string(std_cm,"Get"),
				List(
					n_get_string(
						"invalid file format"),
					get_integer(er,0),
					-1));
		}
	}
	close_pdb(p);
	bin = make_binary(&sw);

	free_trailer_list(sw.t_list);
	free_p2d_col_t_list(sw.color);
	return bin;
}
