/**********************************************************************
 
	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	"math.h"
#include	<fcntl.h>
#include	"equation.h"
#include	"gbgraph.h"
#include	"long_char.h"
#include	"change_endian.h"
#include	"memory_debug.h"
#include	"pdb.h"
#include	"filespace.h"
#include	"dtree.h"
#include	"kyotodb.h"

PDB * p;
unsigned int names_root;
unsigned int ids_root;

extern L_CHAR cutpattern[100][10];
extern int cutpattern_len;
extern L_CHAR patoffset[100];

void
dummy_endian()
{
}

void
change_endian_k_table(PN_K_TABLE * t)
{
	change_endian_header(&t->h);
	change_endian_l_char(&t->name);
	change_endian_i(t->ids);
	change_endian_i(t->points);
}

void
change_endian_k_ids(PN_K_IDS * id)
{
	change_endian_header(&id->h);
}

void
change_endian_k_points_to_net(PN_K_POINTS * p)
{
int len,i;
	len = (p->h.size - PN_K_POINTS_S(0))/sizeof(GB_POINT);
	change_endian_header(&p->h);
	for ( i = 0 ; i < len ; i ++ ) {
		change_endian_gb_point(&p->p[i]);
	}
}

void
change_endian_k_points_to_host(PN_K_POINTS * p)
{
int len,i;
	change_endian_header(&p->h);
	len = (p->h.size - PN_K_POINTS_S(0))/sizeof(GB_POINT);
	for ( i = 0 ; i < len ; i ++ ) {
		change_endian_gb_point(&p->p[i]);
	}
}



void
set_tword(L_CHAR * str,L_CHAR * direct)
{
	l_strcpy(&cutpattern[cutpattern_len][0],str);
	patoffset[cutpattern_len] = *direct;
	cutpattern_len ++;
}


void
init_k_table(int oflags)
{



	p = open_filespace("kyotodb",oflags|O_RDWR,0644,
		PF_USEFREELIST,
		FT_ADDRESS_LUMP,0,0,0);


	if ( oflags ) {
		names_root = alloc_dtree(p,FAT_K_NAME)->h.fofs;
		ids_root = alloc_dtree(p,FAT_K_IDS)->h.fofs;
	}
	else {
	PN_FAVT_ROOT * rr;
		names_root = 0;
		for ( ; ; ) {
			rr = get_file_record(&names_root,p,PNT_FAVT_ROOT);
			change_endian_favt_root(rr);
			if ( rr->type == (FAT_K_NAME|FAST_DTREE_ROOT) ){
				d_f_ree(rr);
				break;
			}
			d_f_ree(rr);
		}

		ids_root = 0;
		for ( ; ; ) {
			rr = get_file_record(&ids_root,p,PNT_FAVT_ROOT);
			if ( rr->type == (FAT_K_IDS|FAST_DTREE_ROOT) ) {
				d_f_ree(rr);
				break;
			}
			d_f_ree(rr);
		}
	}

}

void
finish_k_table()
{
	close_filespace(p);
}

int
cmp_string(IDS_NODE * i1,IDS_NODE * i2)
{
int ret;
	ret = strcmp(i1->id,i2->id);
	if ( ret < 0 )
		return -1;
	else if ( ret > 0 )
		return 1;
	return 0;
}

unsigned int
set_k_table(
	unsigned int fofs,L_CHAR * name,char * id)
{
ACC_PN_K_TABLE * t;
ACC_PN_K_IDS * ids;
unsigned int ret;
int len;
	if ( fofs == 0 ) {
		t = d_alloc(sizeof(*t));
		ids = d_alloc(PN_K_IDS_S(1) + sizeof(ACC_HEADER));
		t->d.h.type = PNT_K_TABLE;
		t->d.h.size = sizeof(PN_K_TABLE);
		t->a.fofs = alloc_filespace(p,&t->d.h);
		if ( l_strlen(name) >= NAME_SIZE )
			er_panic("name_size(1)");
		l_strcpy(t->d.name,name);

		ids->d.h.type = PNT_K_IDS;
		ids->d.h.size = PN_K_IDS_S(1);
		if ( strlen(id) >= ID_SIZE )
			er_panic("id_size(3)");
		strcpy(ids->d.id[0],id);
		t->d.ids = ids->a.fofs = alloc_filespace(p,&ids->d.h);
		t->d.points = 0;
		change_endian_k_table(&t->d);
		change_endian_k_ids(&ids->d);
		write_filespace(p,t);
		write_filespace(p,ids);
		ret = t->a.fofs;
		d_f_ree(t);
		d_f_ree(ids);
	}
	else {
		t = read_filespace(p,fofs);
		change_endian_k_table(&t->d);
		if ( t->d.h.type != PNT_K_TABLE )
			er_panic("set_k_table(1)");
		if ( t->d.ids == 0 )
			er_panic("set_k_table(2)");
		if ( l_strcmp(t->d.name,name) )
			er_panic("set_k_table(4)");
		ids = read_filespace(p,t->d.ids);
		change_endian_k_ids(&ids->d);
		if ( ids->d.h.type != PNT_K_IDS )
			er_panic("set_k_table(3)");
		len = (ids->d.h.size - sizeof(PN_HEADER))/ID_SIZE;
		ids = d_re_alloc(ids,PN_K_IDS_S(len+1) + sizeof(ACC_HEADER));
		if ( strlen(id) >= ID_SIZE )
			er_panic("id_size(5)");
		strcpy(ids->d.id[len],id);
		ids->d.h.size = PN_K_IDS_S(len+1);
		free_filespace(p,ids->a.fofs);
		t->d.ids = ids->a.fofs = alloc_filespace(p,&ids->d.h);
		change_endian_k_table(&t->d);
		change_endian_k_ids(&ids->d);
		write_filespace(p,t);
		write_filespace(p,ids);
		ret = t->a.fofs;
		d_f_ree(t);
		d_f_ree(ids);
	}
	return ret;
}

ACC_PN_K_IDS *
get_ids(unsigned int fofs)
{
ACC_PN_K_TABLE * t;
ACC_PN_K_IDS * ids;
	t = read_filespace(p,fofs);
	change_endian_k_table(&t->d);
	if ( t->d.h.type != PNT_K_TABLE ) 
		er_panic("get_ids");
	if ( t->d.ids == 0 )
		er_panic("get_ids(2)");
	ids = read_filespace(p,t->d.ids);
	d_f_ree(t);
	return ids;
}

ACC_PN_K_POINTS *
get_points(unsigned int fofs)
{
ACC_PN_K_TABLE * t;
ACC_PN_K_POINTS * points;
	t = read_filespace(p,fofs);
	change_endian_k_table(&t->d);
	if ( t->d.h.type != PNT_K_TABLE ) 
		er_panic("get_points");
	if ( t->d.points == 0 )
		er_panic("get_points(2)");
	points = read_filespace(p,t->d.points);
	d_f_ree(t);
	return points;
}

void
clear_space(char ** lp)
{
char * p;
	p = *lp;
	for ( ; *p &&
		(*p == ' ' ||
		*p == '\t' ||
		*p == '\r' ||
		*p == '\n' ||
		*p == 'M' ||
		*p == 'L' ); p ++ );
	*lp = p;
}

REAL1
get_real(char ** lp)
{
char buf[50];
char * p1, * p2;
REAL1 ret;

	clear_space(lp);
	if ( *lp == 0 )
		return 0;
	p1 = *lp;
	p2 = buf;
	for ( ; *p1 &&
			*p1 != ' ' &&
			*p1 != '\t' &&
			*p1 != '\r' &&
			*p1 != '\n' &&
			*p1 != 'M' &&
			*p1 != 'L' ; )
		*p2 ++ = *p1++;
	*p2 = 0;
	sscanf(buf,"%f",&ret);
	*lp = p1;
	return ret;
}

GB_POINT * 
get_plist(int * lenp,char * lst)
{
REAL1 x,y;
GB_POINT * ret;
int len;
	len = 0;
	ret = d_alloc(sizeof(GB_POINT));
	for ( ; *lst ; ) {
		x = get_real(&lst);
		y = get_real(&lst);
		ret = d_re_alloc(ret,(len+1)*sizeof(GB_POINT));
		ret[len].x = x;
		ret[len].y = y;
		len ++;
	}
	*lenp = len;
	return ret;
}


int
cmp_ptr(GB_POINT p1,GB_POINT p2)
{
	if ( p1.x < p2.x )
		return -1;
	if ( p1.x > p2.x )
		return 1;
	if ( p1.y < p2.y )
		return -1;
	if ( p1.y > p2.y )
		return 1;
	return 0;
}

GB_POINT *
marge_points(int * lenp,GB_POINT * ptr1,int len1,GB_POINT * ptr2,int len2)
{
GB_POINT * ret, * ptr3;
int i1,i2,i;
int cmp;
	ret = d_alloc((len1+len2)*sizeof(GB_POINT));
	i1 = i2 = i = 0;
	for ( ; i1 < len1 && i2 < len2 ; ) {
		cmp = cmp_ptr(ptr1[i1],ptr2[i2]);
		if ( cmp < 0 ) {
			ret[i] = ptr1[i1];
			i ++;
			i1 ++;
		}
		else if ( cmp == 0 ) {
			ret[i] = ptr1[i1];
			i ++;
			i1 ++;
			i2 ++;
		}
		else {
			ret[i] = ptr2[i2];
			i ++;
			i2 ++;
		}
	}
	for ( ; i1 < len1 ; )
		ret[i++] = ptr1[i1++];
	for ( ; i2 < len2 ; )
		ret[i++] = ptr2[i2++];
	*lenp = i;
	return ret;
}

GB_POINT *
sort_points(int *lenp,GB_POINT * ptr,int len)
{
GB_POINT * ret, * ret1, * ret2;
int len1,len2,len3;
	if ( len == 0 ) {
		*lenp = 0;
		return 0;
	}
	if ( len == 1 ) {
		*lenp = 1;
		ret = d_alloc(sizeof(GB_POINT));
		*ret = *ptr;
		return ret;
	}
	ret1 = sort_points(&len1,ptr,len/2);
	ret2 = sort_points(&len2,&ptr[len/2],len - (len/2));
	ret = marge_points(&len3,ret1,len1,ret2,len2);
	d_f_ree(ret1);
	d_f_ree(ret2);
	*lenp = len3;
	return ret;
}

void
insert_point(char * id,char * plist)
{
int len,flen;
GB_POINT * ptr1,* ptr2;
FAVT_ROOT * r;
FAVT_NODE * n;
IDS_NODE idsn;
IDS_NODE * idsp;
ACC_PN_K_TABLE * t;
ACC_PN_K_POINTS * points;

	r = get_root(p,ids_root,dummy_endian);
	if ( r == 0 )
		er_panic("insert_point");
	strcpy(idsn.id,id);
	n = favt_search(r,root_node(r),&idsn,cmp_string);
	if ( n == 0 )
		return;
printf("*****************\n");
	idsp = n->data;
	if ( idsp->fofs == 0 )
		er_panic("insert_point(1)");
	t = read_filespace(p,idsp->fofs);
	change_endian_k_table(&t->d);
	if ( t->d.h.type != PNT_K_TABLE )
		er_panic("inesrt_point(2)");
printf("== %s\n",n_string(std_cm,t->d.name));
       	ptr1 = get_plist(&len,plist);
	if ( t->d.points ) {
		points = read_filespace(p,t->d.points);
		change_endian_k_points_to_host(&points->d);
		if ( points->d.h.type != PNT_K_POINTS )
			er_panic("insert_point(3)");
		flen = (points->d.h.size - PN_K_POINTS_S(0))/sizeof(GB_POINT);
		ptr2 = sort_points(&len,ptr1,len);
		d_f_ree(ptr1);
		ptr1 = ptr2;
		ptr2 = marge_points(&len,points->d.p,flen,ptr1,len);
		points = d_re_alloc(points,
			PN_K_POINTS_S(len) + sizeof(ACC_HEADER));
		points->d.h.size = PN_K_POINTS_S(len);
		memcpy(points->d.p,ptr2,len*sizeof(GB_POINT));
		free_filespace(p,points->a.fofs);
		t->d.points = points->a.fofs = alloc_filespace(p,&points->d.h);
		change_endian_k_points_to_net(&points->d);
		write_filespace(p,points);
		d_f_ree(ptr1);
		d_f_ree(ptr2);
	}
	else {
		ptr2 = sort_points(&len,ptr1,len);
		d_f_ree(ptr1);
		ptr1 = ptr2;

		points = d_alloc(PN_K_POINTS_S(len) + sizeof(ACC_HEADER));
		points->d.h.type = PNT_K_POINTS;
		points->d.h.size = PN_K_POINTS_S(len);
		memcpy(points->d.p,ptr1,len*sizeof(GB_POINT));
		t->d.points = points->a.fofs = alloc_filespace(p,&points->d.h);
		change_endian_k_points_to_net(&points->d);
		write_filespace(p,points);
		d_f_ree(ptr1);
	}
	change_endian_k_table(&t->d);
	write_filespace(p,t);
}


int
dtree_alias(L_CHAR * from,L_CHAR * to)
{
unsigned int fofs;
int er;
int size;
	fofs = search_dtree(&size,get_root(p,names_root,dtree_endian),
			from,1);
	if ( fofs == 0 )
		return -1;
	er = insert_dtree(get_root(p,names_root,dtree_endian),
		to,0,fofs);
	if ( er < 0 )
		return -2;
	return 0;
}


int
get_line(GB_POINT * dp,GB_POINT * qp,GB_POINT * ptr,int len)
{
GB_POINT q;
int i;
GB_POINT * p;
double A,B,sum1,sum2;
GB_POINT d1,d2;
double r;
	q.x = q.y = 0;

	for ( i = 0 ; i < len ; i ++ ) 
		q = p_add(q,ptr[i]);
	q.x = q.x/len;
	q.y = q.y/len;
	*qp = q;
	p = d_alloc(sizeof(GB_POINT)*len);
	for ( i = 0 ; i < len ; i ++ )
		p[i] = p_sub(ptr[i],q);
	A = B = 0;
	for ( i = 0 ; i < len ; i ++ ) {
		A += p[i].x * p[i].y;
		B += - p[i].x * p[i].x + p[i].y * p[i].y;
	}
	if ( A == 0 ) {
		d1.x = 1;
		d1.y = 0;
		d2.x = 0;
		d2.y = 1;
	}
	else {
		d1.x = -B + sqrt(B*B + 4*A*A);
		d1.y = 2*A;
		r = sqrt(inner(d1,d1));
		d1.x = d1.x/r;
		d1.y = d1.y/r;

		d2.x = -B - sqrt(B*B + 4*A*A);
		d2.y = 2*A;
		r = sqrt(inner(d2,d2));
		d2.x = d2.x/r;
		d2.y = d2.y/r;
	}
	sum1 = 0;
	sum2 = 0;
	for ( i = 0 ; i < len ; i ++ ) {
		r = inner(d1,p[i]);
		sum1 += r*r;
		r = inner(d2,p[i]);
		sum2 += r*r;
	}
	if ( sum1 < sum2 )
		*dp = d1;
	else	*dp = d2;
	return 0;
}

GB_POINT
get_cross_point(GB_POINT d1,GB_POINT q1,GB_POINT d2,GB_POINT q2)
{
double result[E_DIM],v[E_DIM];
double m[E_DIM][E_DIM];
GB_POINT ret;
	v[0] = inner(d1,q1);
	v[1] = inner(d2,q2);
	v[2] = 1;
	m[0][0] = d1.x;
	m[0][1] = d1.y;
	m[0][2] = 0;
	m[1][0] = d2.x;
	m[1][1] = d2.y;
	m[1][2] = 0;
	m[2][0] = 0;
	m[2][1] = 0;
	m[2][2] = 1;
	equation(result,m,v);
	ret.x = result[0];
	ret.y = result[1];
	return ret;
}


GB_RECT check_rect = {
	{-114290, -25155},
	{-103789, -18774}
};

int
extended_cross(GB_POINT * retp,ACC_PN_K_POINTS * ptr1,ACC_PN_K_POINTS * ptr2)
{
int len1,len2;
GB_POINT d1,d2,q1,q2;
REAL1 r;
GB_POINT ret;
	len1 = (ptr1->d.h.size - sizeof(PN_HEADER))/sizeof(GB_POINT);
	len2 = (ptr2->d.h.size - sizeof(PN_HEADER))/sizeof(GB_POINT);
	get_line(&d1,&q1,ptr1->d.p,len1);
	get_line(&d2,&q2,ptr2->d.p,len2);
	r = inner(d1,d2)/(sqrt(inner(d1,d1))*sqrt(inner(d2,d2)));
	if ( r >= 0.5 )
		return -1;
	ret = get_cross_point(d1,q1,d2,q2);
	if ( inside_rect(&check_rect,ret) == 0 )
		return -1;
	*retp = ret;
	return 0;
}

