/**********************************************************************
 
	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	<stdlib.h>
#include	"utils.h"
#include	"gbview.h"
#include	"xlerror.h"
#include	"u_math.h"
#include	"routing.h"
#include	"memory_debug.h"
#include	"xl.h"

/* List of points
one point:
	(x y)

one pair of point and resolution:
	((x y) r1)

list of points:
	((x1 y1)
	 (x2 y2)
	 (x3 y3)
	 ....)

list of points and resolution:
	(((x1 y1) r1)
	 ((x2 y2) r2)
	 ((x3 y3) r3)
	 ....)

*/

#define CPP_POINT	0
#define CPP_RESO	1
#define CPP_OTHER	2


int
check_point_pair(XL_SEXP * inp)
{
int len;
XL_SEXP * a,* b;
	if ( get_type(inp) != XLT_PAIR )
		return CPP_OTHER;
	len = list_length(inp);
	if ( len != 2 )
		return CPP_OTHER;
	a = get_el(inp,0);
	b = get_el(inp,1);
	switch ( get_type(b) ) {
	case XLT_INTEGER:
	case XLT_FLOAT:
		break;
	default:
		return CPP_OTHER;
	}
	switch ( get_type(a) ) {
	case XLT_INTEGER:
	case XLT_FLOAT:
		return CPP_POINT;
	case XLT_PAIR:
		if ( list_length(a) != 2 )
			return CPP_OTHER;
		if ( check_point_pair(a) == CPP_POINT )
			return CPP_RESO;
		return CPP_OTHER;
	default:
		return CPP_OTHER;
	}
}

int
get_point_list(
	COORDINATE_UNIT * cu,
	GB_POINT ** _ptr_list,
	int * reso_fg,
	REAL1 ** _reso_list,
	XL_SEXP * inp)
{
int ret;
GB_POINT * ptr_list;
REAL1 * reso_list;
XL_SEXP * el;
int i;
int er;
	switch ( check_point_pair(inp) ) {
	case CPP_POINT:
		ptr_list = d_alloc(sizeof(GB_POINT));
		reso_list = d_alloc(sizeof(REAL1));
		*reso_fg = 0;
		reso_list[0] = 1;
		if ( get_gbpoint(cu,ptr_list,inp) < 0 ) {
			d_f_ree(ptr_list);
			d_f_ree(reso_list);
			ret = -1;
			goto syntax;
		}
		*_ptr_list = ptr_list;
		*_reso_list = reso_list;
		return 1;
	case CPP_RESO:
		ptr_list = d_alloc(sizeof(GB_POINT));
		reso_list = d_alloc(sizeof(REAL1));
		*reso_fg = 1;
		if ( get_gbpoint(cu,ptr_list,car(inp)) < 0 ) {
			d_f_ree(ptr_list);
			d_f_ree(reso_list);
			ret = -2;
			goto syntax;
		}
		reso_list[0] = get_sexp2real(&er,cu,get_el(inp,1),1);
		if ( er < 0 ) {
			d_f_ree(ptr_list);
			d_f_ree(reso_list);
			ret = -3;
			goto syntax;
		}
		*_ptr_list = ptr_list;
		*_reso_list = reso_list;
		return 1;
	case CPP_OTHER:
		break;
	}
	if ( get_type(inp) != XLT_PAIR ) {
		ret = -4;
		goto syntax;
	}
	ret = list_length(inp);
	ptr_list = d_alloc(sizeof(GB_POINT)*ret);
	reso_list = d_alloc(sizeof(REAL1)*ret);
	i = 0;
	*reso_fg = 0;
	for ( ; get_type(inp) == XLT_PAIR ; inp = cdr(inp) , i ++ ) {
		el = car(inp);
		switch ( check_point_pair(el) ) {
		case CPP_POINT:
			reso_list[i] = 1;
			if ( get_gbpoint(cu,&ptr_list[i],el) < 0 ) {
				d_f_ree(ptr_list);
				d_f_ree(reso_list);
				ret = -5;
				goto syntax;
			}
			break;
		case CPP_RESO:
			*reso_fg = 1;
			if ( get_gbpoint(cu,&ptr_list[i],car(el)) < 0 ) {
				d_f_ree(ptr_list);
				d_f_ree(reso_list);
				ret = -6;
				goto syntax;
			}
			reso_list[i] = get_sexp2real(&er,cu,get_el(el,1),1);
			if ( er < 0 ) {
				d_f_ree(ptr_list);
				d_f_ree(reso_list);
				ret = -7;
				goto syntax;
			}
			break;
		case CPP_OTHER:
			d_f_ree(ptr_list);
			d_f_ree(reso_list);
			ret = -8;
			goto syntax;
			
		}
	}
	*_ptr_list = ptr_list;
	*_reso_list = reso_list;
	return ret;
syntax:
	return ret;
}

XL_SEXP *
get_one_point(
	COORDINATE_UNIT * cu,
	GB_POINT pt,
	int fg,
	REAL1 reso)
{
	if ( fg == 0 ) {
		return List(
			get_floating(pt.x,cu->unit),
			get_floating(pt.y,cu->unit),
			-1);
	}
	else {
		return List(
				List(	get_floating(pt.x,cu->unit),
					get_floating(pt.y,cu->unit),
					-1),
				get_floating(reso,
					reso_c_unit(cu)),
				-1);
	}
}

XL_SEXP *
get_point_list_sexp(
	COORDINATE_UNIT * cu,
	GB_POINT * ptr_list,
	int reso_fg,
	REAL1 * reso_list,
	int len)
{
XL_SEXP * ret;
	if ( len == 1 )
		return get_one_point(cu,ptr_list[0],reso_fg,reso_list[0]);
	ret = 0;
	for ( ; len > 0 ; len -- )
		ret = cons(
			get_one_point(cu,
				*ptr_list++,
				reso_fg,
				*reso_list++),ret);
	return reverse(ret);
}


XL_SEXP *
gb_convert_coordinate(
	XLISP_ENV * env,
	XL_SEXP * s,
	XLISP_ENV * a,
	XL_SYM_FIELD * sf)
{
XL_SEXP * src;
XL_SEXP * dest;
MAP_HISTORY * mh, * mh_p = 0;
L_CHAR * waiting_mode;
int wm;
char * err;
XL_SEXP * ret;
REAL1 * reso_list = 0;
GB_POINT * ptr_list = 0;
int len;
int reso_fg;
COORDINATE_UNIT * cu;

	waiting_mode = get_sf_attribute(
				sf,l_string(std_cm,"waitmode"));
	err = "waitmode";
	if ( waiting_mode == 0 )
		wm = RR_WM_DIRECT;
	else if ( l_strcmp(waiting_mode,
			l_string(std_cm,"direct.nowait")) == 0 )
		wm = RR_WM_DIRECT_NO_WAIT;
	else if ( l_strcmp(waiting_mode,
			l_string(std_cm,"indirect.nowait")) == 0 )
		wm = RR_WM_INDIRECT_NO_WAIT;
	else if ( l_strcmp(waiting_mode,l_string(std_cm,"direct")) == 0 )
		wm = RR_WM_DIRECT;
	else if ( l_strcmp(waiting_mode,l_string(std_cm,"indirect")) == 0 )
		wm = RR_WM_INDIRECT;
	else	goto param_error;

	src = get_el(s,1);
	err = "source";
	if ( get_type(src) != XLT_STRING )
		goto type_missmatch;
	dest = get_el(s,2);
	err = "destination";
	if ( get_type(dest) != XLT_STRING )
		goto type_missmatch;

	mh = resolve_mappath(0,src->string.data,dest->string.data,wm);
	if ( mh == MP_MH_NO_ROUTE )
		goto no_route_error;

	if ( mh == 0 ) {
		ret = get_el(s,3);
		goto end;
	}
	if ( mh->type != MHT_MAP )
		er_panic("convert_coordinate");
	if ( mh->dir == MHD_FORWARD )
		cu = &mh->d.map->map.cu_src;
	else	cu = &mh->d.map->map.cu_dest;
	len = get_point_list(
		cu,
		&ptr_list,
		&reso_fg,
		&reso_list,
		get_el(s,3));
	if ( len < 0 )
		goto arg_param_error;

	map_from_flame(mh,ptr_list,reso_list,len);

	mh_p = mh;
	for ( ; mh_p->next ; mh_p = mh_p->next );
	if ( mh_p->dir == MHD_FORWARD )
		cu = &mh_p->d.map->map.cu_dest;
	else 	cu = &mh_p->d.map->map.cu_src;
	ret = get_point_list_sexp(cu,ptr_list,reso_fg,reso_list,len);
	goto end;
no_route_error:
	ret = get_error(
		s->h.file,
		s->h.line,
		XLE_PROTO_NO_ROUTE,
		l_string(std_cm,"ConvertCoordinate"),
		n_get_string("there is no route of mapping path"));
	goto end;

type_missmatch:
	ret = get_error(
		s->h.file,
		s->h.line,
		XLE_SEMANTICS_TYPE_MISSMATCH,
		l_string(std_cm,"ConvertCoordinate"),
		List(n_get_string("type missmatch"),
			n_get_string(err),
			-1));
	goto end;
param_error:
	ret = get_error(
		s->h.file,
		s->h.line,
		XLE_PROTO_INV_PARAM,
		l_string(std_cm,"ConvertCoordinate"),
		List(n_get_string("attribute parameter error"),
			n_get_string(err),
			-1));
	goto end;
arg_param_error:
	ret = get_error(
		s->h.file,
		s->h.line,
		XLE_PROTO_INV_PARAM,
		l_string(std_cm,"ConvertCoordinate"),
		List(n_get_string("argument parameter error"),
			get_integer(len,0),
			-1));
	goto end;
end:
	free_mh(mh);
	if ( ptr_list )
		d_f_ree(ptr_list);
	if ( reso_list )
		d_f_ree(reso_list);
	return ret;
}


void
init_gb_convert_coordinate(XLISP_ENV * env)
{
	set_env(env,l_string(std_cm,"ConvertCoordinate"),
		get_func_prim(gb_convert_coordinate,FO_APPLICATIVE,0,4,4));
}

