/**********************************************************************
 
	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"



MP_T *
new_mp_t(int ttl,L_CHAR * start,L_CHAR * target)
{
MP_T * ret;
	ret = new_queue_node(sizeof(*ret),346);
	memset(ret,0,sizeof(*ret));
	ret->mp_tbl = d_alloc(sizeof(MAP_PATH)*ttl,346);
	ret->ttl = ttl;
	reset_mp(ret->mp_tbl,ttl);
	ret->start = ll_copy_str(start,346);
	if ( target )
		ret->target = ll_copy_str(target,46);
	return ret;
}

void
free_mp_t(MP_T * m)
{
	if ( m->h.key )
		d_f_ree(m->h.key);
	free_mp(m->mp_tbl,m->ttl);
	if ( m->start )
		d_f_ree(m->start);
	if ( m->target )
		d_f_ree(m->target);
	if ( m->target_cid )
		d_f_ree(m->target_cid);
	d_f_ree(m);
}


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;
XL_SEXP * gb_MPTrigger();
	u->port = get_mp_port();
	f = get_url_filepath(u);
	ret = gb_MPTrigger(gblisp_top_env0,
		List(n_get_symbol("MPTrigger"),
			get_string(f),
			-1),
		0,0);
	d_f_ree(f);
	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,1252);
		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,addr_len;

	t0 = t = get_mpi_cp_mpt(0,url,0);
	if ( t == 0 )
		return -1;
	addr_len = addr_length(t);
	len = target[0];
	if ( len < addr_len ) {
		prev_t = 0;
		for ( i = 0 ; t ; i < addr_len - len ) {
			if ( t->id == 0 ) {
				prev_t = t;
				t = t->next;
			}
			else {
				prev_t = t;
				*ep = t->ent[0];
				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,1385);
					ep->dir[j].crd
					  = ll_copy_str(ep->dir[j].crd,1384);
				}
				free_mpt(t0);
				if ( ep->dir[0].hops < 0 )
					return -1;
				return 0;
			}
		}
		i = 1;
	}
	else if ( len > addr_len ) {
	int _len;
		_len = len;
		for ( i = 1 ; _len > addr_len ; i ++ , _len -- ) {
			if ( target[i] == 0 )
				continue;
			return -1;
		}
		prev_t = 0;
	}
	else {
		prev_t = 0;
		i = 1;
	}
	for ( ; i < len+1 ; ) {
		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,1385);
				ep->dir[j].crd
				  = ll_copy_str(ep->dir[j].crd,1384);
			}
			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
			= ll_copy_str(ep->dir[j].map,1383);
		ep->dir[j].crd
			= ll_copy_str(ep->dir[j].crd,1382);
	}
	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,1251);
		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;
char buf[20];
XL_SEXP * t;
int mp_ptr;
XL_SEXP * gt;
XL_SEXP * local_ses;

	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));
	local_ses = local_query(gblisp_top_env0,
		l_string(std_cm,"<OpenSession/>"),
		0);
	if ( get_type(local_ses) == XLT_ERROR ) {
		gc_pop(0,0);
		return MPE_NO_ROUTE;
	}
	ret = local_query(gblisp_top_env0,
		0,
		List(n_get_symbol("RemoteSession"),
			local_ses,
			List(n_get_symbol("Agent"),
				n_get_string("gbstd"),
				-1),
			List(n_get_symbol("URL"),
				get_string(url),
				-1),
			List(gt,
				get_string(url),
				List(	n_get_symbol("quote"),
					cid2list(target),
					-1),
				-1),
			-1));
	local_query(gblisp_top_env0,
		0,
		List(n_get_symbol("CloseSession"),
			local_ses,
			-1));
	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,1381);
		gc_pop(0,0);
		return 1;
	}
	else {
		for ( mp_ptr = 0 ; mp_ptr < ttl ; mp_ptr ++ ) {
			reset_mp_entry(&mp[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,1380);
			ret = cdr(ret);
			t = car(ret);
			if ( get_type(t) != XLT_STRING ) {
				d_f_ree(mp[mp_ptr].map);
				mp[mp_ptr].map = 0;
				return MPE_UNKNOWN;
			}
			mp[mp_ptr].crd = ll_copy_str(t->string.data,1379);
			ret = cdr(ret);
		}
		gc_pop(0,0);
		return mp_ptr;
	}
}

void
free_mp_entry(MAP_PATH * e)
{
ACRP_DIR * d;
int i;
	for ( i = 0 ; i < ACRP_DIR_NOS ; i ++ ) {
		d = &e->ent.dir[i];
		if ( d->map == e->map )
			e->map = 0;
		if ( d->crd == e->crd )
			e->crd = 0;
	}
	free_mpt_entry(&e->ent);
	if ( e->map )
		d_f_ree(e->map);
	if ( e->crd )
		d_f_ree(e->crd);
	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");
*/
set_cpu_msg(720010);
	ses = open_session(SEST_OPTIMIZE);
	ret = MP_routing(ses,&mp_ptr,mp,ttl,url,target,mode,option);
set_cpu_msg(720011);
	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;
	}
set_cpu_msg(720012);
	for ( ; ; ) {
		for ( ; ; ) {
set_cpu_msg(720013);
			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 ++;
set_cpu_msg(720014);
			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;
set_cpu_msg(720015);
		}
set_cpu_msg(720016);
		free_mp_entry(&mp[mp_ptr]);
		if ( mp_ptr == 0 ) {
			ses_ret = MPE_NO_ROUTE;
printf("no route 3\n");
			goto end;
		}
set_cpu_msg(720017);
		ret = MP_routing(ses,&ptr,&mp[mp_ptr],ttl-mp_ptr,
			mp[mp_ptr-1].crd,target,mode,option);
		mp_ptr += ptr;
set_cpu_msg(720018);
		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;
		}
set_cpu_msg(720019);
	}
end:
set_cpu_msg(720020);
	close_session(ses);
	return ses_ret;
}
