/**********************************************************************
 
	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	"gbview.h"
#include	"avt.h"
#include	"xl.h"
#include	"HTMLDB.h"
#include	"mlong_char.h"
#include	"xlerror.h"
#include	"lock_level.h"
#include	"pri_level.h"

#define MAX_STYLE_TREE_SIZE	10

AVT_NODE *	style_tree;
SEM		style_lock;
int style_tree_size;
int max_style_tree_size;
INFO_SCHEME *	default_scheme;
STYLE_FILE	style_root;

XL_SEXP * gv_set_default_style();
XL_SEXP * gv_set_default_scheme();

void
init_sring(STYLE_FILE * sf)
{
	sf->next = sf->prev = sf;
}

void
insert_sring(STYLE_FILE * s1,STYLE_FILE * s2)
{
	s2->prev = s1;
	s2->next = s1->next;
	s2->prev->next = s2;
	s2->next->prev = s2;
}

void
delete_sring(STYLE_FILE * s)
{
	s->prev->next = s->next;
	s->next->prev = s->prev;
}

void
init_style()
{
void style_load_task();
	style_tree = 0;
	default_scheme = 0;
	style_lock = new_lock(LL_STYLE);
	init_sring(&style_root);
	create_task(style_load_task,0,PRI_FETCH_STRONG);
	max_style_tree_size = MAX_STYLE_TREE_SIZE;
	set_env(gblisp_top_env0,l_string(std_cm,"gv-set-default-style"),
		get_func_prim(gv_set_default_style,FO_NORMAL,0,2,-1));
	set_env(gblisp_top_env0,l_string(std_cm,"gv-set-default-scheme"),
		get_func_prim(gv_set_default_scheme,FO_APPLICATIVE,0,1,1));
}

void
gc_style_file(STYLE_FILE * sf)
{
void gc_gb_sexp();
	gc_gb_sexp(sf->style);
}

int
gc_style_func(AVT_NODE * a)
{
	gc_style_file(a->data);
	return 0;
}

void
gc_style()
{
	avt_trace_from_small(style_tree,gc_style_func,0);
}

int 
cmp_style(STYLE_FILE * sf1,STYLE_FILE * sf2)
{
	return l_strcmp(sf1->name,sf2->name);
}

STYLE_FILE *
_insert_style_file(L_CHAR * name,int type,int sts,XL_SEXP * style)
{
AVT_NODE * a, * a2;
STYLE_FILE * sf;
	sf = d_alloc(sizeof(*sf));
	sf->name = ll_copy_str(name);
	sf->type = type;
	sf->style = style;
	sf->state = sts;
	a = d_alloc(sizeof(*a));
	a->data = sf;
	a2 = avt_insert(&style_tree,a,cmp_style);
	if ( a2 != a ) {
		d_f_ree(a);
		d_f_ree(sf->name);
		d_f_ree(sf);
		sf = a2->data;
		sf->type = type;
		sf->style = style;
		sf->state = sts;
		delete_sring(sf);
	}
	insert_sring(&style_root,sf);
	return sf;
}

STYLE_FILE *
insert_style_file(L_CHAR * name,int type,int sts,XL_SEXP * style)
{
STYLE_FILE * ret;
	lock_task(style_lock);
	ret = _insert_style_file(name,type,sts,style);
	unlock_task(style_lock,"insert_style_file");
	return ret;
}

void
insert_scheme(
	INFO_SCHEME ** is_header,
	L_CHAR * scheme,
	int	card_type,
	int	onmap_type,
	L_CHAR * card,
	L_CHAR * onmap)
{
INFO_SCHEME * is;
	lock_task(style_lock);
	for ( is = *is_header ; is ; is = is->next )
		if ( l_strcmp(is->scheme,scheme) == 0 ) {
			is->card_type = card_type;
			is->onmap_type = onmap_type;
			if ( is->card_style )
				d_f_ree(is->card_style);
			if ( is->onmap_style )
				d_f_ree(is->onmap_style);
			if ( card == 0 || card[0] == 0 )
				is->card_style = 0;
			else	is->card_style = ll_copy_str(card);
			if ( onmap == 0 || onmap[0] == 0 )
				is->onmap_style = 0;
			else	is->onmap_style = ll_copy_str(onmap);
			goto end;
		}
	is = d_alloc(sizeof(*is));
	is->scheme = ll_copy_str(scheme);
	is->card_type = card_type;
	is->onmap_type = onmap_type;
	if ( card == 0 || card[0] == 0 )
		is->card_style = 0;
	else	is->card_style = ll_copy_str(card);
	if ( onmap == 0 || onmap[0] == 0 )
		is->onmap_style = 0;
	else	is->onmap_style = ll_copy_str(onmap);
	is->next = *is_header;
	*is_header = is;
end:
	unlock_task(style_lock,"insert_scheme");
}

XL_SEXP *
get_xl_env(XL_SEXP * style)
{
XLISP_ENV * env;
XL_SEXP * ret;
	env = new_env(gblisp_top_env0);
	for ( ; get_type(style) == XLT_PAIR ; style = cdr(style) ) {
		ret = eval(env,car(style));
		if ( get_type(ret) == XLT_ERROR ) {
			s_printf(s_stdout,"STYLE ERROR ");
			print_sexp(s_stdout,ret,0);
			s_printf(s_stdout,"\n");
		}
	}
	return get_env(env);
}

void
fetch_style_file(int ses,STYLE_FILE * sf)
{
URL u;
XL_SEXP * query;
XL_SEXP * ret;
int sts;
XLISP_ENV * env;
L_CHAR * f;
	get_url2(&u,sf->name);
	if ( u.proto == 0 ) {
		sf->state = SFS_ERROR;
		return;
	}
	gc_push(0,0,"fetch_style_file");
	query = List(get_symbol(l_string(std_cm,"Get")),
			get_string(f=get_url_filepath(&u)),
			-1);
	d_f_ree(f);
	ret = remote_session(
		gblisp_top_env0,
		ses,
		&u,
		l_string(std_cm,"standard"),
		l_string(std_cm,"user"),
		l_string(std_cm,"Get"),
		List(query,-1),
		0,0,0);

	switch ( get_type(ret) ) {
	case XLT_ERROR:
		fprintf(stderr,"style get error ");
		print_sexp(s_stderr,ret,0);

		fprintf(stderr,"\n");
		sts = SFS_ERROR;
		break;
	case XLT_PAIR:
	case XLT_NULL:
		sts = SFS_LOADED;
		break;
	default:

		fprintf(stderr,"get: type missmatch return\n");
		print_sexp(s_stderr,ret,PF_RAW_DISABLE);
		fprintf(stderr,"\n");
		sts = SFS_ERROR;
	}
	sf->state = sts;
	switch ( sf->type ) {
	case SFT_XL:
	case SFT_XL_ENV:
		sf->type = SFT_XL_ENV;
		sf->style = get_xl_env(ret);
		break;
	default:
		sf->style = ret;
	}
	free_url(&u);
	gc_pop(0,0);
	wakeup_task((int)sf);
}

STYLE_FILE *
search_style_file(L_CHAR * name,int type,int wait_flag)
{
AVT_NODE * a;
STYLE_FILE * sf;
STYLE_FILE sfb;

	lock_task(style_lock);
	sfb.name = name;
	a = avt_search(style_tree,&sfb,cmp_style);
	if ( a == 0 ) {
		sf = _insert_style_file(name,type,SFS_UNLOAD,0);
		style_tree_size ++;
		wakeup_task((int)&style_root);
	}
	else {
		sf = a->data;
		delete_sring(sf);
		insert_sring(&style_root,sf);
	}
retry:
	switch ( sf->state ) {
	case SFS_UNLOAD:
		if ( wait_flag == 0 ) {
			unlock_task(style_lock,"search_style_file");
			return 0;
		}
		sleep_task((int)sf,style_lock);
		lock_task(style_lock);
		goto retry;
	case SFS_LOADED:
	case SFS_LOCKED:
		unlock_task(style_lock,"search_Style_file");
		return sf;
	case SFS_ERROR:
		unlock_task(style_lock,"search_Style_file");
		return 0;
	}
	return 0;
}

INFO_SCHEME *
search_scheme(INFO_SCHEME * is,L_CHAR * scheme)
{
	lock_task(style_lock);
	for ( ; is ; is = is->next )
		if ( l_strcmp(scheme,is->scheme) == 0 ) {
			unlock_task(style_lock,"search_scheme");
			return is;
		}
	unlock_task(style_lock,"search_scheme");
	return 0;
}

STYLE_FILE *
get_info_style(RESOURCE * r,L_CHAR * scheme,int type,int wait_flag)
{
INFO_SCHEME * is;
STYLE_FILE * sf;

	is = search_scheme(r->draw_gb.ischeme,scheme);
	if ( is == 0 )
		goto next1;
	if ( type == IS_CARD ) {
		if ( is->card_style == 0 )
			goto next1;
		sf = search_style_file(is->card_style,is->card_type,
				wait_flag);
		if ( sf == 0 )
			goto next1;
		return sf;
	}
	else {
		if ( is->onmap_style == 0 )
			goto next1;
		sf = search_style_file(is->onmap_style,
			is->onmap_type,wait_flag);
		if ( sf == 0 )
			goto next1;
		return sf;
	}
next1:
	is = search_scheme(default_scheme,scheme);
	if ( is == 0 )
		return 0;
	if ( type == IS_CARD ) {
		if ( is->card_style == 0 )
			return 0;
		return search_style_file(is->card_style,
			is->card_type,wait_flag);
	}
	else {
		if ( is->onmap_style == 0 )
			return 0;
		return search_style_file(is->onmap_style,
			is->onmap_type,wait_flag);
	}
}

void
setup_onmap_env(RESOURCE * r,XLISP_ENV * env)
{
	set_env(env,l_string(std_cm,"entry-uri"),
		get_string(get_url_str(&r->h.entry)));
	set_env(env,l_string(std_cm,"target-uri"),
		get_string(get_url_str(&r->h.target)));
	set_env(env,l_string(std_cm,"onmap-resolution"),
		get_floating(0,l_string(std_cm,"dot/m")));
}


XL_SEXP *
_get_styled_info(RESOURCE * r,int type,XL_SEXP * org,REAL1 * reso)
{
STYLE_FILE * sf;
L_CHAR * scheme;
XL_SEXP * sym;
XL_SYM_FIELD * sff;
XL_SEXP * e;
XLISP_ENV * env;
XL_SEXP * ret;
XL_SEXP * _reso;
REAL1 __reso;
int er;
L_CHAR * u;
void gc_gb_sexp();
	if ( get_type(org) != XLT_PAIR )
		er_panic("get_card_info(1)");
	sym = get_el(org,0);
	if ( get_type(sym) != XLT_SYMBOL )
		er_panic("get_card_info(2)");
	scheme = nl_copy_mstr(std_cm,"xml");
	for ( sff = sym->symbol.field ; sff ; sff = sff->next ) {
		if ( l_strcmp(sff->name,l_string(std_cm,"scheme")) == 0 ) {
			scheme = sff->data;
			break;
		}
	}
	sf = get_info_style(r,scheme,type,1);
	if ( sf == 0 )
		return org;
	if ( sf->style == 0 ) {
		printf("STYLE ZERO %x\n",sf);
		return org;
	}
	switch ( sf->type ) {
	case SFT_XL_ENV:
		gc_push(0,0,"_get_style_info");
		env = new_env(sf->style->env.data);
		setup_onmap_env(r,env);
		ret = eval(env,org);
		_reso = eval(env,n_get_symbol("onmap-resolution"));
		*reso = 0;
		switch ( get_type(_reso) ) {
		case XLT_INTEGER:
			__reso = _reso->integer.data;
			u = _reso->integer.unit;
			goto conv;
		case XLT_FLOAT:
			__reso = _reso->floating.data;
			u = _reso->floating.unit;
		conv:
			*reso = conv_unit(&er,
					get_uenv(gblisp_top_env0),
					__reso,u,
					l_string(std_cm,"dot/m"));
		}
		gc_pop(ret,gc_gb_sexp);
		return ret;
	default:
		return org;
	}
	return 0;
}

XL_SEXP *
get_card_info(RESOURCE * r,XL_SEXP * org)
{
REAL1 reso;
	return _get_styled_info(r,IS_CARD,org,&reso);
}

XL_SEXP *
get_onmap_info(RESOURCE * r,XL_SEXP * org,REAL1 * reso)
{
	return _get_styled_info(r,IS_ONMAP,org,reso);
}

void
style_load_task()
{
XL_INTERPRETER * xli;
int ses;
STYLE_FILE * sf;
AVT_NODE * a;

	xli = new_xl_interpreter();
	xli->a_type = XLA_SELF;
	setup_i(xli);
	ses = open_session(SEST_OPTIMIZE);

	for ( ; ; ) {
		lock_task(style_lock);
		for ( sf = style_root.next ; sf != &style_root ;
					sf = sf->next ) {
			if ( sf->state == SFS_UNLOAD )
				goto fetch;
		}
		for ( ; style_tree_size > max_style_tree_size ; ) {
			sf = style_root.prev;
			for ( ; sf != &style_root ; sf = sf->prev )
				if ( sf->type == SFS_LOADED )
					break;
			if ( sf == &style_root ) {
				max_style_tree_size *= 2;
				goto end;
			}
			a = avt_delete(&style_tree,sf,cmp_style);
			sf = a->data;
			delete_sring(sf);
			d_f_ree(sf->name);
			d_f_ree(sf);
			d_f_ree(a);
			style_tree_size --;
		}
	end:
printf("**** sleep\n");
		sleep_task((int)&style_root,style_lock);
printf("**** wakeup\n");
		continue;
	fetch:
		unlock_task(style_lock,"style_lock_task");
		fetch_style_file(ses,sf);
	}
}

XL_SEXP *
gv_set_default_scheme(XLISP_ENV * env,XL_SEXP * s,
	XLISP_ENV * a,XL_SYM_FIELD * sf)
{
L_CHAR * card, * onmap;
L_CHAR * scheme;
char * e_param;
int card_type,onmap_type;
	scheme = 0;
	card = 0;
	onmap = 0;
	card_type = SFT_XL;
	onmap_type = SFT_XL;
	for ( ; sf ; sf = sf->next ) {
		if ( l_strcmp(sf->name,l_string(std_cm,"scheme")) == 0 )
			scheme = sf->data;
		else if ( l_strcmp(sf->name,l_string(std_cm,"card")) == 0 )
			card = sf->data;
		else if ( l_strcmp(sf->name,l_string(std_cm,"onmap")) == 0 )
			onmap = sf->data;
		else if ( l_strcmp(sf->name,l_string(std_cm,"card.type"))
				== 0 ) {
			e_param = "card.type";
			if ( l_strcmp(sf->data,
				l_string(std_cm,"text/xl")) == 0 )
				card_type = SFT_XL;
			else if ( l_strcmp(sf->data,
				l_string(std_cm,"text/xsl")) == 0 )
				card_type = SFT_XSL;
			else goto type_missmatch;
		}
		else if ( l_strcmp(sf->name,l_string(std_cm,"onmap.type"))
				== 0 ) {
			e_param = "onmap.type";
			if ( l_strcmp(sf->data,
				l_string(std_cm,"text/xl")) == 0 )
				onmap_type = SFT_XL;
			else if ( l_strcmp(sf->data,
				l_string(std_cm,"text/xsl")) == 0 )
				onmap_type = SFT_XSL;
			else goto type_missmatch;
		}
	}
	if ( scheme == 0 ) {
		e_param = "scheme";
		goto attr_required;
	}
	if ( card == 0 ) {
		e_param = "card";
		goto attr_required;
	}
	if ( onmap == 0 ) {
		e_param = "onmap";
		goto attr_required;
	}
	insert_scheme(&default_scheme,scheme,
		card_type,onmap_type,
		card,onmap);
	return 0;
attr_required:
	return get_error(
		s->h.file,
		s->h.line,
		XLE_PROTO_INV_PARAM,
		l_string(std_cm,"gv-set-default-scheme"),
		List(n_get_string("attribute"),
			n_get_string(e_param),
			n_get_string("is required"),
			-1));
type_missmatch:
	return get_error(
		s->h.file,
		s->h.line,
		XLE_SEMANTICS_TYPE_MISSMATCH,
		l_string(std_cm,"gv-set-default-scheme"),
		List(n_get_string("attribute"),
			n_get_string(e_param),
			n_get_string("type missmatch"),
			-1));
}


XL_SEXP *
gv_set_default_style(XLISP_ENV * env,XL_SEXP * s,
	XLISP_ENV * a,XL_SYM_FIELD * sf)
{
L_CHAR * name;
XL_SEXP * style;
char * e_param;
int type;
	type = SFT_XL;
	name = 0;
	for ( ; sf ; sf = sf->next ) {
		if ( l_strcmp(sf->name,l_string(std_cm,"name")) == 0 )
			name = sf->data;
		else if ( l_strcmp(sf->name,l_string(std_cm,"type"))
				== 0 ) {
			e_param = "type";
			if ( l_strcmp(sf->data,
				l_string(std_cm,"text/xl")) == 0 )
				type = SFT_XL;
			else if ( l_strcmp(sf->data,
				l_string(std_cm,"text/xsl")) == 0 )
				type = SFT_XSL;
			else goto type_missmatch;
		}
	}
	if ( name == 0 ) {
		e_param = "name";
		goto attr_required;
	}
	style = cdr(s);
	if ( type == SFT_XL ) {
		style = get_xl_env(style);
		type = SFT_XL_ENV;
	}
	insert_style_file(name,type,SFS_LOCKED,style);
	return 0;
attr_required:
	return get_error(
		s->h.file,
		s->h.line,
		XLE_PROTO_INV_PARAM,
		l_string(std_cm,"gv-set-default-scheme"),
		List(n_get_string("attribute"),
			n_get_string(e_param),
			n_get_string("is required"),
			-1));
type_missmatch:
	return get_error(
		s->h.file,
		s->h.line,
		XLE_SEMANTICS_TYPE_MISSMATCH,
		l_string(std_cm,"gv-set-default-scheme"),
		List(n_get_string("attribute"),
			n_get_string(e_param),
			n_get_string("type missmatch"),
			-1));
}


