/**********************************************************************
 
	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	<fcntl.h>
#include	<math.h>
#include	"memory_debug.h"
#include	"xl.h"
#include	"gbparam.h"
#include	"utils.h"
#include	"gbview.h"
#include	"memory_routine.h"
#include	"xlerror.h"
#include	"tree_cache.h"
#include	"lock_level.h"
#include	"pri_level.h"
#include	"win_flame.h"

typedef struct pixel_req {
	struct pixel_req *	next;
	int			loop_no;
	WF_ID	 		wfid;
	RESOURCE *		r;
	int			level;
	int			x;
	int			y;
} PIXEL_REQ;

extern int spiral_count;
void pixel_req_task();
void pixel_gc_task();

extern SEM luster_lock,tc_lock;
extern XLISP_ENV * gv_resource_env[];
PIXEL_REQ *	pixel_req_queue;
PIXEL_REQ * 	prq_free;
int queue_length;
void pixel_gc_tick();
int get_pixel_r64();
int get_pixel_cr();
int get_query_r64();
int get_query_cr();

void
init_luster_r64()
{
XLISP_ENV * l_top;
XL_SEXP * gv_set_visible_resolution();
	init_luster();
	l_top = gv_resource_env[RT_PIXEL_MAP_R64];
	if ( l_top == 0 )
		goto next;
	set_env(l_top,l_string(std_cm,"set-visible-resolution"),
			get_func_prim(gv_set_visible_resolution,
			FO_APPLICATIVE,0,2,2));
next:
/*
	create_task(pixel_req_task,0,PRI_FETCH);
*/
	new_tick(pixel_gc_tick,1,0);
}

void
gv_new_luster_option(RESOURCE * r,int r64_level)
{
int ses;
	ses = open_session(SEST_OPTIMIZE);
	switch ( r->pr64.type ) {
	case 'P':
	case 'G':
	case 'B':
		init_tc(ses,&r->pr64.tc,r64_level,RECT_LOGSIZE,
			get_pixel_r64,get_query_r64,r);
		break;
	case '0':
	case '1':
	case '2':
		init_tc(ses,&r->pr64.tc,r64_level,RECT_LOGSIZE,
			get_pixel_cr,get_query_cr,r);
		break;
	default:
		er_panic("gv_new_luster_option(1)");
	}
	close_session(ses);
}

void
insert_pixel_req(WF_ID wfid,RESOURCE * r,int lev,int x,int y)
{
PIXEL_REQ * pr;
	if ( prq_free ) {
		pr = prq_free;
		prq_free = pr->next;
	}
	else	pr = d_alloc(sizeof(*pr),2);
	pr->loop_no = r->pr64.loop_no;
	pr->wfid = wfid;
	pr->r = r;
	pr->x = x;
	pr->y = y;
	pr->level = lev;
	pr->next = pixel_req_queue;
	pixel_req_queue = pr;
	queue_length ++;
}

PIXEL_REQ *
delete_pixel_req()
{
PIXEL_REQ * pr;
	pr = pixel_req_queue;
	pixel_req_queue = pr->next;
	queue_length --;
	return pr;
}

void
clean_prq(PIXEL_REQ * pr)
{
PIXEL_REQ * pr1,** prp;
int _yy,_yy1;
int _xx,_xx1;
	for ( prp = &pixel_req_queue ; *prp ; ) {
		pr1 = *prp;
		if ( pr1->r != pr->r )
			goto next;
		if ( pr1->wfid != pr->wfid )
			goto next;
		if ( pr1->level < pr->level )
			goto next;
		_xx1 = pr1->x&(-(RECT_SIZE<<pr1->level));
		_xx = pr->x&(-(RECT_SIZE<<pr1->level));
		if ( _xx != _xx1 )
			goto next;
		_yy1 = pr1->y&(-(RECT_SIZE<<pr1->level));
		_yy = pr->y&(-(RECT_SIZE<<pr1->level));
		if ( _yy != _yy1 )
			goto next;
		*prp = pr1->next;
		pr1->next = prq_free;
		prq_free = pr1;
		queue_length --;
		continue;
	next:
		prp = &pr1->next;
	}
	pr->next = prq_free;
	prq_free = pr;
}

void
purge_prq(PIXEL_REQ * pr)
{
RESOURCE * r;
PIXEL_REQ * pr1, ** prp;
	r = pr->r;
	for ( prp = &pixel_req_queue ; *prp ; ) {
		pr1 = *prp;
		if ( pr1->r == r ) {
			*prp = pr1->next;
			pr1->next = prq_free;
			prq_free = pr1;
			queue_length --;
		}
		else {
			prp = &pr1->next;
		}
	}
	pr->next = prq_free;
	prq_free = pr;
}

void
pixel_gc_tick()
{
int i;
PIXEL_REQ * pr;
	lock_task(luster_lock);
	for ( i = 0 ; i < 20000 && prq_free ; i ++ ) {
		pr = prq_free;
		prq_free = pr->next;
		d_f_ree(pr);
	}
	unlock_task(luster_lock,"tick");
}


void
set_pixel_dirty(PIXEL_REQ * pr)
{
int length;
GB_RECT r;
int pic;
int x,y;
int mask;
	length = RECT_SIZE<<pr->level;
	mask = -length;
	x = pr->x & mask;
	y = pr->y & mask;
	r.tl.x = x / pr->r->pr64.dpm;
	r.tl.y = y / pr->r->pr64.dpm;
	pic = x + length;
	if ( pic > pr->r->pr64.width[0] )
		pic = pr->r->pr64.width[0];
	r.br.x = pic / pr->r->pr64.dpm;
	pic = pr->y + length;
	if ( pic > pr->r->pr64.height[0] )
		pic = pr->r->pr64.height[0];
	r.br.y = pic / pr->r->pr64.dpm;
	wf_insert_dirty_rect(pr->r,&r,WFF_LUSTER_DIRTY,120,10);
}

/*
void
pixel_req_task()
{
int lev2;
int ses;
XL_INTERPRETER * xli;
PIXEL_REQ * pr;
int i;
	xli = new_xl_interpreter();
	xli->a_type = XLA_SELF;
	setup_i(xli);
	ses = open_session(SEST_OPTIMIZE);


	for ( ; ; ) {

		lock_task(luster_lock);
		for ( ; pixel_req_queue == 0 ; ) {
			sleep_task((int)&pixel_req_queue,luster_lock);
			lock_task(luster_lock);
		}
		pr = delete_pixel_req();
		if ( pr->loop_no < pr->r->pr64.loop_no - spiral_count - 2 ) {
			purge_prq(pr);
			unlock_task(luster_lock,"pixel_req_task");
			continue;
		}
		unlock_task(luster_lock,"pixel_req_task");

		tc_get_pixel(ses,&pr->r->pr64.tc,
			pr->level,
			pr->x,
			pr->y,1,
			&lev2);
		if ( lev2 == pr->level ) {
			set_pixel_dirty(pr);
		}
		lock_task(luster_lock);
		clean_prq(pr);
		unlock_task(luster_lock,"pixel_req_task");
	}
}
*/

void
draw_pixel_data(
	RESOURCE* r,
	DRAW_WORK * dw)
{
int i,j;
GB_POINT p;
REAL1 res,dx,dy,px,py;
int xx,yy;
REAL1 dpm1,dpm2;
int level;
float d1,d2,d;
int k;
unsigned long cc,cc00,cc01,cc10,cc11,cc2;
unsigned long _cc00,_cc01,_cc10,_cc11;
float cf0,cf1;
GB_POINT ppp;
int e;
int index;
int dirty;
int lev;
int lev_cnt,lev2;
TREE_CACHE cache;



	dw->limit_reso = r->pr64.dpm;
	dw->limit_ptr = dw->pt_list[dw->size/2];
	r->pr64.loop_no ++;
	dw->flags |= WFF_LUSTER; 
	dpm1 = r->pr64.dpm;
	dpm2 = dpm1;
	level = 0;
	d1 = 1;
	d2 = 0;

	res = -1;
	for ( i = 0 ; i < dw->size ; i ++ ) {
		if ( res < dw->pt_reso[i] )
			res = dw->pt_reso[i];
	}

/*
	if ( res > dpm1*LIMIT_RESOLUTION_RATE )
		return;
*/
	if ( dpm2 < res || res < dpm2/2 ) {
		if ( res > dpm1 ) {
			dpm2 = dpm1;
		level = 0;
		}
		else {
			level = 0;
			d = -1;
			for ( dpm2 = dpm1 ;
				res <= dpm2 &&
				level < r->pr64.max_level;
				d = dpm2,
				dpm2 /= 2,
				level ++ );
			dpm2 = d;
			level --;
		}
	}

	lock_task(luster_lock);
	lock_task(tc_lock);
	lev_cnt = 0;
	cache.cache = 0;
	lev = level;
	for ( i = 0 ; i < dw->size ; i ++ ) {
		p = dw->pt_list[i];
		if ( dw->pt_reso[i] < 0 )
			continue;
		xx = rint(p.x*r->pr64.dpm);
		yy = rint(p.y*r->pr64.dpm);
		if ( xx < 0 || yy < 0 )
			continue;
		if ( xx >= r->pr64.width[0] )
			continue;
		if ( yy >= r->pr64.height[0] )
			continue;
		if ( lev_cnt && cache.cache ) {
		int lx,ly;
			if (	cache.tl_x <= xx && xx < cache.br_x &&
				cache.tl_y <= yy && yy < cache.br_y &&
				cache.cache->level == lev ) {

				lx = (xx>>(lev))&((1<<cache.rect_logsize)-1);
				ly = (yy>>(lev))&((1<<cache.rect_logsize)-1);
				if ( lx >= cache.cache->w ) {
					cc = C_TRANSPARENT;
				}
				else if ( ly >= cache.cache->h ) {
					cc = C_TRANSPARENT;
				}
				else cc = cache.cache->pixels[lx + 
					ly*cache.cache->w];
			}
			else goto non_cache;
			lev_cnt ++;
			if ( lev_cnt >= 100 )
				lev_cnt = 0;
		}
		else {
		non_cache:
			cc = _tc_get_pixel(&r->pr64.tc,level,xx,yy,&lev);
			cache = r->pr64.tc;
			lev_cnt ++;
		}
		dw->pixels[i] = cc;
		dw->flags |= WFF_DRAW;
	}
	unlock_task(tc_lock,"draw_pixel");
	unlock_task(luster_lock,"draw_pixel");

}




int
get_point_pixel_data(
	RESOURCE *	r,
	GET_POINT_WORK * w)
{
INDICATE * in;
GB_POINT p; 
	p = w->pt;
	if ( p.x < 0 || p.y < 0 )
		return -1;
	if ( p.x >= r->pr64.width[0]/r->pr64.dpm ) {
		return -1;
	}
	if ( p.y >= r->pr64.height[0]/r->pr64.dpm ) {
		return -1;
	}
	in = _search_indicate(&r->h.entry);
	if ( in == 0 )
		return 0;
	if ( in->flags&IF_COORDINATE ) {
		in->result = p;
		in->status = IS_COORDINATE;
	}
	return 0;
}

