/**********************************************************************
 
	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	"gbacrp.h"
#include	"gbmp.h"
#include 	"task.h"
#include	"lock_level.h"
#include	"pri_level.h"
#include	"memory_debug.h"
#include	"mp.h"

typedef struct cindex_que {
	struct cindex_que * 	next;
	L_CHAR *		crd;
	unsigned int		interval;
	unsigned int		invoke;
	int			cnt;
} CINDEX_QUE;

unsigned int c_min_interval = CINDEX_MIN_INTERVAL;

XL_SEXP * gb_MPcindex();
SEM cindex_lock;

CINDEX_QUE * cindex_head;

void cindex_task();

void
init_MPcindex(XLISP_ENV * env)
{
	set_env(env,l_string(std_cm,"MPcindex"),
		get_func_prim(gb_MPcindex,FO_APPLICATIVE,0,2,2));
	cindex_lock = new_lock(LL_CINDEX);
	create_task(cindex_task,0,PRI_FETCH);
}


void
__insert_cindex_que(CINDEX_QUE * q)
{
CINDEX_QUE ** qp;
	for ( qp = &cindex_head ; *qp ; qp = &(*qp)->next ) {
		if ( (*qp)->invoke < q->invoke )
			continue;
		break;
	}
	q->next = *qp;
	*qp = q;
}

void
free_cindex_que(CINDEX_QUE * q)
{
	d_f_ree(q->crd);
	d_f_ree(q);
}

void
_insert_cindex_que(L_CHAR * crd)
{
CINDEX_QUE * q, ** qp;
MP_WORK mpw;
	for ( q = cindex_head ; q ; q = q->next ) {
		if ( l_strcmp(q->crd,crd) == 0 ) {
			q->cnt ++;
			return;
		}
	}

	get_mp_work(&mpw,0);

	q = d_alloc(sizeof(*q),124);
	q->crd = ll_copy_str(crd,346);
	q->interval = mpw.c_min_interval;
	q->invoke = get_xltime() + mpw.c_min_interval;
	q->cnt = 1;
	q->next = 0;
	__insert_cindex_que(q);
}

void
insert_cindex_que(L_CHAR * crd)
{
	lock_task(cindex_lock);
	_insert_cindex_que(crd);
	unlock_task(cindex_lock,"insert_cindex_que");
}


L_CHAR *
_delete_cindex_que()
{
CINDEX_QUE * ret;
int now;
L_CHAR * crd;
MP_WORK mpw;

	now = get_xltime();
retry:
	ret = cindex_head;
	if ( ret == 0 )
		return 0;
	if ( ret->invoke > now )
		return 0;
	cindex_head = ret->next;
	if ( ret->cnt == 0 ) {
		free_cindex_que(ret);
		goto retry;
	}
	crd = ll_copy_str(ret->crd,346);

	get_mp_work(&mpw,0);

	if ( 2*(now - ret->invoke + ret->interval) > 
			mpw.c_min_interval ) {
		mpw.c_min_interval =
			2*(now - ret->invoke + ret->interval);
	}
	ret->cnt = 0;
	ret->interval *= 2;
	if ( ret->interval < mpw.c_min_interval )
		ret->interval = mpw.c_min_interval;
	ret->invoke = now + ret->interval;
	__insert_cindex_que(ret);
	return crd;
}

L_CHAR *
delete_cindex_que()
{
L_CHAR * ret;
	lock_task(cindex_lock);
	ret = _delete_cindex_que();
	unlock_task(cindex_lock,"delete_cindex_que");
	return ret;
}


XL_SEXP *
get_cindex_que()
{
CINDEX_QUE * q;
XL_SEXP * ret, * e;
unsigned int now;
MP_WORK mpw;
	now = get_xltime();
	for ( q = cindex_head ; q ; q = q->next ) {
		e = List(n_get_symbol("entry"),
			get_string(q->crd),
			get_integer(q->interval ,l_string(std_cm,"sec")),
			get_integer(q->invoke - now,l_string(std_cm,"sec")),
			get_integer(q->cnt,0),
			-1);
		ret = cons(e,ret);
	}
	get_mp_work(&mpw,0);
	return cons(
		List(n_get_symbol("cmin"),
			get_integer(mpw.c_min_interval,
				l_string(std_cm,"sec")),
			-1),
		reverse(ret));
}



void
send_gbstd_cindex(int ses,L_CHAR * crd)
{
URL u;
XL_SEXP * s,* ret;
L_CHAR * fcrd;
int q;
int interval;

MP_WORK mpw;

ss_printf("gbmp send_cindex %ls\n",crd);

	gc_push(0,0,"send_gbstd_cindex");

	fcrd = ll_copy_str(crd,235);

	get_url2(&u,l_string(std_cm,"xlp://localhost:9100/"),1218);
	u.port = get_my_port();
	if ( u.port == 0 ) {
		goto end;
	}

	if ( ses < 0 )
		ses = open_session(SEST_OPTIMIZE);

	interval = get_xltime();

	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("MPcindex"),
				get_string(fcrd),
				-1),
			-1),
		0,0,0);


	get_type(ret);
print_sexp(s_stdout,ret,0);
ss_printf("\n");
	get_mp_work(&mpw,0);
	interval = 2*(get_xltime() - interval);
	if ( mpw.c_min_interval > interval )
		mpw.c_min_interval = mpw.c_min_interval * 0.9 +
				interval * 0.1;
	else	mpw.c_min_interval = mpw.c_min_interval * 0.7 +
				interval * 0.3;
	if ( mpw.c_min_interval < CINDEX_MIN_INTERVAL )
		mpw.c_min_interval = CINDEX_MIN_INTERVAL;
	set_mp_work(&mpw);
end:
	free_url(&u);
	gc_pop(0,0);
	return;
}



void
cindex_task()
{
L_CHAR * crd;
XL_INTERPRETER * xli;
int ses;

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

	ses = open_session(SEST_OPTIMIZE);
	for ( ; ; ) {
		crd = delete_cindex_que();
		if ( crd == 0 ) {
			sleep_sec(1);
			continue;
		}
ss_printf("CINDEX %ls\n",crd);
		send_gbstd_cindex(ses,crd);
		d_f_ree(crd);
	}
}


XL_SEXP *
gb_MPcindex(XLISP_ENV * env,XL_SEXP * s,
	XLISP_ENV* a,XL_SYM_FIELD * sf)
{
XL_SEXP * f;
int len;
int mode;
int ses;



	f = get_el(s,1);
	if ( get_type(f) != XLT_STRING )
		goto type_missmatch;
	len = l_strlen(f->string.data);
	if ( l_strcmp(&f->string.data[len-4],l_string(std_cm,".crd")) != 0 )
		goto invalid_arg;

	insert_cindex_que(f->string.data);
	return 0;
type_missmatch:

	return get_error(
		s->h.file,
		s->h.line,
		XLE_SEMANTICS_TYPE_MISSMATCH,
		l_string(std_cm,"MPcindex"),
		n_get_string("type missmatch"));

invalid_arg:

	return get_error(
		s->h.file,
		s->h.line,
		XLE_PROTO_INV_PARAM,
		l_string(std_cm,"MPcindex"),
		n_get_string("invalid argument"));
}

