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

typedef struct rs_t {
	Q_HEADER	h;
	XLISP_ENV *	env;
	int		id;
	L_CHAR *	path;
	L_CHAR *	agent;
	L_CHAR *	lt;
	L_CHAR *	ccmd;
	URL		u;
	XL_SEXP *	s;
	XL_FILE *	file;
	int		line;

	XL_SEXP *	ret;
	D_SEXP *	d;
} RS_T;

extern SEM xli_lock;
XL_SEXP * xl_RemoteSession();
SYS_QUEUE rs_que1,rs_que2;
int rs_t_count;

void gc_rs_que();
void gc_get_rs_que();
void rs_task1();
void rs_task2();

void gc_gb_sexp();
void gc_gb_file();
void gc_gblisp_env();
void gc_d_sexp();

void
init_RemoteSession(XLISP_ENV * env)
{
	set_env(env,l_string(std_cm,"RemoteSession"),
		get_func_prim(xl_RemoteSession,FO_NORMAL,0,2,-1));

	memset(&rs_que1,0,sizeof(SYS_QUEUE));
	rs_que1.flags = QF_STACK;
	rs_que1.gc_func = gc_rs_que;
	rs_que1.gc_get = gc_get_rs_que;
	rs_que1.key_func = rs_task1;
	rs_que1.pri = PRI_FETCH;
	setup_queue(&rs_que1);

	memset(&rs_que2,0,sizeof(SYS_QUEUE));
	rs_que2.flags = QF_STACK;
	rs_que2.gc_func = gc_rs_que;
	rs_que2.gc_get = gc_get_rs_que;
	rs_que2.key_func = rs_task2;
	rs_que2.pri = PRI_FETCH;
	setup_queue(&rs_que2);

}


void
gc_rs_que(RS_T * n)
{
	gc_gb_sexp(n->s);
	gc_d_sexp(n->d);
	gc_gb_sexp(n->ret);
	gc_gblisp_env(n->env);
	gc_gb_file(n->file);
}

void
gc_get_rs_que(RS_T * n)
{
	lock_mem();
	gc_set_nl(n->s,gc_gb_sexp);
	gc_set_nl(n->ret,gc_gb_sexp);
	gc_set_nl(n->d,gc_d_sexp);
	gc_set_nl(n->env,gc_gblisp_env);
	gc_set_nl(n->file,gc_gb_file);
	unlock_mem();
}


void
_wait_rs_queue()
{
	for ( ; rs_t_count ; ) {
		sleep_task((int)&rs_t_count,xli_lock);
		lock_task(xli_lock);
	}
}

void
wait_rs_queue()
{
	lock_task(xli_lock);
	_wait_rs_queue();
	unlock_task(xli_lock,"wait_rs_queue");
}

void
rs_count(int c)
{
	lock_task(xli_lock);
	rs_t_count += c;
	wakeup_task((int)&rs_t_count);
	unlock_task(xli_lock,"wait_rs_queue");
}

void
rs_task1(TKEY d)
{
RS_T * n;
SYS_QUEUE * que;
L_CHAR * key;
XL_INTERPRETER * xli;

	que = (SYS_QUEUE *)GET_TKEY(d);
	key = touch_qkey(que);
	if ( key == 0 )
		return;

	xli = new_xl_interpreter();
	xli->a_type = XLA_SELF;
	setup_i(xli);
	for ( ; ; ) {
		gc_push(0,0,"rs_task1");

		n = delete_queue(que,sq_key_cond,key,0);
		if ( n == 0 ) {
			gc_pop(0,0);
			break;
		}
		n->ret = remote_session(
				n->env,
				n->id,
				&n->u,
				n->agent,
				n->lt,
				n->ccmd,
				n->s,
				n->file,
				n->line,
				0);

		insert_queue(&rs_que2,n,1);

		gc_pop(0,0);
	}

	release_qkey(que,key);
	d_f_ree(key);

	close_self_interpreter();


}

void
rs_task2(TKEY d)
{
RS_T * n;
SYS_QUEUE * que;
L_CHAR * key;
XL_INTERPRETER * xli;
XL_SEXP * s;


	que = (SYS_QUEUE *)GET_TKEY(d);
	key = touch_qkey(que);
	if ( key == 0 )
		return;

	xli = new_xl_interpreter();
	xli->a_type = XLA_SELF;
	setup_i(xli);

	for ( ; ; ) {
		gc_push(0,0,"rs_task2");
		n = delete_queue(que,sq_key_cond,key,0);
		if ( n == 0 ) {
			gc_pop(0,0);
			break;
		}
		for ( s = n->ret ; get_type(s) == XLT_PAIR ; s = cdr(s) );
		set_d_sexp(n->d,n->ret);

		free_url(&n->u);
		if ( n->lt )
			d_f_ree(n->lt);
		if ( n->ccmd )
			d_f_ree(n->ccmd);
		if ( n->agent )
			d_f_ree(n->agent);
		d_f_ree(n->h.key);
		d_f_ree(n);

		gc_pop(0,0);

		rs_count(-1);
	}

	release_qkey(que,key);
	d_f_ree(key);

	close_self_interpreter();


}



XL_SEXP *
xl_RemoteSession(XLISP_ENV * env,XL_SEXP * s,
	XLISP_ENV * a,
	XL_SYM_FIELD * sf)
{
XL_SEXP * id;
XL_SEXP * path, * agent,* lt,* ccmd;
int _id;
L_CHAR * _path;
L_CHAR * _agent;
L_CHAR * _lt;
L_CHAR * _ccmd;
XL_SEXP * sym, * cmd,* ret,* s_i;
URL u;
L_CHAR * type;
RS_T	* n;

	s_i = s;
	id = eval(env,get_el(s,1));
	switch ( get_type(id) ) {
	case XLT_ERROR:
		return id;
	case XLT_INTEGER:
		_id = id->integer.data;
		break;
	default:
		goto type_missmatch;
	}
	_path = 0;
	_agent = 0;
	_lt = 0;
	_ccmd = 0;
	zero_url(&u);
	s = cdr(cdr(s));
	for ( ; get_type(s) ; s = cdr(s) ) {
		cmd = car(s);
		switch ( get_type(cmd) ) {
		case XLT_ERROR:
			return cmd;
		case XLT_PAIR:
			break;
		default:
			goto next;
		}
		sym = car(cmd);
		switch ( get_type(sym) ) {
		case XLT_ERROR:
			return sym;
		case XLT_SYMBOL:
			break;
		default:
			goto next;
		}
		if ( l_strcmp(sym->symbol.data,l_string(std_cm,"URL"))
				== 0 ) {
			if ( list_length(cmd) != 2 )
				goto length_error;
			path = eval(env,get_el(cmd,1));
			switch ( get_type(path) ) {
			case XLT_ERROR:
				return path;
			case XLT_STRING:
				_path = path->string.data;
				break;
			default:
				goto type_missmatch;
			}
		}
		else if ( l_strcmp(sym->symbol.data,l_string(std_cm,"Agent"))
				== 0 ) {
			if ( list_length(cmd) != 2 )
				goto length_error;
			agent = eval(env,get_el(cmd,1));
			switch ( get_type(agent) ) {
			case XLT_ERROR:
				return agent;
			case XLT_STRING:
				_agent = 
				  ll_copy_str(agent->string.data);
				break;
			default:
				goto type_missmatch;
			}
		}
		else if ( l_strcmp(sym->symbol.data,
			l_string(std_cm,"LoginMode")) == 0 ) {
			if ( list_length(cmd) != 2 )
				goto length_error;
			lt = eval(env,get_el(cmd,1));
			switch ( get_type(lt) ) {
			case XLT_ERROR:
				return lt;
			case XLT_STRING:
				_lt = ll_copy_str(lt->string.data);
				break;
			default:
				goto type_missmatch;
			}
		}
		else if ( l_strcmp(sym->symbol.data,
			l_string(std_cm,"Command")) == 0 ) {
			if ( list_length(cmd) != 2 )
				goto length_error;
			ccmd = eval(env,get_el(cmd,1));
			switch ( get_type(ccmd) ) {
			case XLT_ERROR:
				return ccmd;
			case XLT_STRING:
				_ccmd = 
				  ll_copy_str(ccmd->string.data);
				break;
			default:
				goto type_missmatch;
			}
		}
		else goto next;
	}
next:
	if ( _path )
		get_url2(&u,_path);
	else	zero_url(&u);
	if ( _ccmd == 0 )
		_ccmd = nl_copy_str(std_cm,"Get");
	type = get_sf_attribute(sf,l_string(std_cm,"type"));
	if ( type )
		s = gb_quote_trace(env,s,type);
/*
	ret = remote_session(env,_id,&u,_agent,_lt,_ccmd,s,
			     s_i->h.file,s_i->h.line,0);
*/
	n = d_alloc(sizeof(*n));
	memset(n,0,sizeof(*n));

	n->h.key = get_server_key(&u);
	n->env = env;
	n->id = _id;
	n->u = u;
	n->agent = _agent;
	n->lt = _lt;
	n->ccmd = _ccmd;
	n->s = s;
	n->file = s_i->h.file;
	n->line = s_i->h.line; 
	ret = new_d_sexp(&n->d);

	insert_queue(&rs_que1,n,1);

	rs_count(1);

	return ret;
type_missmatch:
	ret = get_error(
		s->h.file,
		s->h.line,
		XLE_SEMANTICS_TYPE_MISSMATCH,
		l_string(std_cm,"RemoteSession"),
		n_get_string("type missmatch"));
	goto end;
length_error:
	ret = get_error(
		s->h.file,
		s->h.line,
		XLE_SEMANTICS_INV_PARAM_LENGTH,
		l_string(std_cm,"RemoteSession"),
		n_get_string("invalid parameter length"));
end:
	free_url(&u);
	if ( _lt )
		d_f_ree(_lt);
	if ( _ccmd )
		d_f_ree(_ccmd);
	if ( _agent )
		d_f_ree(_agent);
	return ret;
}


