/**********************************************************************
 
	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	"gbacrp.h"
#include	"lock_level.h"

XL_SEXP * 
remote_trigger(int ses,URL * u)
{
L_CHAR * f;
XL_SEXP * ret;
	f = get_url_filepath(u);
	ret = remote_session(
		gblisp_top_env0,
		ses,
		u,
		l_string(std_cm,"gbstd"),
		l_string(std_cm,"user"),
		l_string(std_cm,"Get"),
		List(
			List(n_get_symbol("MPTrigger"),
				get_string(f),
				-1),
			-1),
		0,0,0);
	d_f_ree(f);
	return ret;
}

XL_SEXP *
local_trigger(int ses,URL * u)
{
L_CHAR * f;
XL_SEXP * ret;
	u->port = get_mp_port();
	if ( u->port == 0 ) {
		ret = 0;
		goto end;
	}
	f = get_url_filepath(u);
	remote_session(
		gblisp_top_env0,
		ses,
		u,
		l_string(std_cm,"gbmp"),
		l_string(std_cm,"user"),
		l_string(std_cm,"Get"),
		List(
			List(n_get_symbol("MPTrigger"),
				get_string(f),
				-1),
			-1),
		0,0,0);
	d_f_ree(f);
end:
	return ret;
}


void
map_trigger(int ses,MAP_PATH * mp,int len)
{
int i;
URL u;
int ip;
int port;
XL_SEXP * ret;
	ip = get_localhostip();
	port = get_my_port();

	for ( i = 0 ; i < len ; i ++ ) {
		get_url2(&u,mp->map);
		if ( cmp_site(u.server,0,u.port,
				0,ip,port) )
			ret = remote_trigger(ses,&u);
		else	ret = local_trigger(ses,&u);
		free_url(&u);
	}
}

int
get_MP_entry(ACRP_ENTRY * ep,L_CHAR * url,int * target)
{
MAP_PATH_TABLE * t, * prev_t, * t0;
int i,j;
int len;

printf("*\n");
	t0 = t = get_mpi_cp_mpt(0,url,0);
	if ( t == 0 )
		return -1;
	len = target[0];
	prev_t = 0;
	for ( i = 1 ; i < len+1 ; ) {
printf("target %i %i %i\n",i,target[i],t->id);
		if ( t->id == target[i] ) {
			prev_t = t;
			t = t->next;
			i ++;
		}
		else {
			prev_t = t;
			*ep = t->ent[target[i]];
			ep->pri = 0;
			for ( j = 0 ; j < ACRP_DIR_NOS ; j ++ ) {
				if ( ep->dir[j].hops < 0 )
					break;
				ep->dir[j].map
				    = ll_copy_str(ep->dir[j].map);
				ep->dir[j].crd
				    = ll_copy_str(ep->dir[j].crd);
			}
			free_mpt(t0);
			if ( ep->dir[0].hops < 0 )
				return -1;
			return 0;
		}
	}
	*ep = prev_t->ent[target[len]];
	ep->pri = 0;
	for ( j = 0 ; j < ACRP_DIR_NOS ; j ++ ) {
		if ( ep->dir[j].hops < 0 )
			break;
		ep->dir[j].map = 0;
		ep->dir[j].crd = 0;
	}
	free_mpt(t0);
	return 1;
}

int
MP_routing(
	int ses,
	int * len,MAP_PATH * mp,int ttl,L_CHAR * url,int * target,int type,
	int option)
{
MAP_PATH * mp1;
int mp_ptr;
URL u;
int ip,port;

	ip = get_localhostip();
	port = get_my_port();
	mp_ptr = 0;
	for ( ; mp_ptr < ttl ; ) {
		get_url2(&u,url);
		if ( cmp_site(u.server,0,u.port,
				0,ip,port) ) {
			free_url(&u);
			*len = mp_ptr;
			if ( option )
				map_trigger(ses,mp,mp_ptr);
			return MPE_OTHER_SITE;
		}
		free_url(&u);
		mp1 = &mp[mp_ptr];
		switch ( get_MP_entry(&mp1->ent,url,target) ) {
		case -1:
			for ( ; ; ) {
				if ( mp_ptr == 0 )
					return MPE_NO_ROUTE;
				mp_ptr --;
				mp1 = &mp[mp_ptr];
				mp1->dir ++;
				if ( mp1->dir >= ACRP_DIR_NOS )
					goto next;
				if ( mp1->ent.dir[mp1->dir].hops < 0 )
					goto next;
				break;
			next:
				if ( type == MPR_FAST )
					return MPE_NO_ROUTE;
			}
			mp1->map = mp1->ent.dir[mp1->dir].map;
			mp1->crd = mp1->ent.dir[mp1->dir].crd;
			url = mp1->crd;
			mp_ptr ++;
			break;
		case 0:
			mp1->map = mp1->ent.dir[0].map;
			mp1->crd = mp1->ent.dir[0].crd;
			url = mp1->crd;
			mp1->dir = 0;
			mp_ptr ++;
			break;
		case 1:
			*len = mp_ptr;
			if ( option )
				map_trigger(ses,mp,mp_ptr);
			return MPE_FIT;
		}
	}
	*len = ttl;
	return MPE_OVER_TTL;
}

int
remote_routing(
	int ses,
	MAP_PATH * mp,int ttl,L_CHAR * url,int * target,
	int mode,
	int ret_mode,
	int option)
{
XL_SEXP * ret;
URL u;
char buf[20];
XL_SEXP * t;
int mp_ptr;
XL_SEXP * gt;

XL_SEXP * cmd;

	gc_push(0,0,"MProting_over_site");
	gt = n_get_symbol("MPRouting");
	if ( mode ) {
		set_attribute(gt,
			l_string(std_cm,"mode"),
			l_string(std_cm,"fast"));
	}
	else {
		set_attribute(gt,
			l_string(std_cm,"mode"),
			l_string(std_cm,"best"));
	}
	if ( ret_mode ) {
		set_attribute(gt,
			l_string(std_cm,"return"),
			l_string(std_cm,"target"));
	}
	else {
		set_attribute(gt,
			l_string(std_cm,"return"),
			l_string(std_cm,"route"));
	}
	if ( option ) {
		set_attribute(gt,
			l_string(std_cm,"trigger"),
			l_string(std_cm,"on"));
	}
	else {
		set_attribute(gt,
			l_string(std_cm,"trigger"),
			l_string(std_cm,"off"));
	}
	sprintf(buf,"%i",ttl);
	set_attribute(gt,
		l_string(std_cm,"ttl"),
		l_string(std_cm,buf));
	get_url2(&u,url);
	ret = remote_session(
		gblisp_top_env0,
		ses,
		&u,
		l_string(std_cm,"gbstd"),
		l_string(std_cm,"user"),
		l_string(std_cm,"Get"),
		cmd = List(List(gt,
			get_string(url),
			List(	n_get_symbol("quote"),
				cid2list(target),
				-1),
			-1),
			-1),
		0,0,0);
printf("remote_routing ");
print_sexp(s_stdout,cmd,0);
printf("-->");
print_sexp(s_stdout,ret,0);
printf("\n");
	if ( get_type(ret) == XLT_ERROR ) {
		gc_pop(0,0);
		return MPE_NO_ROUTE;
	}
	if ( ret_mode == MPR_TARGET ) {
		t = ret;
		if ( get_type(t) != XLT_STRING )
			return MPE_UNKNOWN;
		mp->map = 0;
		mp->crd = ll_copy_str(t->string.data);
		gc_pop(0,0);
		return 1;
	}
	else {
		for ( mp_ptr = 0 ; mp_ptr < ttl ; mp_ptr ++ ) {
			if ( get_type(ret) != XLT_PAIR )
				break;
			t = car(ret);
			if ( get_type(t) != XLT_STRING )
				return MPE_UNKNOWN;
			mp[mp_ptr].map = ll_copy_str(t->string.data);
			ret = cdr(ret);
			t = car(ret);
			if ( get_type(t) != XLT_STRING )
				return MPE_UNKNOWN;
			mp[mp_ptr].crd = ll_copy_str(t->string.data);
			ret = cdr(ret);
		}
		gc_pop(0,0);
		return mp_ptr;
	}
}

void
free_mp_entry(MAP_PATH * e)
{
	free_mpt_entry(&e->ent);
	e->map = e->crd = 0;
	e->dir = -1;
}

void
free_mp(MAP_PATH * mp,int len)
{
int i;
	for ( i = 0 ; i < len ; i ++ )
		free_mp_entry(&mp[i]);
}

void
reset_mpt_entry_dir(ACRP_DIR * d)
{
	d->hops = -1;
	d->crd = d->map = 0;
	d->flags = 0;
}

void
reset_mpt_entry(ACRP_ENTRY * e)
{
int i;
	e->pri = 0;
	e->sum = 0;
	for ( i = 0 ; i < ACRP_DIR_NOS ; i ++ )
		reset_mpt_entry_dir(&e->dir[i]);
}

void
reset_mp_entry(MAP_PATH * e)
{
	reset_mpt_entry(&e->ent);
	e->map = e->crd = 0;
	e->dir = -1;
}

void
reset_mp(MAP_PATH * mp,int len)
{
int i;
	for ( i = 0 ; i < len ; i ++ )
		reset_mp_entry(&mp[i]);
}

void
print_mp(MAP_PATH * p,int size)
{
int i;
MAP_PATH * p1;
	printf("(");
	for ( i = 0 ; i < size ; i ++ ) {
		p1 = &p[i];
		printf("(%i %s %s)",
			p1->dir,
			n_string(std_cm,p1->map),
			n_string(std_cm,p1->crd));
	}
	printf(")");
}

int
MP_routing_over_site(
	int * len,
	MAP_PATH * mp,int ttl,L_CHAR * url,int * target,int mode,
	int ret_mode,int option)
{
int mp_ptr,ptr;
MAP_PATH * mp1;
ACRP_ENTRY ebuf;
XL_SEXP * gt;
int ret;
int ses;
int ses_ret;

printf("MP_routing_over_site\n");
	ses = open_session(SEST_OPTIMIZE);
	ret = MP_routing(ses,&mp_ptr,mp,ttl,url,target,mode,option);
	switch ( ret ) {
	case MPE_FIT:

printf("*a fit\n");

		*len = mp_ptr;
		close_session(ses);
		return MPE_FIT;
	case MPE_OTHER_SITE:

printf("*a other site\n");

		break;
	default:

printf("*a other error %i\n",ret);
		close_session(ses);
		return ret;
	}
	for ( ; ; ) {
		for ( ; ; ) {
			if ( mp_ptr == 0 )
				ret = remote_routing(ses,&mp[mp_ptr],
					ttl-mp_ptr,
					url,target,mode,ret_mode,
					option);
			else	ret = remote_routing(ses,&mp[mp_ptr],
					ttl-mp_ptr,
					mp[mp_ptr-1].crd,target,mode,ret_mode,
					option);
printf("ret = %i\n",ret);
			if ( ret >= 0 ) {
				*len = ses_ret = mp_ptr + ret;
				ses_ret = MPE_FIT;
				goto end;
			}
			if ( mp_ptr == 0 ) {
				ses_ret = MPE_NO_ROUTE;
				goto end;
			}
			mp[mp_ptr-1].dir ++;
			if ( mp[mp_ptr-1].dir >= ACRP_DIR_NOS ) {
				if ( mode == MPR_FAST ) {
					ses_ret = MPE_NO_ROUTE;
printf("no route 1\n");
					goto end;
				}
				mp_ptr --;
				break;
			}
			if ( mp[mp_ptr-1].ent.dir[mp[mp_ptr-1].dir].hops < 0 )
						{
				if ( mode == MPR_FAST ) {
					ses_ret = MPE_NO_ROUTE;
printf("no route 2\n");
					goto end;
				}
				mp_ptr --;
				break;
			}
			mp[mp_ptr-1].map =
				mp[mp_ptr-1].ent.dir[mp[mp_ptr-1].dir].map;
			mp[mp_ptr-1].crd =
				mp[mp_ptr-1].ent.dir[mp[mp_ptr-1].dir].crd;
		}
		free_mp_entry(&mp[mp_ptr]);
		if ( mp_ptr == 0 ) {
			ses_ret = MPE_NO_ROUTE;
printf("no route 3\n");
			goto end;
		}
		ret = MP_routing(ses,&ptr,&mp[mp_ptr],ttl-mp_ptr,
			mp[mp_ptr-1].crd,target,mode,option);
		mp_ptr += ptr;
		switch ( ret ) {
		case MPE_FIT:
			*len = mp_ptr;
			ses_ret = MPE_FIT;
printf("** b fit\n");
			goto end;
		case MPE_OTHER_SITE:
			break;
		default:
printf("some error\n");
			ses_ret = ret;
			goto end;
		}
	}
end:
	close_session(ses);
	return ses_ret;
}
