/**********************************************************************
 
	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	<stdio.h>
#include	"change_endian.h"
#include	"memory_debug.h"
#include	"xl2pdb_p.h"
#include	"svg2gb.h"
extern PDB_LOD_PT * pdb_lpt;
extern int lod_min,lod_max;
extern double max_resolution;
extern L_CHAR * unitname;
extern char * data_encoding;
extern CODE_METHOD * data_encoding_cm;
extern GB_RECT minrect;
extern CODE_INDEX * code_index;
extern unsigned int code_max;
extern char info_file[];

PN_FILE_HEADER fh;
int fh_size;
PN_2D_DRAW_HEADER dh;
int dh_size;
unsigned int file_offset;
int max_depth;

#ifndef O_BINARY
#define O_BINARY 0
#endif

void
make_file_header()
{
	if ( data_encoding_cm == 0 )
		er_panic("save_pdb");

	fh.h.size = PN_FILE_HEADER_S;
	fh.h.type = PNT_FILE_HEADER;
	fh.type = FT_2D_DRAW;
	strcpy(fh.version,VERSION);
	strcpy(fh.encoding,data_encoding);
	fh.linear = PN_FILE_HEADER_S + PN_2D_DRAW_HEADER_S;
	fh.linear_length = code_max * sizeof(CODE_INDEX);
	fh_size = fh.h.size;
	change_endian_file_header(&fh);

	dh.h.size = PN_2D_DRAW_HEADER_S;
	dh.h.type = PNT_2D_DRAW_HEADER;
	dh.level = lod_max+1;
	dh.resolution = 1/max_resolution;
	dh.fofs = PN_FILE_HEADER_S + PN_2D_DRAW_HEADER_S
		+ code_max * sizeof(CODE_INDEX);
	dh.minrect = minrect;
	strcpy(dh.unit,n_string(data_encoding_cm,unitname));
	strcpy(dh.infofile,info_file);
	dh_size = dh.h.size;
	change_endian_2d_draw_header(&dh);
	file_offset = PN_FILE_HEADER_S + PN_2D_DRAW_HEADER_S;
}


unsigned int
make_pdb_point(PDB_PD_POINT * ptr)
{

	if ( ptr->no & (PP_LNO&0xffff8000) ) {
		ptr->fofs = file_offset;
		ptr->long_sg = d_alloc(PN_LNO_POLY_POINT_S,212);
		ptr->long_sg->h.type = PNT_LNO_POLY_POINT;
		ptr->sg_size = ptr->long_sg->h.size = PN_LNO_POLY_POINT_S;
		ptr->long_sg->no = ptr->no;
		ptr->long_sg->lod_min = ptr->lod_min;
		ptr->long_sg->lod_max = ptr->lod_max;
		ptr->long_sg->p = ptr->p;
		ptr->short_sg = 0;
		file_offset += PN_LNO_POLY_POINT_S;
		change_endian_lno_poly_point(ptr->long_sg);
		return ptr->fofs;
	}
	else {
		ptr->long_sg = 0;
		ptr->fofs = file_offset;
		ptr->short_sg = d_alloc(PN_POLY_POINT_S,212);
		ptr->short_sg->h.type = PNT_POLY_POINT;
		ptr->sg_size = 
			ptr->short_sg->h.size = PN_POLY_POINT_S;
		if ( ptr->no & PP_LMOVE )
			ptr->short_sg->no = (ptr->no&PP_NO) | PP_MOVE;
		else	ptr->short_sg->no = ptr->no;
		ptr->short_sg->lod_min = ptr->lod_min;
		ptr->short_sg->lod_max = ptr->lod_max;
		ptr->short_sg->p = ptr->p;
		file_offset += PN_POLY_POINT_S;
		change_endian_poly_point(ptr->short_sg);
		return ptr->fofs;
	}
}

unsigned int
make_pdb_polygon2d(PDB_POLYGON2D * p)
{
PDB_PD_POINT * ptr;
int len;
unsigned int ret;
	if ( p == 0 )
		return 0;
	p->start_sg = d_alloc((len=PN_POLYGON2D_S),43);
	p->fofs = file_offset;
	p->start_sg->h.type = PNT_POLYGON2D;
	p->start_sg_size = p->start_sg->h.size = len;
	p->start_sg->type = p->type;
	p->start_sg->code = p->code;
	p->start_sg->line_color = p->line;
	p->start_sg->padding_color = p->padding;
	p->start_sg->lod_max = p->lod_max;
	p->start_sg->lod_min = p->lod_min;
	p->start_sg->minrect = p->minrect;
	file_offset += p->start_sg->h.size;
	change_endian_polygon2d(p->start_sg);

	for ( ptr = p->point ; ptr ; ptr = ptr->next )
		make_pdb_point(ptr);

	p->end_sg =
		d_alloc(PN_POLY_END_S,03);
	p->end_sg->h.type = PNT_POLY_END;
	p->end_sg_size = p->end_sg->h.size = PN_POLY_END_S;
	file_offset += p->end_sg->h.size;
	change_endian_poly_end(p->end_sg);
	return p->fofs;
}

int
make_linear_section()
{
int i;
	for ( i = 0 ; i < code_max ; i ++ ) {
		change_endian_code_index(&code_index[i]);
	}
	file_offset += code_max * sizeof(CODE_INDEX);
	return 0;
}

unsigned int
make_pdb_pt(PDB_PT * p,int depth)
{
int i,j,cnt;
PDB_POLYGON2D * pp;
int same_index;
	if ( p == 0 )
		return 0;
	if ( max_depth < depth )
		max_depth = depth;
	p->fofs = file_offset;
	p->sg = d_alloc(PN_TREE_NODE_S(PTR_MAX),33);
	p->sg->h.type = PNT_TREE_NODE;
	p->sg->lod_min = p->lod_min;
	p->sg->lod_max = p->lod_max;
	p->sg->r = p->r;
	if ( p->same_index ) {
		for ( i = PTR_MAX-1 ; i >= 0  ; i --  )
			if ( p->next[i].type )
				break;
		same_index = i;
		cnt = 0;
		for ( pp = p->next[i].ptr.poly ; pp ; pp = pp->next )
			cnt ++;
	}
	else {
		cnt = 0;
		for ( i = 0 ; i < PTR_MAX ; i ++ ) {
			if ( p->next[i].type == 0 )
				continue;
			cnt ++;
		}
	}
	p->sg->fofs_size = cnt;
	p->sg_size = p->sg->h.size = PN_TREE_NODE_S(cnt);
	file_offset += p->sg->h.size;

	if ( p->same_index ) {
		j = cnt-1;
		for ( pp = p->next[i].ptr.poly ; pp ; pp = pp->next ) {
				p->sg->fo.lst[j].fofs
					= make_pdb_polygon2d(pp);
				p->sg->fo.lst[j].index = same_index;
				j --;
		}
	}
	else if ( cnt < PTR_MAX/2 ) {
		j = 0;
		for ( i = PTR_MAX-1 ; i >= 0  ; i --  ) {
			switch ( p->next[i].type ) {
			case 0:
				continue;
			case PTT_POLYGON2D:
				p->sg->fo.lst[j].index = i;
				p->sg->fo.lst[j].fofs
					= make_pdb_polygon2d(
						p->next[i].ptr.poly);
				break;
			case PTT_PT:
				p->sg->fo.lst[j].index = i;
				p->sg->fo.lst[j].fofs
					= make_pdb_pt(p->next[i].ptr.pt,
						depth+1);
				break;
			}
			j ++;
		}
	}
	else {
		for ( i = PTR_MAX-1 ; i >= 0 ; i --  ) {
			switch ( p->next[i].type ) {
			default:
				p->sg->fo.fofs[i] = 0;
				continue;
			case PTT_POLYGON2D:
				p->sg->fo.fofs[i]
					= make_pdb_polygon2d(
						p->next[i].ptr.poly);
				break;
			case PTT_PT:
				p->sg->fo.fofs[i]
					= make_pdb_pt(p->next[i].ptr.pt,
						depth+1);
				break;
			}
		}
	}
	change_endian_tree_node_to_net(p->sg);
	return p->fofs;
}

unsigned int
make_pdb_lod_pt(PDB_LOD_PT * p)
{
int lod;
	p->sg = d_alloc(PN_LOD_LIST_SIZE(p->max),346);
	p->sg_size = PN_LOD_LIST_SIZE(p->max);
	p->fofs = file_offset;
	file_offset += p->sg_size;

	p->sg->h.size = p->sg_size;
	p->sg->h.type = PNT_LOD_LIST;
	p->sg->max = p->max;
	for ( lod = 0 ; lod < p->max ; lod ++ ) {
		p->sg->fofs[lod] =
			make_pdb_pt(p->next[lod].ptr.pt,1);
	}
	change_endian_lod_list_to_net(p->sg);
	return p->fofs;
}

int
open_pdb_file(int argc,char ** argv)
{
int i;
int fd;
	for ( i = 0 ; i < argc ; i ++ )
		if ( strcmp(argv[i],"/") == 0 )
			break;
	if ( i == argc )
		fd = 0;
	else {
		i ++;
		if ( i == argc )
			fd = 0;
	 	else {
			fd = open(argv[i],O_RDWR|O_CREAT|O_TRUNC,0644);
			if ( fd < 0 ) {
				fprintf(stderr,"cannot open the file\n");
				perror(argv[i]);
				exit(1);
			}
		}
	}
	return fd;
}



void
save_point(STREAM *fd,PDB_PD_POINT * ptr)
{
	if ( s_seek_file(fd,0,SEEK_CUR) != ptr->fofs ) {
		fprintf(stderr,"save_point(offset error)\n");
		exit(1);
	}
	if ( ptr->short_sg )
		s_write(fd,ptr->short_sg,ptr->sg_size);
	else
		s_write(fd,ptr->long_sg,ptr->sg_size);
}

void
save_polygon2d(STREAM *fd, PDB_POLYGON2D * p)
{
PDB_PD_POINT * ptr;


	if ( s_seek_file(fd,0,SEEK_CUR) != p->fofs ) {
		fprintf(stderr,"save_polygon2d(offset error)\n");
		exit(1);
	}
	s_write(fd,p->start_sg,p->start_sg_size);
	for ( ptr = p->point ; ptr ; ptr = ptr->next )
		save_point(fd,ptr);
	s_write(fd,p->end_sg,p->end_sg_size);
}

void
save_pdb_pt(STREAM *fd, PDB_PT * pt)
{
unsigned int ofs;
int i;
PDB_POLYGON2D * pp;
	ofs = s_seek_file(fd, 0, SEEK_CUR);
	if ( ofs != pt->fofs ) {
		fprintf(stderr,"save_pdb_pt(offset error)\n");
		exit(1);
	}
	s_write(fd, pt->sg, pt->sg_size);
	for ( i = PTR_MAX-1 ; i >= 0 ; i -- ) {
		switch ( pt->next[i].type ) {
		case 0:
			continue;
		case PTT_POLYGON2D:
			if ( pt->same_index )
				goto same;
			save_polygon2d(fd,pt->next[i].ptr.poly);
			break;
		case PTT_PT:
			save_pdb_pt(fd,pt->next[i].ptr.pt);
			break;
		}
	}
	return;
same:
	for ( pp = pt->next[i].ptr.poly; pp ; pp = pp->next )
		save_polygon2d(fd,pp);
}


void
save_pdb_lod_pt(STREAM *fd,PDB_LOD_PT * p)
{
unsigned int ofs;
int i;
	ofs = s_seek_file(fd, 0, SEEK_CUR);
	if ( ofs != p->fofs ) {
		fprintf(stderr,"save_pdb_lod_pt(offset error)\n");
		exit(1);
	}
	s_write(fd, p->sg, p->sg_size);
	for ( i = 0 ; i < p->max ; i ++ ) {
		if ( p->next[i].ptr.pt == 0 )
			continue;
		save_pdb_pt(fd,p->next[i].ptr.pt);
	}
}

void 
save_pdb(SVG2GB_OUTPUTPP_INFO *info)
{
STREAM *s;
	ss_printf("\tSETUP OFFSET...\n");
	make_file_header();
	make_linear_section();
	make_pdb_lod_pt(pdb_lpt);
	ss_printf("\tSAVE...\n");
	s = s_open_file(info->filename_pp, O_RDWR|O_CREAT|O_TRUNC|O_BINARY, 0664);
	if(s <= 0){
		fprintf(stderr, "err openfile %s.\n", info->filename_pp);
		exit(1);
	}
	s_write(s, &fh, fh_size);
	s_write(s, &dh, dh_size);
	s_write(s, code_index, code_max*sizeof(CODE_INDEX));
	save_pdb_lod_pt(s, pdb_lpt);

	ss_printf("\t\tmax depth = %i\n",max_depth);
}
