/**********************************************************************
 
	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	<stdlib.h>
#include	<math.h>
#include	"resource.h"
#include	"memory_debug.h"
#include	"memory_routine.h"
#include	"xlerror.h"

extern SEM res_lock;

void
set_triangle_minrect(TRIANGLE * t)
{
int i;
	t->minrect.tl.x = t->minrect.tl.y = 0;
	t->minrect.br.x = t->minrect.br.y = -1;
	for ( i = 0 ; i < 3 ; i ++ )
		insert_rect(&t->minrect,t->ptp[i]);
}

void
marge(TRIANGLE ** list1,TRIANGLE ** list2,int st,int len,int cnt)
{
int i,j,k;
int end1,end2;
	i = st;
	j = st + len;
	k = st;
	end1 = st + len;
	if ( end1 > cnt )
		end1 = cnt;
	end2 = st + 2*len;
	if ( end2 > cnt )
		end2 = cnt;
	for ( ; i < end1 && j < end2 ; ) {
		if ( list1[i]->resolution_rate <
				list1[j]->resolution_rate ) {
			list2[k] = list1[i];
			k ++;
			i ++;
		}
		else {
			list2[k] = list1[j];
			k ++;
			j ++;
		}
	}
	for ( ; i < end1 ; i ++ , k ++ )
		list2[k] = list1[i];
	for ( ; j < end2 ; j ++ , k ++ )
		list2[k] = list1[j];
}


TRIANGLE *
get_middle(TRIANGLE_SET * ts)
{
TRIANGLE ** list1,** list2, ** tmp;
int cnt;
TRIANGLE * t1;
int i,j;
int len,st;
TRIANGLE * ret;
	cnt = 0;
	for ( t1 = ts->tri_list ; t1 ; t1 = t1->next )
		cnt ++;
	list1 = d_alloc(sizeof(TRIANGLE*)*cnt);
	list2 = d_alloc(sizeof(TRIANGLE*)*cnt);
	i = 0;
	for ( t1 = ts->tri_list ; t1 ; t1 = t1->next , i ++ )
		list1[i] = t1;
	for ( len = 1 ; len < cnt ; len = len*2 ) {
		for ( st = 0 ; st < cnt ; st += len*2 )
			marge(list1,list2,st,len,cnt);
		tmp = list1;
		list1 = list2;
		list2 = tmp;
	}
	ret = list1[cnt/2];
	d_f_ree(list1);
	d_f_ree(list2);
	return ret;
}

void
set_outside(TRIANGLE_SET * ts,TRIANGLE * t)
{
int i,j;
int cnt;
REAL1 rate;
TRIANGLE * t2;
	switch ( ts->sts ) {
	case TS_NOSET:
		ts->sts = TS_TMP;
		ts->a = t->a;
		ts->minrect.tl = ts->minrect.br = t->ptp[0];
		insert_rect(&ts->minrect,t->ptp[1]);
		insert_rect(&ts->minrect,t->ptp[2]);
		ts->cnt = 1;
		ts->resolution_rate = 1/puseudo_rate(ts->a.matrix);
		break;
	case TS_FIX:
		insert_rect(&ts->minrect,t->ptp[0]);
		insert_rect(&ts->minrect,t->ptp[1]);
		insert_rect(&ts->minrect,t->ptp[2]);
		break;
	case TS_TMP:
		t2 = get_middle(ts);
		ts->a = t2->a;

		insert_rect(&ts->minrect,t->ptp[0]);
		insert_rect(&ts->minrect,t->ptp[1]);
		insert_rect(&ts->minrect,t->ptp[2]);

		ts->resolution_rate = t2->resolution_rate;
		break;
	default:
		er_panic("set_outside");
	}
}

XL_SEXP *
triangle_map(XLISP_ENV * e,XL_SEXP * s)
{
XL_SEXP * tag;
int i;
RESOURCE * r;
MAP_POINT_LIST * p;
MAP_POINT_LIST * ptr[3];
TRIANGLE * frw,* rev;
XL_SEXP * ret;

	r = get_resource_ptr(&ret,e,s->h.file,s->h.line);
	if ( r == 0 )
		return ret;
	for ( i = 1 ; i <= 3 ; i ++ ) {
		tag = eval(e,get_el(s,i));
		switch ( get_type(tag) ) {
		case XLT_ERROR:
			return tag;
		case XLT_STRING:
			break;
		default:
			goto typemissmatch;
		}
		lock_task(res_lock);
		for ( p = R_NEXT(MAP_POINT_LIST*,&r->map.point_list);
				&p->h != &r->map.point_list;
				p = R_NEXT(MAP_POINT_LIST*,&p->h) )
			if ( l_strcmp(p->tag,tag->string.data) == 0 )
				goto ok;
		unlock_task(res_lock,"triagle_map");
		return get_error(
			s->h.file,
			s->h.line,
			XLE_PROTO_UNDEF_NAME,
			l_string(std_cm,"triangle-map"),
			list(n_get_string("undefined tag"),0));
	ok:
		unlock_task(res_lock,"triagle_map");
		ptr[i-1] = p;
	}
	frw = d_alloc(sizeof(*frw));
	rev = d_alloc(sizeof(*rev));
	frw->c = rev;
	rev->c = frw;

	for ( i = 0 ; i < 3 ; i ++ ) {
		frw->ptl[i] = ptr[i];
		frw->ptp[i] = ptr[i]->src;
		rev->ptl[i] = ptr[i];
		rev->ptp[i] = ptr[i]->dest;
	}
	if ( get_matrix(&frw->a,
			ptr[0]->src,ptr[0]->dest,
			ptr[1]->src,ptr[1]->dest,
			ptr[2]->src,ptr[2]->dest) < 0 ) {
		return get_error(
			s->h.file,
			s->h.line,
			XLE_PROTO_INV_OBJECT,
			l_string(std_cm,"triangle-map"),
			list(n_get_string("invalid triangle"),0));
	}
	if ( get_matrix(&rev->a,
			ptr[0]->dest,ptr[0]->src,
			ptr[1]->dest,ptr[1]->src,
			ptr[2]->dest,ptr[2]->src) < 0 ) {
		return get_error(
			s->h.file,
			s->h.line,
			XLE_PROTO_INV_OBJECT,
			l_string(std_cm,"triangle-map"),
			list(n_get_string("invalid triangle"),0));
	}
	frw->resolution_rate = 1/puseudo_rate(frw->a.matrix);
	rev->resolution_rate = 1/puseudo_rate(rev->a.matrix);
	set_triangle_minrect(frw);
	set_triangle_minrect(rev);

	lock_task(res_lock);

	if ( r->map.param_forward.type != MT_TRIANGLE ) {
		r->map.param_forward.type = MT_TRIANGLE;
		r->map.param_forward.opt.tri.sts = TS_NOSET;
		r->map.param_forward.opt.tri.tri_list = frw;
		frw->next = 0;
	}
	else {
		frw->next = r->map.param_forward.opt.tri.tri_list;
		r->map.param_forward.opt.tri.tri_list = frw;
	}
	set_outside(&r->map.param_forward.opt.tri,frw);
	if ( r->map.param_reverse.type != MT_TRIANGLE ) {
		r->map.param_reverse.type = MT_TRIANGLE;
		r->map.param_reverse.opt.tri.sts = TS_NOSET;
		r->map.param_reverse.opt.tri.tri_list = rev;
		rev->next = 0;
	}
	else {
		rev->next = r->map.param_reverse.opt.tri.tri_list;
		r->map.param_reverse.opt.tri.tri_list = rev;
	}
	set_outside(&r->map.param_reverse.opt.tri,rev);
	set_reload(r);
	unlock_task(res_lock,"map_triagle_map");
	return 0;
typemissmatch:
	return get_error(
		s->h.file,
		s->h.line,
		XLE_SEMANTICS_TYPE_MISSMATCH,
		l_string(std_cm,"triangle-map"),
		list(n_get_string("type missmatch"),0));
}
