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

**********************************************************************/


#define STREAM_LIB

#include	<stdlib.h>
#include	"memory_debug.h"
#include	"memory_routine.h"
#include	"task.h"
#include	"xlerror.h"
#include	"xl.h"

extern SEM xli_lock;

void gc_gblisp_env(XLISP_ENV *);


int
cmp_round(int a,int b)
{
unsigned int c,d;
int a_s;
	a_s = a - 0x40000000;
	if ( a_s < 0 ) {
		if ( a < b )
			return -1;
		if ( a > b )
			return 1;
		return 0;
	}
	else {
		c = a;
		d = b;
		if ( c < d )
			return -1;
		if ( c > d )
			return 1;
		return 0;
	}
}


void
delete_result_queue(XL_INTERPRETER * xli,XL_RESULT * r)
{
XL_RESULT ** rp;
	if ( xli == 0 )
		return;
	for ( rp = &xli->result_head ; *rp ; rp = &(*rp)->next )
		if ( *rp == r ) {
			*rp = r->next;
			r->out_interpreter = 0;
			if ( xli->result_head == 0 )
				wakeup_task((int)&xli->result_head);
			return;
		}
}


void
delete_result_list(XL_RESULT * resp)
{
XL_INTERPRETER * xli;
XL_RESULT ** rpp;
	xli = resp->inp_interpreter;
	if ( xli == 0 )
		return;
	for ( rpp = &xli->result_list ; *rpp ;
			rpp = &(*rpp)->my_xli_result_next ) {
		if ( (*rpp) != resp )
			continue;
		*rpp = resp->my_xli_result_next;
		break;
	}
	resp->inp_interpreter = 0;
}

int
return_result(XL_INTERPRETER * xli,XL_SEXP * ret)
{
XL_RESULT * r;
XL_SEXP * seq;
int seq_no;
int ret_no;


	ret_no = -1;
	seq = get_el(ret,1);
	if ( get_type(seq) != XLT_INTEGER )
		return -1;
	seq_no = seq->integer.data;

	get_type(get_el(ret,2));


	lock_task(xli_lock);

	if ( xli->result_head == 0 )
		goto err;
	for ( r = xli->result_head ; r ; r = r->next ) {
		if ( cmp_round(r->seq_from,seq_no) <= 0 &&
				cmp_round(r->seq_to,seq_no) > 0 )
			goto ok;
	}
set_cpu_msg(80000);
	goto err;
ok:
set_cpu_msg(80000+seq_no);
/*
ss_printf("res(%i) %x %i\n",getpid(),r,seq_no);
*/

	r->ret = ret;
	r->flags |= XRF_RETURN;
	if ( r->wup_key )
		wakeup_task(r->wup_key);

	delete_result_queue(xli,r);

	wakeup_task((int)r);
	ret_no = 0;
err:
	unlock_task(xli_lock,"return_result");
	return ret_no;
}

XL_SEXP * remote_delay_func(XL_SEXP *);


void
gc_remote_delay(XL_RESULT * p)
{
void gc_gb_sexp();
void gc_gb_file();
	if ( p == 0 )
		return;
	if ( TEST_AND_SET(p) )
		return;
	gc_gb_sexp(p->ret);
	gc_gb_file(p->file);
	gc_gblisp_env(p->cur_env);

	if ( p->s_id ) {
		gc_gblisp_env(p->s_env);
		gcv_url(&p->s_url);
		gc_text((char*)p->s_agent);
		gc_text((char*)p->s_login_mode);
		gc_text((char*)p->s_center_cmd);
		gc_gb_sexp(p->s_cmd);
		gc_gb_file(p->s_file);
	}

gc_gb_sexp(p->send);

}


int
_check_remote_delay(XL_RESULT * resp,int key)
{
XL_INTERPRETER * my_xli;
	if ( key )
		resp->wup_key = key;
	if ( resp->flags & XRF_RETURN )
		return CDT_READY;
	my_xli = resp->inp_interpreter;
	if ( my_xli == 0 )
		return CDT_WAIT;
	if ( my_xli->flags & (XIF_BREAK|XIF_CANCEL) )
		return CDT_READY;
	return CDT_WAIT;
}

int
check_remote_delay(XL_RESULT * resp,int key)
{
int ret;

	lock_task(xli_lock);
	ret = _check_remote_delay(resp,key);
	unlock_task(xli_lock,"check_remote_delay");
	return ret;
}

XL_SEXP *
_remote_query(XL_INTERPRETER * xli,
	XLISP_ENV * env,L_CHAR * cmd_str,XL_SEXP * cmd_sexp)
{
XL_SEXP * ret;
int seq_no;
XL_RESULT * resp;
XL_SEXP * r;
XL_INTERPRETER * my_xli;
void gc_gb_sexp();
CODE_METHOD * cm1;


	my_xli = _get_my_xli();
	if ( my_xli == 0 )
		er_panic("_remote_query");


	resp = mmalloc(sizeof(*resp),gc_remote_delay);


	resp->h.func = remote_delay_func;
	resp->h.gc_func = gc_remote_delay;
	resp->h.check_func = check_remote_delay;
	resp->s_agent = 0;
	resp->wup_key = 0;

	resp->ret = 0;
	resp->next = 0;
	resp->flags = 0;
	if ( cmd_str ) {

		resp->line = 0;
		resp->file = 0;
	}
	else {

		resp->line = cmd_sexp->h.line;
		resp->file = cmd_sexp->h.file;
	}
	resp->remote_seq = resp->line;

	resp->cur_env = env;
	if ( xli == 0 ) {
		ret = get_error(
			resp->file,
			resp->line,
			XLE_PROTO_ACCESS_STREAM,
			l_string(std_cm,"Remote"),
			n_get_string("invalid id"));
		return ret;
	}
	resp->inp_interpreter = my_xli;
	resp->out_interpreter = xli;
	if ( xli->mode != XIM_RUN ) {
		ret = get_error(
			resp->file,
			resp->line,
			XLE_PROTO_ACCESS_STREAM,
			l_string(std_cm,"Remote"),
			n_get_string("invalid id"));
		return ret;
	}

	_lock_xli_out(xli);

	if ( xli->mode != XIM_RUN ) {
		ret = get_error(
			resp->file,
			resp->line,
			XLE_PROTO_ACCESS_STREAM,
			l_string(std_cm,"Remote"),
			n_get_string("invalid id"));

		_unlock_xli_out(xli);
		return ret;
	}
	if ( xli->out == 0 ) {
		ret = get_error(
			resp->file,
			resp->line,
			XLE_PROTO_ACCESS_STREAM,
			l_string(std_cm,"Remote"),
			n_get_string("output stream error"));

		_unlock_xli_out(xli);
		return ret;
	}

	resp->seq_from = xli->out->h.cr_cnt;
	if ( xli->flags & XIF_CODE_SYNC ) {
		cm1 = s_get_cm(xli->out);
		if ( cm1 )
			s_printf(xli->out,"&cx%x;",cm1->lccode);
		xli->flags &= ~XIF_CODE_SYNC;
	}
	if ( cmd_str )
		s_printf(xli->out,"%ls\n",cmd_str);
	else {
/*
ss_printf("seq %i ",resp->seq_from);
print_sexp(s_stdout,cmd_sexp,0);
ss_printf("\n");
*/
		print_sexp(xli->out,cmd_sexp,
			xli->ps_flags&(~PF_MULTI_ROOT));
		s_printf(xli->out,"\n");
	}

	resp->seq_to = xli->out->h.cr_cnt;
	s_flush(xli->out);
	_unlock_xli_out(xli);
	resp->next = xli->result_head;
	xli->result_head = resp;



	resp->my_xli_result_next = my_xli->result_list;
	my_xli->result_list = resp;

	return init_delay_func(&resp->h);
}


XL_SEXP *
_wait_result_inp(XL_RESULT * resp)
{
XL_INTERPRETER * my_xli,* xli;
XL_SEXP *ret;
void gc_gb_sexp();


	my_xli = resp->inp_interpreter;
	xli = resp->out_interpreter;
	goto loop;

retry:

	resp->my_xli_result_next = my_xli->result_list;
	my_xli->result_list = resp;
	resp->inp_interpreter = my_xli;

loop:

	if ( (resp->flags & (XRF_RETURN|XRF_INPUT)) == 0 &&
			(my_xli == 0 || 
			(my_xli->flags & (XIF_BREAK|XIF_CANCEL)) == 0) ) {
set_cpu_msg(98000+resp->seq_from);

set_t_msg(2010);
		sleep_task((int)resp,xli_lock);

		lock_task(xli_lock);
set_t_msg(2011);


set_cpu_msg(98149);
	}
set_cpu_msg(98150);

	my_xli = resp->inp_interpreter;
	xli = resp->out_interpreter; 

	delete_result_list(resp);
set_t_msg(2012);

	if ( my_xli && my_xli->flags & XIF_BREAK ) {
		delete_result_queue(xli,resp);

		return get_error(
			resp->file,
			resp->line,
			XLE_SYSTEM_EXIT,
			l_string(std_cm,"break"),
			0);
	}
set_cpu_msg(98151);

	if ( my_xli && my_xli->flags & XIF_CANCEL ) {
		delete_result_queue(xli,resp);


		return get_error(
			resp->file,
			resp->line,
			XLE_SYSTEM_INTERRUPT,
			l_string(std_cm,"cancel"),
			0);
	}
set_cpu_msg(98152);

	if ( resp->flags & XRF_RETURN ) {
		ret = get_el(resp->ret,2);
		if ( get_type(ret) == XLT_ERROR ){
			ret = get_error(
				resp->file,
				resp->line,
				ret->err.code,
				l_string(std_cm,"remote call"),
				List(
					get_string(ret->err.site),
					get_string(ret->err.filename),
					get_integer(ret->err.line,0),
					get_string(ret->err.func),
					ret->err.data,
					-1));
		}
		else {
			gc_set(ret,gc_gb_sexp);
		}

		delete_result_queue(xli,resp);


		return ret;
	}
set_cpu_msg(98153);

	if ( resp->flags & XRF_INPUT ) {
		resp->flags &= ~XRF_INPUT;
		if ( my_xli != _get_my_xli() )
			goto retry;
		if ( xli == 0 )
			goto retry;
	fifo_retry:
set_cpu_msg(98154);
		unlock_task(xli_lock,"_remote_query");
		relay_loop(xli,XFF_NONBLOCK);
		lock_task(xli_lock);
		if ( check_fifo(xli) )
			goto fifo_retry;
	}
set_cpu_msg(98155);

	goto retry;
}

XL_SEXP *
remote_delay_func(XL_SEXP * s)
{
XL_RESULT * resp;
XL_SEXP * ret;
void gc_gb_sexp();

	lock_task(xli_lock);
	resp = (XL_RESULT*)s->delay.d.func;
	gc_push(0,0,"remote_delay_func");
	ret = _wait_result_inp(resp);
	gc_pop(ret,gc_gb_sexp);
	over_write_sexp(s,ret);
	unlock_task(xli_lock,"remote_delay_func");
	return s;
}

XL_SEXP *
remote_query(int id,XLISP_ENV * env,L_CHAR * cmd_str,XL_SEXP * cmd_sexp)
{
XL_INTERPRETER * xli;
XL_SEXP * ret;
XL_FILE * file;
int line;

	if ( cmd_sexp ) {
		file = cmd_sexp->h.file;
		line = cmd_sexp->h.line;
	}
	else if ( cmd_str ) {
		file = 0;
		line = 0;
	}
	else 	return 0;
	lock_task(xli_lock);
	xli = _search_xli_id(id);
	if ( xli == 0 ) {
		ret = get_error(
			file,
			line,
			XLE_PROTO_INV_IID,
			l_string(std_cm,"Remote"),
			list(	get_string(
				 l_string(std_cm,
					  "invalid iid 1")),
				0));
		goto end;
	}
	if ( xli->mode != XIM_RUN ) {
		ret = get_error(
			file,
			line,
			XLE_PROTO_INV_IID,
			l_string(std_cm,"Remote"),
			list(	get_string(
				 l_string(std_cm,
					  "invalid iid 2")),
				0));
		goto end;
	}

	if ( xli->thread_mode != TM_2 ) {
		unlock_task(xli_lock,"remote_query");
		return 0;
	}

	ret = _remote_query(xli,env,cmd_str,cmd_sexp);

end:
	unlock_task(xli_lock,"remote_query");

	return ret;
}

XL_SEXP *
local_eval(int seq_no,XL_SEXP * cmd_sexp)
{
XL_INTERPRETER * xli;
XL_RESULT * rr;
XL_SEXP * ret;
int tid;
int inp_line;
XL_INTERPRETER * inp_interpreter;
int err_type;
XLISP_ENV * cenv;

	lock_task(xli_lock);
	xli = _get_my_xli();
	if ( xli == 0 )
		er_panic("local_eval(1)");
	if ( xli->result_head == 0 ) {
		err_type = 1;
		goto err;
	}


	for ( rr = xli->result_head ; rr ; rr = rr->next ) {
		if ( cmp_round(rr->seq_from,seq_no) <= 0 &&
				cmp_round(rr->seq_to,seq_no) > 0 )
			goto ok;
	}
	err_type = 2;


	goto err;
ok:


	inp_line = xli->inp_line;
	inp_interpreter = xli->inp_interpreter;
	xli->inp_line = rr->remote_seq;
	xli->inp_interpreter = rr->inp_interpreter;
	cenv = rr->cur_env;

	lock_mem();
	gc_set_nl(cenv,gc_gblisp_env);
	unlock_mem();

	unlock_task(xli_lock,"local_eval");

	ret = eval(cenv,cmd_sexp);

	lock_task(xli_lock);
	xli->inp_line = inp_line;
	xli->inp_interpreter = inp_interpreter;
	unlock_task(xli_lock,"local_eval(2)");
	return ret;
err:



	unlock_task(xli_lock,"local_eval");
	return get_error(
		cmd_sexp->h.file,
		cmd_sexp->h.line,
		XLE_SYSTEM_NETWORK,
		l_string(std_cm,"LocalEval"),
		List(	get_string(
			 l_string(std_cm,
				  "local evaluation error")),
			get_integer(err_type,0),
			-1));
}

XL_SEXP *
local_query(XLISP_ENV * env,L_CHAR * cmd_str,XL_SEXP * cmd_sexp)
{
XL_INTERPRETER * xli;
XL_SEXP * ret;
int tid;
XL_SEXP * cmd;
L_CHAR * str;
char * st_buf,* en_buf;
int len;

	lock_task(xli_lock);
	xli = _get_my_xli();
	if ( xli == 0 )
		er_panic("local_query");
	if ( xli->mode != XIM_RUN ) {
		ret = get_error(
			0,
			0,
			XLE_PROTO_INV_IID,
			l_string(std_cm,"LocalEval"),
			list(	get_string(
				 l_string(std_cm,
					  "invalid iid 3")),
				0));
	}
	else if ( cmd_str ) {
		st_buf = d_alloc(50);
		en_buf = d_alloc(50);
		if ( cmd_str[0] == '<' ) {
			sprintf(st_buf,"<LocalEval>%i ",xli->inp_line);
			sprintf(en_buf,"</LocalEval>\n");
		}
		else {
			sprintf(st_buf,"(LocalEval %i ",xli->inp_line);
			sprintf(en_buf,")\n");
		}
		len = strlen(st_buf) + strlen(en_buf) + l_strlen(cmd_str);
		str = d_alloc((len+1)*sizeof(L_CHAR));
		l_strcpy(str,l_string(std_cm,st_buf));
		l_strcpy(&str[l_strlen(str)],cmd_str);
		l_strcpy(&str[l_strlen(str)],l_string(std_cm,en_buf));
		d_f_ree(st_buf);
		d_f_ree(en_buf);
		ret = _remote_query(xli->inp_interpreter,env,str,0);
		d_f_ree(str);
	}
	else {
		cmd = List(get_symbol(l_string(std_cm,"LocalEval")),
			get_integer(xli->inp_line,0),
			cmd_sexp,
			-1);
		ret = _remote_query(xli->inp_interpreter,env,0,cmd);
	}
	unlock_task(xli_lock,"remote_query");
	return ret;
}

void
flush_result_queue(XL_INTERPRETER * xli)
{
XL_RESULT * r;
XL_SEXP * err;

	for ( r = xli->result_head ; r ; r = r->next ) {
		err = 0;
		if ( xli->input_file_name )
			err = cons(get_string(xli->input_file_name),err);
		if ( xli->hostname )
			err = cons(get_string(xli->hostname),err);
		if ( xli->ip )
			err = cons(get_integer(xli->ip,0),err);
		r->flags |= XRF_RETURN;
		r->ret = list(
			get_symbol(l_string(std_cm,"Result")),
			get_integer(0,0),
			get_error(
				0,
				r->seq_from,
				XLE_PROTO_ACCESS_STREAM,
				l_string(std_cm,"stream error"),
				err),
			0);
		wakeup_task((int)r);
		if ( r->wup_key )
			wakeup_task(r->wup_key);
	}
/*
	for ( ; xli->result_head ; ) {
		sleep_task((int)&xli->result_head,xli_lock);
		lock_task(xli_lock);
	}
*/
	for ( ; xli->result_head ; )
		delete_result_queue(xli,xli->result_head);

}


int
aboat_remote_query(XL_SEXP * s)
{
STREAM * st;
XL_RESULT * resp;
XL_INTERPRETER * xli;
int iid;
	gc_push(0,0,"aboat_remote_query");
	lock_task(xli_lock);
	if ( get_delay_info(&st,(XL_DELAY**)&resp,s) == CDT_READY ) {
		xli = resp->out_interpreter;
		if ( xli )
			iid = xli->id;
		else	iid = 0;
		unlock_task(xli_lock,"aboat_remote_query");
		close_interpreter(iid);
		gc_pop(0,0);
		return 0;
	}
	unlock_task(xli_lock,"aboat_remote_query");
	if ( st ) {
		s_close(st);
		gc_pop(0,0);
		return 1;
	}
	gc_pop(0,0);
	return -1;
}


