/**********************************************************************
 
	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	"memory_debug.h"
#include	"xlerror.h"
#include	"xl.h"
#include	"kyotodb.h"
#include	"favt.h"
#include	"dtree.h"
#include	"filespace.h"

#define K_OFFSET	20

typedef struct hit_list {
	struct hit_list *	next;
	int			stp;
	int			len;
	unsigned int		fofs;
} HIT_LIST;


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


XL_SEXP * xl_search();

void
init_search(XLISP_ENV * env)
{
	set_env(env,l_string(std_cm,"search"),
		get_func_prim(xl_search,FO_APPLICATIVE,0,2,2));
}

L_CHAR * 
cutpattern_check(L_CHAR * poffset,L_CHAR * n)
{
int i,ofs;
int len,plen;
	len = l_strlen(n);
	*poffset = 0;
	for ( i = 0 ; cutpattern[i][0] ; i ++ ) {
		if ( cutpattern[i][0] == '*' ) {
			plen = l_strlen(&cutpattern[i][1]);
			if ( len < plen )
				continue;
			if ( l_strcmp(&n[len-plen],&cutpattern[i][1]) == 0 ) {
				*poffset = patoffset[i];
				n[len-plen] = 0;
				return 0;
			}
		}
		else {
			plen = l_strlen(cutpattern[i]);
			if ( len < plen )
				continue;
			for ( ofs = 0 ; ofs <= len-plen ; ofs ++ ) {
				if ( memcmp(&n[ofs],cutpattern[i],
						sizeof(L_CHAR)*plen) == 0 ) {
					*poffset = patoffset[i];
					n[ofs] = 0;
					if ( n[ofs+plen] == 0 )
						return 0;
					return &n[ofs+plen];
				}
			}
		}
	}
	return 0;
}

GB_POINT
get_patoffset(L_CHAR ofs)
{
GB_POINT ret;
	ret.x = ret.y = 0;
	switch ( ofs ) {
	case 0:
		break;
	case 'W':
		ret.y = - K_OFFSET;
		break;
	case 'E':
		ret.y = K_OFFSET;
		break;
	case 'N':
		ret.x = K_OFFSET;
		break;
	case 'S':
		ret.x = - K_OFFSET;
		break;
	default:
		er_panic("get_patoffset");
	}
	return ret;
}


XL_SEXP * 
get_data(unsigned int fofs,int flag)
{
XL_SEXP * ret;
ACC_PN_K_IDS * ids;
ACC_PN_K_POINTS * points;
int len;
int i;
	if ( flag == 0 ) {
		ids = get_ids(fofs);
		len = (ids->d.h.size - PN_K_IDS_S(0))/ID_SIZE;
		ret = 0;
		for ( i = 0 ; i < len ; i ++ )
			ret = cons(n_get_string(ids->d.id[i]),ret);
		d_f_ree(ids);
	}
	else {
		points = get_points(fofs);
		len = (points->d.h.size - PN_K_POINTS_S(0))/sizeof(GB_POINT);
		ret = 0;
		for ( i = 0 ; i < len ; i ++ )
			ret = cons(
				List(	get_floating(points->d.p[i].x,0),
					get_floating(points->d.p[i].y,0),
					-1),
				ret);
		d_f_ree(points);
	}
	return ret;
}

int
get_cross(GB_POINT * retp,ACC_PN_K_POINTS * points1,ACC_PN_K_POINTS * points2)
{
int ix1,ix2,len1,len2;
int cmp;
	len1 = (points1->d.h.size - PN_K_POINTS_S(0))/sizeof(GB_POINT);
	len2 = (points2->d.h.size - PN_K_POINTS_S(0))/sizeof(GB_POINT);
	ix1 = ix2 = 0;
	for ( ; ix1 < len1 && ix2 < len2 ; ) {
		cmp = cmp_ptr(
			points1->d.p[ix1],
			points2->d.p[ix2]);
		if ( cmp < 0 )
			ix1 ++;
		else if ( cmp > 0 )
			ix2 ++;
		else {
			*retp = points1->d.p[ix1];
			return 0;
		}
	}
	return -1;
}

int
get_rect(GB_RECT * r,ACC_PN_K_POINTS * points)
{
int len,i;
	len = (points->d.h.size - PN_K_POINTS_S(0))/sizeof(GB_POINT);
	r->tl.x = r->tl.y = 0;
	r->br.x = r->tl.y = -1;
	for ( i = 0 ; i < len ; i ++ )
		insert_rect(r,points->d.p[i]);
	return 0;
}

XL_SEXP *
xl_search_by_name(XLISP_ENV * env,XL_SEXP * s,int flag)
{
XL_SEXP * name;
L_CHAR * _name;
unsigned int fofs;
int size;
XL_SEXP * ret1,* ret2;
ACC_PN_K_POINTS * points;
int len,i;
	name = get_el(s,1);
	switch ( get_type(name) ) {
	case XLT_STRING:
		_name = name->string.data;
		break;
	default:
		goto type_missmatch;
	}
	fofs = search_dtree(&size,get_root(p,names_root,dtree_endian),
			_name,0);
	ret1 = ret2 = 0;
	if ( fofs ) {
		ret1 = get_data(fofs,flag);
		fofs = search_dtree(&size,get_root(p,names_root,dtree_endian),
			&_name[size],0);
		if ( fofs ) {
			ret2 = get_data(fofs,flag);
		}
	}
	return List(ret1,ret2,-1);
type_missmatch:
	return get_error(
		s->h.file,
		s->h.line,
		XLE_SEMANTICS_TYPE_MISSMATCH,
		l_string(std_cm,"search"),
		0);
}

L_CHAR *
get_hit(L_CHAR * n,int s,int e)
{
L_CHAR * ret;
L_CHAR * p1,* p2;
	ret = d_alloc((e-s+1)*sizeof(L_CHAR));
	p1 = ret;
	p2 = &n[s];
	for ( ; s < e ; e -- )
		*p1++ = *p2++;
	*p1 = 0;
	return ret;
}

void
set_prev_attr(XL_SEXP * tag,XL_SEXP * prev)
{
XL_SEXP * ptag;
XL_SYM_FIELD * sf;
	if ( prev == 0 )
		return;
	ptag = car(prev);
	if ( get_type(ptag) != XLT_SYMBOL )
		er_panic("set_prev_attr(1)");
	for ( sf = ptag->symbol.field ; sf ; sf = sf->next ) {
		if ( l_strcmp(sf->name,l_string(std_cm,"hit2")) == 0 )
			set_attribute(tag,
				l_string(std_cm,"phit2"),
				sf->data);
		else if ( l_strcmp(sf->name,l_string(std_cm,"hit1")) == 0 )
			set_attribute(tag,
				l_string(std_cm,"phit1"),
				sf->data);
	}
}


HIT_LIST *
hit_search(L_CHAR * str,HIT_LIST * hl)
{
int stp;
unsigned int fofs;
int len;
int size;
L_CHAR * buf;
HIT_LIST * ret, * hl1, * hl2;
	len = l_strlen(str);
	buf = d_alloc(sizeof(L_CHAR)*(len+1));
	if ( hl )
		stp = hl->stp + hl->len;
	else	stp = 0;
	ret = 0;
	for ( ; stp < len ; stp ++ ) {
		size = len-stp;
		for ( ; ; ) {
			memcpy(buf,&str[stp],size*sizeof(L_CHAR));
			buf[size] = 0;
			fofs = search_dtree(&size,
				get_root(p,names_root,dtree_endian),
				buf,0);
			if ( fofs == 0 )
				break;
			hl1 = d_alloc(sizeof(*hl1));
			hl1->fofs = fofs;
			hl1->stp = stp;
			hl1->len = size;
			hl1->next = ret;
			ret = hl1;
			size --;
		}
	}
	hl1 = ret;
	ret = 0;
	for ( ; hl1 ; ) {
		hl2 = hl1;
		hl1 = hl1->next;
		hl2->next = ret;
		ret = hl2;
	}
	d_f_ree(buf);
	return ret;
}

void
free_hit_list(HIT_LIST * h)
{
HIT_LIST * h1;
	for ( ; h ; ) {
		h1 = h->next;
		d_f_ree(h);
		h = h1;
	}
}

HIT_LIST * 
copy_hit_list(HIT_LIST * h)
{
HIT_LIST * ret;
	ret = d_alloc(sizeof(*ret));
	*ret = *h;
	ret->next = 0;
	return ret;
}

XL_SEXP *
xl_search_ptr(XLISP_ENV * env,XL_SEXP * s)
{
XL_SEXP * name;
L_CHAR * _name;
int size;
ACC_PN_K_POINTS * points1, * points2;
int len,i;
int er;
XL_SEXP * ret;
GB_POINT ptr;
GB_RECT rct;
XL_SEXP * tag;
L_CHAR * tmp, * pref;

HIT_LIST * h1,* h2,* h3, * h4;
int cross_miss;
HIT_LIST * h5,* h6;
L_CHAR pofs[2];
GB_POINT pofs_ptr;

	name = get_el(s,1);
	pofs[0] = pofs[1] = 0;
	switch ( get_type(name) ) {
	case XLT_STRING:
		_name = ll_copy_str(name->string.data);
		pref = cutpattern_check(&pofs[0],_name);
		pofs_ptr = get_patoffset(pofs[0]);
		if ( pofs[0] == 0 )
			pofs[0] = '*';
		break;
	default:
		goto type_missmatch;
	}

	h1 = hit_search(_name,0);
	if ( h1 == 0 )
		return 0;
	cross_miss = 0;
	h5 = h6 = 0;
	for ( h2 = h1 ; h2 ; h2 = h2->next ) {
		h3 = hit_search(_name,h2);
		if ( h3 == 0 )
			continue;
		points1 = get_points(h2->fofs);
		for ( h4 = h3 ; h4 ; h4 = h4->next ) {
			points2 = get_points(h4->fofs);
			er = get_cross(&ptr,points1,points2);
			if ( er < 0 ) {
				d_f_ree(points2);
				if ( cross_miss == 0 ) {
					h5 = h2;
					h6 = copy_hit_list(h4);
				}
				cross_miss = 1;
				continue;
			}
			goto hit2;
		}
		free_hit_list(h3);
	}
	if ( cross_miss == 0 )
		goto hit1;
extend_corss:
	points1 = get_points(h5->fofs);
	points2 = get_points(h6->fofs);
	if ( extended_cross(&ptr,points1,points2) < 0 ) {
		d_f_ree(points1);
		d_f_ree(points2);
		goto hit1;
	}
	ptr = p_add(ptr,pofs_ptr);
	tag = n_get_symbol("point");
	set_attribute(tag,
		l_string(std_cm,"offset"),
		pofs);
	set_attribute(tag,
		l_string(std_cm,"hit2"),
		tmp=get_hit(_name,
			h6->stp,h6->stp+h6->len));
	d_f_ree(tmp);
	set_attribute(tag,
		l_string(std_cm,"hit1"),
		tmp=get_hit(_name,
			h5->stp,h5->stp+h5->len));
	set_attribute(tag,
		l_string(std_cm,"cross"),
		l_string(std_cm,"extend"));
	d_f_ree(tmp);
	ret = List(tag,
		get_floating(ptr.x,0),
		get_floating(ptr.y,0),
		-1);
	free_hit_list(h1);
	free_hit_list(h6);
	d_f_ree(points1);
	d_f_ree(points2);
	return ret;
	
hit1:
	points1 = get_points(h1->fofs);
	get_rect(&rct,points1);
	tag = n_get_symbol("rect");
	set_attribute(tag,
		l_string(std_cm,"offset"),
		pofs);
	set_attribute(tag,
		l_string(std_cm,"hit1"),
		tmp=get_hit(_name,h1->stp,h1->stp+h1->len));
	if ( cross_miss )
		set_attribute(tag,
			l_string(std_cm,"cross"),
			l_string(std_cm,"miss"));
	d_f_ree(tmp);
	ret = List(tag,
		List(	get_floating(rct.tl.x,0),
			get_floating(rct.tl.y,0),
			-1),
		List(	get_floating(rct.br.x,0),
			get_floating(rct.br.y,0),
			-1),
		-1);
	d_f_ree(points1);
	free_hit_list(h1);
	return ret;
hit2:
	ptr = p_add(ptr,pofs_ptr);
	tag = n_get_symbol("point");
	set_attribute(tag,
		l_string(std_cm,"offset"),
		pofs);
	set_attribute(tag,
		l_string(std_cm,"hit2"),
		tmp=get_hit(_name,
			h4->stp,h4->stp+h4->len));
	d_f_ree(tmp);
	set_attribute(tag,
		l_string(std_cm,"hit1"),
		tmp=get_hit(_name,
			h2->stp,h2->stp+h2->len));
	d_f_ree(tmp);
	ret = List(tag,
		get_floating(ptr.x,0),
		get_floating(ptr.y,0),
		-1);
	free_hit_list(h1);
	free_hit_list(h3);
	d_f_ree(points1);
	d_f_ree(points2);
	return ret;

type_missmatch:
	return get_error(
		s->h.file,
		s->h.line,
		XLE_SEMANTICS_TYPE_MISSMATCH,
		l_string(std_cm,"search"),
		0);
}

XL_SEXP *
xl_search_by_id(XLISP_ENV * env,XL_SEXP * s)
{
XL_SEXP * id;
IDS_NODE idsn;
IDS_NODE * idsp;
ACC_PN_K_TABLE * t;
FAVT_ROOT * r;
FAVT_NODE * n;
XL_SEXP * ret;
	id = get_el(s,1);
	if ( get_type(id) != XLT_STRING )
		goto type_missmatch;
	r = get_root(p,ids_root,dummy_endian);
	if ( r == 0 )
		er_panic("search_by_id(1)");
	strcpy(idsn.id,n_string(std_cm,id->string.data));
	n = favt_search(r,root_node(r),&idsn,cmp_string);
	if ( n == 0 )
		goto no_data;
	idsp = n->data;
	t = read_filespace(p,idsp->fofs);
	if ( t == 0 )
		er_panic("search_by_id(2)");
	ret = get_string(t->d.name);
	d_f_ree(t);
	return ret;
type_missmatch:
	return get_error(
		s->h.file,
		s->h.line,
		XLE_SEMANTICS_TYPE_MISSMATCH,
		l_string(std_cm,"search"),
		0);
no_data:
	return get_error(
		s->h.file,
		s->h.line,
		XLE_PROTO_UNDEF_RESOURCE,
		l_string(std_cm,"search"),
		n_get_string("no data"));
}


XL_SEXP *
xl_search(XLISP_ENV * env,XL_SEXP * s,
	XLISP_ENV * a,XL_SYM_FIELD * sf)
{
int type;
int flag;
	type = 0;
	flag = 2;
	for ( ; sf ; sf = sf->next ) {
		if ( l_strcmp(sf->name,l_string(std_cm,"type")) == 0 ) {
			if ( l_strcmp(sf->data,
					l_string(std_cm,"byname")) == 0 )
				type = 0;
			else if ( l_strcmp(sf->data,
					l_string(std_cm,"byid")) == 0 )
				type = 1;
		}
		else if ( l_strcmp(sf->name,l_string(std_cm,"target")) == 0 ) {
			if ( l_strcmp(sf->data,
					l_string(std_cm,"id")) == 0 )
				flag = 0;
			else if ( l_strcmp(sf->data,
					l_string(std_cm,"point")) == 0 )
				flag = 1;
			else if ( l_strcmp(sf->data,
					l_string(std_cm,"position")) == 0 )
				flag = 2;
		}
	}
	switch ( type ) {
	case 0:
		if ( flag == 2 )
			return xl_search_ptr(env,s);
		else	return xl_search_by_name(env,s,flag);
	case 1:
		return xl_search_by_id(env,s);
	default:
		er_panic("xl_search");
	}
	return 0;
}
