/**********************************************************************
 
	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	<stdlib.h>
#include	"memory_debug.h"
#include	"v.h"
#include	"gbparam.h"
#include	"gbgraph.h"
#include	"xlerror.h"
#include	"resource.h"
#include	"tree_cache.h"
#include	"task.h"
#include	"lock_level.h"
#include	"pri_level.h"
#include	"queue.h"
#include	"xl.h"
#include	"win_flame.h"

typedef struct tree_cache_t {
	Q_HEADER		h;
	struct tree_cache_t *	next;
	TREE_CACHE *		tc;
	TREE_NODE * 		tn;
	XL_SEXP *		data;
	XL_SEXP *		tmp;
	int			loop_no;
	int			req_level;
} TREE_CACHE_T;

SEM tc_lock;
TREE_CACHE * tc_list;
int t_cnt;
int ac_time;

int tc_test;

SYS_QUEUE tc_queue1;
SYS_QUEUE tc_queue2;

int
get_ac_time()
{
	return ac_time ++;
}


int
cmp_access(int a,int b)
{
unsigned int c,d;
	if ( ac_time > 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
init_tree_cache()
{
void tc_task1();
void tc_task2();
void gc_tree_cache_t();
void gc_tree_cache_get();

	tc_lock = new_lock(LL_TREE_CACHE);

	memset(&tc_queue1,0,sizeof(SYS_QUEUE));
	tc_queue1.flags = QF_FIFO;
	tc_queue1.gc_func = 0;
	tc_queue1.gc_get = 0;
	tc_queue1.key_func = tc_task1;
	tc_queue1.pri = PRI_FETCH;
	setup_queue(&tc_queue1);

	memset(&tc_queue2,0,sizeof(SYS_QUEUE));
	tc_queue2.flags = QF_FIFO;
	tc_queue2.gc_func = gc_tree_cache_t;
	tc_queue2.gc_get = gc_tree_cache_get;
	tc_queue2.key_func = tc_task2;
	tc_queue2.key_limit = 10;
	tc_queue2.pri = PRI_FETCH;
	setup_queue(&tc_queue2);
}


void
gc_tree_cache_t(TREE_CACHE_T * n)
{
void gc_gb_sexp();

	for ( ; n; n = n->next ) {
		if ( n->data )
			gc_gb_sexp(n->data);
		if ( n->tmp )
			gc_gb_sexp(n->tmp);
	}
}

void
gc_tree_cache_get(TREE_CACHE_T * n)
{
void gc_gb_sexp();


	for ( ; n ; n = n->next ) {
		lock_mem();
		if ( n->data )
			gc_set_nl(n->data,gc_gb_sexp);
		if ( n->tmp )
			gc_set_nl(n->tmp,gc_gb_sexp);
		unlock_mem();
	}
}

void
insert_req_tree1(TREE_CACHE * tc,TREE_NODE * tn,int req_level)
{
TREE_CACHE_T * n;
	n = new_queue_node(sizeof(*n),1245);
	n->h.key = get_server_key(&tc->r->h.target);
	n->tc = tc;
	n->tn = tn;
	n->data = 0;
	n->tmp = 0;
	n->loop_no = tc->r->pr64.loop_no;
	n->req_level = req_level;
	tn->lock ++;
	insert_queue(&tc_queue1,n,0);
}

void
_unlock_tn(TREE_NODE * tn)
{
	if ( tn->lock == 0 )
		er_panic("unlock_tn");
	tn->lock --;
}

void
free_tc_t(TREE_CACHE_T * n,int flags)
{
TREE_CACHE_T * n1;
	lock_task(tc_lock);
	for ( ; n ; ) {
		n1 = n;
		n = n->next;

		if ( n1->tn ) {
			if ( flags & TNF_ERROR ) {
				if (  n1->tn->pixels )
					d_f_ree(n1->tn->pixels);
				n1->tn->pixels = 0;
			}
			n1->tn->flags |= flags;
			_unlock_tn(n1->tn);
		}
		d_f_ree(n1->h.key);
		d_f_ree(n1);
	}
	unlock_task(tc_lock,"free_tc_t");
}

TREE_NODE **
parse_tree(TREE_NODE ** tnp)
{
int i;
int exist;
TREE_NODE ** tnp1, ** tnp2;
	if ( *tnp == 0 )
		return 0;
	tnp2 = 0;
	exist = 0;
	for ( i = 0 ; i < LEAF_MAX ; i ++ ) {
		if ( (*tnp)->leaf[i] )
			exist = 1;
		tnp1 = parse_tree(&(*tnp)->leaf[i]);
		if ( tnp1 == 0 )
			continue;
		if ( (*tnp1)->lock )
			continue;
		if ( tnp2 == 0 )
			tnp2 = tnp1;
		else if ( cmp_access((*tnp1)->access,(*tnp2)->access) < 0 )
			tnp2 = tnp1;
	}
	if ( exist == 0 )
		return tnp;
	return tnp2;
}

void
_flush_tree_node()
{
TREE_NODE * tn, ** tnp,** tnp1;
TREE_CACHE * tc;
TREE_CACHE * target_tc;
int i;
	tnp = 0;
	for ( tc = tc_list ; tc ; tc = tc->next ) {
		tnp1 = parse_tree(&tc->rt);
		if ( tnp1 == 0 )
			continue;
		if ( tnp == 0 ) {
			tnp = tnp1;
			target_tc = tc;
		}
		else if ( cmp_access((*tnp1)->access,
				(*tnp)->access) < 0  ) {
			tnp = tnp1;
			target_tc = tc;
		}
	}
	tn = *tnp;
	*tnp = 0;
	if ( target_tc->cache == tn )
		target_tc->cache = 0;
	if ( tn->pixels ) {
		d_f_ree(tn->pixels);
		touch_obj_mem(-tn->w*tn->h*sizeof(long)); 
	}
	d_f_ree(tn);
	t_cnt --;
}

void
tc_gc_obj()
{
	lock_task(tc_lock);
	_flush_tree_node();
	unlock_task(tc_lock,"tc");
}


int tc_last_access(int * ret_p)
{
TREE_NODE * tn, ** tnp,** tnp1;
TREE_CACHE * tc;
TREE_CACHE * target_tc;
int i;
int ret;
	lock_task(tc_lock);
	tnp = 0;
	for ( tc = tc_list ; tc ; tc = tc->next ) {
		tnp1 = parse_tree(&tc->rt);
		if ( tnp1 == 0 )
			continue;
		if ( tnp == 0 ) {
			tnp = tnp1;
			target_tc = tc;
		}
		else if ( cmp_access((*tnp1)->access,
				(*tnp)->access) < 0  ) {
			tnp = tnp1;
			target_tc = tc;
		}
	}
	if ( tnp ) {
		tn = *tnp;
		*ret_p = tn->access;
		ret = 0;
	}
	else {
		ret = -1;
	}
	unlock_task(tc_lock,"tc");
	return ret;
}

TREE_NODE *
new_tree_node(TREE_CACHE * tc,TREE_NODE * parent,int leaf)
{
TREE_NODE * tn;
int i;
int lx,ly;
RESOURCE * r;
int level,w,h;

	if ( parent ) {
		if ( parent->leaf[leaf] )
			er_panic("new_tree_node");
		if ( parent->level == 0 )
			er_panic("new_tree_node");
		t_cnt ++;
		touch_obj_mem(sizeof(*tn));
		tn = d_alloc(sizeof(*tn),73);

		tn->parent = parent;
		parent->leaf[leaf] = tn;
		tn->level = parent->level - 1;
		lx = leaf % LEAF_RECT;
		ly = leaf / LEAF_RECT;
		tn->ofs_x = parent->ofs_x*2 + lx * RECT_SIZE;
		tn->ofs_y = parent->ofs_y*2 + ly * RECT_SIZE;

	}
	else {
		t_cnt ++;
		touch_obj_mem(sizeof(*tn));
		tn = d_alloc(sizeof(*tn),73);

		tn->parent = 0;
		tn->level = tc->rt_level;
		tn->ofs_x = tn->ofs_y = 0;
		tc->rt = tn;
	}
	level = tn->level;
	r = tc->r;
	if ( r->pr64.width[level] >= tn->ofs_x + RECT_SIZE )
		w = RECT_SIZE;
	else	w = r->pr64.width[level] - tn->ofs_x;
	if ( r->pr64.height[level]
			>= tn->ofs_y + RECT_SIZE )
		h = RECT_SIZE;
	else	h = r->pr64.height[level] - tn->ofs_y;
	if ( w <= 0 || h <= 0 ) {
		d_f_ree(tn);
		if ( parent )
			parent->leaf[leaf] = 0;
		else	tc->rt = 0;
		t_cnt --;
		return 0;
	}

	tn->w = w;
	tn->h = h;
	tn->pixels = 0;
	for ( i = 0 ; i < LEAF_MAX ; i ++ )
		tn->leaf[i] = 0;
	tn->access = get_ac_time();
	tn->lock = 0;
	tn->flags = 0;
	return tn;
}

void
init_tc(int ses,TREE_CACHE * tc,int level,int rs,
	int (*func_pixels)(),
	int (*func_query)(),
	RESOURCE * r)
{
int i,lp;
unsigned long * pixels;
int w,h;
TREE_NODE * tn;
	tc->rt_level = level-1;
	tc->rt = 0;
	tc->cache = 0;
	tc->rect_logsize = rs;
	tc->get_pixels = func_pixels;
	tc->get_query = func_query;
	tc->r = r;
	tc->next = tc_list;
	tc_list = tc;
}


TREE_CACHE_T *
tc_dirty_insert(TREE_CACHE_T * n3,TREE_CACHE_T * n_end)
{
I_RECT ir;
GB_RECT rct;
REAL1 dpm;
RESOURCE * r;
TREE_NODE * tn;
TREE_CACHE * tc;
TREE_CACHE_T * n2;
	for ( n2 = n3 ; n2 && n2 != n_end ; n2 = n2->next ) {
		tn = n2->tn;
		r = n2->tc->r;

		ir.tl.x = tn->ofs_x * (1<<tn->level);
		ir.tl.y = tn->ofs_y * (1<<tn->level);
		ir.br.x = ir.tl.x +
				tn->w * (1<<tn->level);
		ir.br.y = ir.tl.y +
				tn->h * (1<<tn->level);
		dpm = r->pr64.dpm;
		rct.tl.x = ir.tl.x / dpm;
		rct.tl.y = ir.tl.y / dpm;
		rct.br.x = ir.br.x / dpm;
		rct.br.y = ir.br.y / dpm;
		tc = n2->tc;
		if ( n2->req_level <= tn->level ||
				tc->rt_level - n2->req_level 
				> 2*(tc->rt_level - tn->level) )
			wf_insert_dirty_rect(r,
				&rct,WFF_LUSTER_DIRTY,120,10);
	}
	return n2;
}


void
tc_task2(TKEY d)
{
SYS_QUEUE * que;
XL_INTERPRETER * xli;
L_CHAR * key;
TREE_CACHE_T * n1, * n2, * n3;
XL_SEXP * data;
RESOURCE * r;
TREE_NODE * tn;
long * buf;
int ret;
GB_RECT rct;
I_RECT ir;
REAL1 dpm;
TREE_CACHE * tc;
unsigned int t;

	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,"tc_task2");
		n1 = delete_queue(que,sq_key_cond,key,0);
		if ( n1 == 0 )
			break;
		r = n1->tc->r;
		switch ( get_type(n1->data) ) {
		case XLT_ERROR:
			fprintf(stderr,"r64 get error ");
			print_sexp(s_stderr,n1->data,0);

			set_inc_status_error(r,n1->data);

			fprintf(stderr,"\n");
			goto n1_free;
		case XLT_PAIR:
			set_inc_status(r,RS_IDLE);
			break;
		default:

			set_inc_status_error(r,
				get_error(
					0,
					0,
					XLE_SEMANTICS_TYPE_MISSMATCH,
					l_string(std_cm,"Get(r64)"),
				n_get_string(
				"server return invalid type data")));

			fprintf(stderr,"r64 get: type missmatch return\n");
			print_sexp(s_stderr,n1->data,PF_RAW_DISABLE);
			fprintf(stderr,"\n");
			goto n1_free;
		}
		data = n1->data;
		n3 = n1;
		t = get_xltime();
		for ( n2 = n1 ; n2 ; n2 = n2->next ) {
			tn = n2->tn;
			r = n2->tc->r;
 			(*n2->tc->get_pixels)(n2->tc,tn,
				car(data),n2->tmp);
			data = cdr(data);
			if ( get_xltime() - t >= 2 )
				n3 = tc_dirty_insert(n3,n2);
		}
		tc_dirty_insert(n3,0);
		free_tc_t(n1,0);

		gc_pop(0,0);
		continue;
	n1_free:
		gc_pop(0,0);
		free_tc_t(n1,TNF_ERROR);
	}
	gc_pop(0,0);

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

	close_self_interpreter();

}

void
tc_task1(TKEY d)
{
SYS_QUEUE * que;
XL_INTERPRETER * xli;
L_CHAR * key;
int ses;
TREE_CACHE_T * n1, * n2, * n3, * _n, * np;
XL_SEXP * data, * dd, * tmp, * tmp_dd;
int cnt;
I_RECT ir;
int w,h;
int level;
RESOURCE * r;
extern int spiral_count;

	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);

	ses = open_session(SEST_OPTIMIZE);

	for ( ; ; ) {

		cnt = 0;
		n2 = 0;
		for ( ; cnt < 20 ; ) {
			n1 = delete_queue(que,sq_key_cond,key,0);
			if ( n1 == 0 )
				break;
			lock_task(tc_lock);
			unlock_task(tc_lock,"tc_task");
			if ( n1->tc->r->pr64.loop_no - n1->loop_no
					> spiral_count + 2 ) {
				n1->next = 0;
				free_tc_t(n1,TNF_ERROR);
				continue;
			}
			cnt ++;
			n1->next = n2;
			n2 = n1;
		}
		if ( n2 == 0 )
			break;
		gc_push(0,0,"beam");
		n3 = 0;
		data = 0;
		tmp = 0;
		for ( ; n2 ; ) {
			_n = n2;
			n2 = _n->next;

			ir.tl.x = _n->tn->ofs_x;
			ir.tl.y = _n->tn->ofs_y;

			ir.br.x = ir.tl.x + _n->tn->w;
			ir.br.y = ir.tl.y + _n->tn->h;

			(*_n->tc->get_query)(
				&dd,
				&_n->tmp,
				&ir,
				_n->tc,_n->tn);

			data = cons(dd,data);

			_n->data = 0;

			_n->next = n3;
			n3 = _n;
		}
		data = cons(n_get_symbol("List"),data);;
		n3->data = remote_session(
			gblisp_top_env0,
			ses,
			&n3->tc->r->h.target,
			l_string(std_cm,"gbr64"),
			l_string(std_cm,"user"),
			l_string(std_cm,"Get"),
			List(data,-1),
			0,0,0);
		insert_queue(&tc_queue2,n3,1);

		gc_pop(0,0);
	}


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

	close_session(ses);
	close_self_interpreter();

}


unsigned long
_tc_get_pixel(TREE_CACHE * tc,int level,int x,int y,
	     int * lev)
{
TREE_NODE * tn, ** tnp, * tn2;
int i;
int lf;
int lx,ly;
unsigned long * pixels;
unsigned long ret;
int hit;

int test = 0;


	if ( tc->cache ) {
		if (	tc->cache->level == level &&
			tc->tl_x <= x && x < tc->br_x &&
			tc->tl_y <= y && y < tc->br_y ) {

			tn = tc->cache;
			hit = 1;
			goto end;
		}
	}

	hit = 0;
	if ( tc->rt == 0 ) {
		tn = 0;
		tn2 = new_tree_node(tc,0,0);
		if ( tn2 == 0 )
			er_panic("_tc_get_pixel");
		insert_req_tree1(tc,tn2,level);
		goto empty_loop2;
	}
	else {
		tn = tc->rt;
		if ( tn->pixels == 0 ) {
			tn2 = tn;
			tn = 0;
			goto empty_loop1;
		}
		tn->access = get_ac_time();
	}
	for ( tn = tc->rt ; tn->level != level ; ) {
		tn->lock ++;
		lf = 1 << (tn->level+tc->rect_logsize-1);
		if ( x&lf )
			lx = 1;
		else	lx = 0;
		if ( y&lf )
			ly = 1;
		else	ly = 0;
		tnp = &tn->leaf[lf = lx + ly*LEAF_RECT];
		if ( *tnp == 0 ) {
			tn2 = new_tree_node(tc,tn,lf);
			if ( tn2 == 0 )
				goto no_pixel;
			insert_req_tree1(tc,tn2,level);
			goto empty_loop2;
		}
		else if ( (*tnp)->pixels )
			tn = *tnp;
		else {
			tn2 = *tnp;
		 	goto empty_loop1;
		}
		tn->access = get_ac_time();
	}
	tn->lock ++;
	goto end;
empty_loop1:
	for ( ; tn2->level != level ; ) {
		if ( tn2->flags & TNF_ERROR ) {
			tn2->flags &= ~TNF_ERROR;
			insert_req_tree1(tc,tn2,level);
		}
		lf = 1 << (tn2->level+tc->rect_logsize-1);
		if ( x&lf )
			lx = 1;
		else	lx = 0;
		if ( y&lf )
			ly = 1;
		else	ly = 0;
		tnp = &tn2->leaf[lf = lx + ly*LEAF_RECT];
		if ( *tnp == 0 ) {
			tn2 = new_tree_node(tc,tn2,lf);
			if ( tn2 == 0 )
				goto no_pixel;
			tn2->pixels = 0;
			insert_req_tree1(tc,tn2,level);
			goto empty_loop2;
		}
		tn2 = *tnp;
	}
	if ( tn2->flags & TNF_ERROR ) {
		tn2->flags &= ~TNF_ERROR;
		insert_req_tree1(tc,tn2,level);
	}
	goto end;
empty_loop2:
	for ( ; tn2->level != level ; ) {
		lf = 1 << (tn2->level+tc->rect_logsize-1);
		if ( x&lf )
			lx = 1;
		else	lx = 0;
		if ( y&lf )
			ly = 1;
		else	ly = 0;
		tnp = &tn2->leaf[lf = lx + ly*LEAF_RECT];
		tn2 = new_tree_node(tc,tn2,lf);
		if ( tn2 == 0 )
			goto no_pixel;
		tn2->access = get_ac_time();
		insert_req_tree1(tc,tn2,level);
	}
	goto end;
no_pixel:
	ret = C_TRANSPARENT;
	goto end2;
end:
	if ( tn == 0 )
		return C_TRANSPARENT;
	*lev = tn->level;
	tn->access = get_ac_time();
	lx = (x>>(tn->level))&((1<<tc->rect_logsize)-1);
	ly = (y>>(tn->level))&((1<<tc->rect_logsize)-1);
	if ( lx >= tn->w ) {
		ret = C_TRANSPARENT;
		if ( hit )
			return ret;
		goto end2;
	}
	if ( ly >= tn->h ) {
		ret = C_TRANSPARENT;
		if ( hit )
			return ret;
		goto end2;
	}
	ret = tn->pixels[lx + ly*tn->w];
	if ( hit == 0 ) {
	int mask,size;
		size = 1<<(tn->level+tc->rect_logsize);
		mask = -size;
		tc->tl_x = x&mask;
		tc->tl_y = y&mask;
		tc->br_x = tc->tl_x + size;
		tc->br_y = tc->tl_y + size;
		tc->cache = tn;
end2:
		for ( ; tn ; tn = tn->parent )
			tn->lock --;
	}
	return ret;
}


