/**********************************************************************
 
	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	"xlerror.h"
#include	"xl.h"

#include	"u_math.h"
#include	"gbgraph.h"
#include	"memory_debug.h"
#include	"ppm_types.h"

/*
	
	<Mercator proc="prev">
		<List>
			<List>
				<point> mer_x mer_y </point>
				<point> rad_x rad_y </point>
			</List>
			<List>
				<point> mer_x mer_y </point>
				<point> rad_x rad_y </point>
			</List>
			.....
		</List>
	</Mercator>

	RETURN VALUE:
	<List>
		<List>
			<List>
				<point> pos_x pos_y </point>
				<point> mer_x mer_y </point>
			</List>
			<List>
				<point> pos_x pos_y </point>
				<point> mer_x mer_y </point>
			</List>
			.....
		</List>
		dpr
	</List>


	<Mercator proc="after">
		from_filename.ppm
		to_filename.ppm
		north_rad
		south_rad
		west_rad
		dpr
		<AffenConvert>
	</Mercator>

	RETURN VALUE:
	<List>
		pixels_width
		pixels_height
		pixels_height_center,
	</List>
*/

typedef struct mercator_work {
	AFFEN2D 	a;
	double		dpr;
	double		west_rad;
	int		center;
	int		radius;
	int		rotate;
} MERCATOR_WORK;

XL_SEXP * xl_Mercator();

void
init_Mercator(XLISP_ENV * env)
{
	set_env(env,l_string(std_cm,"Mercator"),
		get_func_prim(xl_Mercator,FO_APPLICATIVE,0,2,8));
}

#define mconv(y) (- log(tan(M_PI/4 - (y)/2)))

void
mercator_pos(GB_POINT * pos,GB_POINT * rad,int len)
{
int i;
	for ( i = 0 ; i < len ; i ++ ) {
		pos[i].x = rad[i].x;
		pos[i].y = mconv(rad[i].y);
	}
}

double
get_dotprad(GB_POINT * mer,GB_POINT * pos,int len)
{
int i,j;
double ret;
int cnt;
double d1,d2;
	cnt = 0;
	ret = 0;
	for ( i = 0 ; i < len ; i ++ )
		for ( j = 0 ; j < len ; j ++ ) {
			if ( i == j )
				continue;
			cnt ++;
			d1 = distance(mer[i],mer[j]);
			d2 = distance(pos[i],pos[j]);
			ret += d1/d2;
		}
	return ret/cnt;
}

double
element_affen(GB_POINT * pos,GB_POINT * mer,GB_POINT * rad,int len)
{
	mercator_pos(pos,rad,len);
	return  get_dotprad(mer,pos,len);
}

XL_SEXP *
xl_MercatorPrev(XLISP_ENV * env,XL_SEXP * s)
{
GB_POINT * mer,* rad, * pos;
int len,i;
XL_SEXP * ptr, * co, * src, * dest;
double dpr;
XL_SEXP * ret_ptr;
	if ( list_length(s) != 2 )
		return get_error(
			s->h.file,
			s->h.line,
			XLE_SEMANTICS_INV_PARAM_LENGTH,
			l_string(std_cm,"Mercator(prev)"),
			List(n_get_string("parameter length"),
				-1));
	ptr = get_el(s,1);
	len = list_length(ptr);
	if ( len < 0 )
		return 0;
	mer = d_alloc(sizeof(GB_POINT)*len,123);
	rad = d_alloc(sizeof(GB_POINT)*len,123);
	pos = d_alloc(sizeof(GB_POINT)*len,123);

	i = 0;
	for ( ; get_type(ptr) == XLT_PAIR ; ptr = cdr(ptr) , i ++ ) {
		co = car(ptr);
		if ( list_length(co) != 2 )
			goto type_missmatch;
		src = get_el(co,0);
		dest = get_el(co,1);
		mer[i] = list2gbpoint(0,src);
		rad[i] = list2gbpoint(0,dest);
	}
	dpr = element_affen(pos,mer,rad,len);
	ret_ptr = 0;
	for ( i = 0 ; i < len ; i ++ ) {
		ret_ptr = cons(
			List(
				List(	get_floating(pos[i].x,0),
					get_floating(pos[i].y,0),
					-1),
				List(	get_floating(mer[i].x,0),
					get_floating(mer[i].y,0),
					-1),
				-1),
			ret_ptr);
	}
	return List(reverse(ret_ptr),
		get_floating(dpr,l_string(std_cm,"dot/rad")),-1);
type_missmatch:
	return get_error(
		s->h.file,
		s->h.line,
		XLE_SEMANTICS_TYPE_MISSMATCH,
		l_string(std_cm,"Mercator(prev)"),
		List(n_get_string("type_missmatch"),
			-1));
}


int test_mec;

int
mercator_converter(
	int * x,
	int * y,
	int * radius_width,
	int * radius_height,
	void * vp)
{
GB_POINT p;
MERCATOR_WORK * w;
int tmp;
double yy;
static double y_rad;
static int y_org = -1;

	w = vp;
	p.x = (*x)/w->dpr + w->west_rad;
	if ( y_org == *y )
		p.y = y_rad;
	else {
		y_rad = p.y = mconv(yy = (w->center - *y)/w->dpr);
		y_org = *y;

		ss_printf("==> %i %f  \r",*y,yy/M_PI*180);
	}

	if ( w->rotate ) {
		*radius_height = w->radius;
		*radius_width = w->radius/cos(p.x) - 1;
		if ( *radius_width < 0 )
			*radius_width = 0;
	}
	else {
		*radius_width = w->radius;
		*radius_height = w->radius/cos(p.x) - 1;
		if ( *radius_height < 0 )
			*radius_height = 0;
	}
	p = caffen2d(&w->a,p);
	*x = p.x;
	*y = p.y;
	return 0;
}


XL_SEXP *
xl_MercatorAfter(XLISP_ENV * env,XL_SEXP * s,XL_SYM_FIELD * sf)
{
MERCATOR_WORK mw;
XL_SEXP * ret;
XL_SEXP * af, * from_ppm,* to_ppm, * north, * south, * dpr;
XL_SEXP * west, * east;
int height,width;
L_CHAR * radius;
double north_rad,south_rad,west_rad;
L_CHAR * rotate;
char * e_param;
	if ( list_length(s) != 8 )
		return get_error(
			s->h.file,
			s->h.line,
			XLE_SEMANTICS_INV_PARAM_LENGTH,
			l_string(std_cm,"Mercator(after)"),
			List(n_get_string("parameter length"),
				-1));
	radius = get_sf_attribute(sf,l_string(std_cm,"radius"));
	if ( radius == 0 )
		mw.radius = 0;
	else	mw.radius = atoi(n_string(std_cm,radius));
	rotate = get_sf_attribute(sf,l_string(std_cm,"rotate"));
	if ( rotate == 0 )
		mw.rotate = 0;
	else if ( atoi(n_string(std_cm,rotate)) == 0 )
		mw.rotate = 0;
	else	mw.rotate = 1;
	af = get_el(s,7);
	if ( list2matrix(&ret,mw.a.matrix,get_el(af,0),s->h.file,s->h.line,
				"MercatorAfter(1)") < 0 )
		return ret;
	if ( list2vector(&ret,&mw.a.org,get_el(af,1),s->h.file,s->h.line,
				"MercatorAfter(2)") < 0 )
		return ret;
	from_ppm = get_el(s,1);
	if ( get_type(from_ppm) != XLT_STRING ) {
		e_param = "from ppm";
		goto type_missmatch;
	}
	to_ppm = get_el(s,2);
	if ( get_type(to_ppm) != XLT_STRING ) {
		e_param = "to ppm";
		goto type_missmatch;
	}
	north = get_el(s,3);
	switch ( get_type(north) ) {
	case XLT_FLOAT:
		north_rad = north->floating.data;
		break;
	case XLT_INTEGER:
		north_rad = north->integer.data;
		break;
	default:
		e_param = "north rad";
		goto type_missmatch;
	}
	south = get_el(s,4);
	switch ( get_type(south) ) {
	case XLT_FLOAT:
		south_rad = south->floating.data;
		break;
	case XLT_INTEGER:
		south_rad = south->integer.data;
		break;
	default:
		e_param = "south rad";
		goto type_missmatch;
	}

	west = get_el(s,5);
	switch ( get_type(west) ) {
	case XLT_FLOAT:
		west_rad = west->floating.data;
		break;
	case XLT_INTEGER:
		west_rad = west->integer.data;
		break;
	default:
		e_param = "west rad";
		goto type_missmatch;
	}

	dpr = get_el(s,6);
	switch ( get_type(dpr) ) {
	case XLT_FLOAT:
		mw.dpr = dpr->floating.data;
		break;
	case XLT_INTEGER:
		mw.dpr = dpr->integer.data;
		break;
	default:
		e_param = "dot per rad";
		goto type_missmatch;
	}
	mw.center = north_rad * mw.dpr;
	mw.west_rad = west_rad;
	width = 2*M_PI * mw.dpr;
	height = mw.center - south_rad * mw.dpr;
ss_printf("north %f - south %f\n",north_rad/M_PI*180,south_rad/M_PI*180);
	ppm_converter(
		n_string(std_cm,from_ppm->string.data),
		n_string(std_cm,to_ppm->string.data),
		width,
		height,
		mercator_converter,
		&mw);
ss_printf("\n");
	return 	List(
			get_integer(width,0),
			get_integer(height,0),
			get_integer(mw.center,0),
			-1);
type_missmatch:
	return get_error(
		s->h.file,
		s->h.line,
		XLE_SEMANTICS_TYPE_MISSMATCH,
		l_string(std_cm,"Mercator(after)"),
		List(n_get_string("type_missmatch"),
			n_get_string(e_param),
			-1));
}


XL_SEXP *
xl_Mercator(XLISP_ENV * env,
	XL_SEXP * s,
	XLISP_ENV * a,
	XL_SYM_FIELD * sf)
{
L_CHAR * proc;
	proc = get_sf_attribute(sf,l_string(std_cm,"proc"));
	if ( proc == 0 )
		proc = l_string(std_cm,"prev");
	if ( l_strcmp(proc,l_string(std_cm,"prev")) == 0 )
		return xl_MercatorPrev(env,s);
	else if ( l_strcmp(proc,l_string(std_cm,"after")) == 0 )
		return xl_MercatorAfter(env,s,sf);
	else	return get_error(
			s->h.file,
			s->h.line,
			XLE_PROTO_INV_PARAM,
			l_string(std_cm,"Mercator"),
			List(n_get_string("proc attribute error"),
				-1));
}
