/**********************************************************************
 
	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	"task.h"
#include	"avt.h"
#include	"gbview.h"
#include	<string.h>
#include	"memory_debug.h"
#include	"xl.h"
#include	"gif.h"
#include	"xlerror.h"
#include	"lock_level.h"
#include	"pri_level.h"
#include	"win_flame.h"

#define RI_COUNT_MAX	100

typedef struct raw_image {
	RING_TYPE		h;
	L_CHAR *		path;
	int			width;
	int			height;
	void *			data;
	void *			ndata;
	int			ndata_len;
	int			err;
#define RIE_OK		0
#define RIE_REQ		1
#define RIE_ERR		-1
	WF_ID			request;
	GBVIEW_FLAME *		request_gf;
	struct raw_image *	next;
} RAW_IMAGE;


RING_TYPE ri_root;
AVT_NODE * avt_ri_root;
SEM	ri_lock;
int ri_count;

void fetch_task();

void
init_ri()
{
	ri_lock = new_lock(LL_RAW_IMAGE);
	INIT_RING(&ri_root);
	ri_count = 0;
	avt_ri_root = 0;
	create_task(fetch_task,0,PRI_FETCH_STRONG);
}


int ri_cmp(RAW_IMAGE * ri1,RAW_IMAGE * ri2)
{
	return l_strcmp(ri1->path,ri2->path);
}

void
_insert_ri(GBVIEW_FLAME * gf,WF_ID req,L_CHAR * path,void * data,int w,int h)
{
AVT_NODE * a1, * a2;
RAW_IMAGE * ri;
int size;
	size = w*h*sizeof(int);
	ri = d_alloc(sizeof(*ri));
	ri->path = ll_copy_str(path);
	ri->width = w;
	ri->height = h;
	ri->err = RIE_REQ;
	ri->request = req;
	ri->request_gf = gf;
	ri->ndata = 0;
	ri->ndata_len = 0;
	if ( data ) {
		ri->data = d_alloc(size);
		memcpy(ri->data,data,size);
	}
	else	ri->data = 0;
	a1 = d_alloc(sizeof(*a1));
	a1->data = ri;
	a2 = avt_insert(&avt_ri_root,a1,ri_cmp);
	if ( a2 == a1 ) {
		INSERT_RING(&ri_root,&ri->h);
		ri_count ++;
	}
	else {
		if ( ri->data )
			d_f_ree(ri->data);
		if ( ri->ndata )
			d_f_ree(ri->ndata);
		d_f_ree(ri->path);
		d_f_ree(ri);
		d_f_ree(a1);
		ri = a2->data;
		DELETE_RING(&ri->h);
		INSERT_RING(&ri_root,&ri->h);
	}
}


void
delete_ri(L_CHAR * path)
{
AVT_NODE * a1;
RAW_IMAGE ri;
RAW_IMAGE * rip;
	lock_task(ri_lock);
	ri.path = path;
	a1 = avt_delete(&avt_ri_root,&ri,ri_cmp);
	if ( a1 == 0 ) {
		unlock_task(ri_lock,"delete_ri");
		return;
	}
	rip = a1->data;
	DELETE_RING(&rip->h);
	d_f_ree(rip->path);
	if ( rip->data )
		d_f_ree(rip->data);
	if ( rip->ndata )
		d_f_ree(rip->ndata);
	d_f_ree(rip);
	ri_count --;
	unlock_task(ri_lock,"delete_ri");
}

RAW_IMAGE *
_search_ri(L_CHAR * path)
{
RAW_IMAGE ri, * rip;
AVT_NODE * a1;

	ri.path = path;
	a1 = avt_search(avt_ri_root,&ri,ri_cmp);
	if ( a1 == 0 ) {
		return 0;
	}
	rip = a1->data;
	DELETE_RING(&rip->h);
	INSERT_RING(&ri_root,&rip->h);
	return rip;
}

void *
_search_ri_org(int * len,L_CHAR * path)
{
RAW_IMAGE ri, * rip;
AVT_NODE * a1;

retry:
	ri.path = path;
	a1 = avt_search(avt_ri_root,&ri,ri_cmp);
	if ( a1 == 0 ) {
		*len = -1;
		return 0;
	}
	rip = a1->data;
	DELETE_RING(&rip->h);
	INSERT_RING(&ri_root,&rip->h);
	if ( rip->err > 0 ) {
		sleep_task((int)rip,ri_lock);
		lock_task(ri_lock);
		goto retry;
	}
	if ( rip->err < 0 ) {
		*len = -1;
		return 0;
	}
	*len = rip->ndata_len;
	return rip->ndata;
}



void *
get_data(int * size,XL_SEXP * data)
{
void * ret;
char * ptr, * ptr2;
XL_SEXP * a, * b;
int s,i;

	a = data;
	s = 0;
	for ( ; get_type(a) ; a = cdr(a) ) {
		b = car(a);
		if ( get_type(b) != XLT_RAW )
			continue;
		s += b->raw.size;
	}
	ret = d_alloc(s);

	ptr = ret;
	a = data;
	for ( ; get_type(a) ; a = cdr(a) ) {
		b = car(a);
		if ( get_type(b) != XLT_RAW )
			continue;
		memcpy(ptr,b->raw.data,b->raw.size);
		ptr += b->raw.size;
	}
	*size = s;
	return ret;
}

void *
get_from_network(XL_SEXP ** err,int ses,int * size,L_CHAR * path)
{
XL_SEXP * ret;
URL u;
void gc_gb_sexp();
L_CHAR * f;

	get_url2(&u,path);
	gc_push(0,0,"raw_image");
printf("request\n");
	ret = remote_session(
		gblisp_top_env0,
		ses,
		&u,
		l_string(std_cm,"standard"),
		l_string(std_cm,"user"),
		l_string(std_cm,"Get"),
		List(List(get_symbol(l_string(std_cm,"Get")),
			get_string(f=get_url_filepath(&u)),
			-1),
			-1),
		0,0,0);
	d_f_ree(f);
	gc_pop(ret,gc_gb_sexp);
	switch ( get_type(ret) ) {
	case XLT_ERROR:
		ss_printf("raw Image error %ls ",path);
		print_sexp(s_stdout,ret,0);
		ss_printf("\n");
		*err = ret;
		return 0;
	case XLT_PAIR:
		return get_data(size,ret);
	default:
		printf("raw image type missmatch\n");
		return 0;
	}
	return 0;
}


void *
decompress(XL_SEXP ** err,int * w,int * h,
	void * data,int src_size,
	L_CHAR * path)
{
int pref;
void * ret;
GIF_ENV ge;
int * dt;
int i;
int c,dd,dif;
RGB4 * rgb;
int flag;
	for ( pref = l_strlen(path)-1 ; pref >= 0 ; pref -- )
		if ( path[pref] == '.' )
			break;
	if ( pref < 0 )
		goto unsupport;
	if ( l_strcmp(&path[pref],l_string(std_cm,".gif")) == 0 ) {
		ret = gif_uncompress(
			w,h,
			&ge,
			data,
			src_size);
		if ( ret == 0 )
			goto unsupport;
		dt = ret;
		for ( i = 0 ; i < (*w)*(*h) ; i ++ ) {
			rgb = (RGB4*)dt;
			c = (rgb->r<<(COL_BIT-8))|
				(rgb->g<<(COL_BIT*2-8))|
				(rgb->b<<(COL_BIT*3-8));
			*dt = c;
			dt ++;
		}
	}
	else {
		goto unsupport;
	}
	return ret;
unsupport:
	*w = *h = 0;
	*err = get_error(
		0,0,
		XLE_PROTO_UNDEF_RESOURCE,
		l_string(std_cm,"raw_image"),
		List(n_get_string("undefined the image data"),
			get_string(path),
			-1));
	return 0;
}

int
fetch_pri()
{
	return -1;
}

void
fetch_task()
{
RAW_IMAGE * ri;
void * data,* ndata;
int w,h;
int ses;
XL_SEXP * err;
XL_INTERPRETER * xli;
int s;
	xli = new_xl_interpreter();
	xli->a_type = XLA_SELF;
	setup_i(xli);

	ses = open_session(SEST_OPTIMIZE);
	for ( ; ; ) {
		gc_push(0,0,"fetch_task");
		lock_task(ri_lock);
		ri = R_NEXT(RAW_IMAGE*,&ri_root);
		for ( ; ri != (RAW_IMAGE*)&ri_root ;
				ri = R_NEXT(RAW_IMAGE*,&ri->h) )
			if ( ri->err > 0 )
				goto ok;
		gc_pop(0,0);
		sleep_task((int)&ri_root,ri_lock);
		continue;
	ok:
		unlock_task(ri_lock,"fetch");
		err = 0;
		ndata = get_from_network(&err,ses,&s,ri->path);
		if ( ndata == 0 ) {
			lock_task(ri_lock);
			ri->err = RIE_ERR;
			wakeup_task((int)ri);
			unlock_task(ri_lock,"get_ri");

			gc_pop(0,0);
			continue;
		}
		data = decompress(&err,&w,&h,ndata,s,ri->path);
		if ( data == 0 ) {
			d_f_ree(ndata);

			lock_task(ri_lock);
			ri->err = RIE_ERR;
			wakeup_task((int)ri);
			unlock_task(ri_lock,"get_ri");

			fprintf(stderr,"decompress error");
			print_sexp(s_stderr,err,0);
			fprintf(stderr,"\n");
			gc_pop(0,0);
			continue;
		}
		gc_pop(0,0);
		lock_task(ri_lock);
		ri->ndata = ndata;
		ri->ndata_len = s;
		ri->data = data;
		ri->width = w;
		ri->height = h;
		ri->err = RIE_OK;
		wakeup_task((int)ri);
		unlock_task(ri_lock,"get_ri");
		if ( ri->request ) {
			win_flame_dirty(ri->request_gf,ri->request,0,
				WFF_PLOT_DIRTY,0);
			ri->request = 0;
			ri->request_gf = 0;
		}

		lock_task(ri_lock);
		for ( ; ri_count >= RI_COUNT_MAX ; ) {
			ri = R_PREV(RAW_IMAGE*,&ri_root);
			unlock_task(ri_lock,"get_ri");
			delete_ri(ri->path);
			lock_task(ri_lock);
		}
		unlock_task(ri_lock,"get_ri");
	}
}


void *
get_ri(int * w,int * h,GBVIEW_FLAME * gf,WF_ID req,L_CHAR * path)
{
RAW_IMAGE * rip;
void * data;
RAW_IMAGE * ri;
	lock_task(ri_lock);
	rip = _search_ri(path);
	if ( rip == 0 ) {
		_insert_ri(gf,req,path,0,0,0);
		wakeup_task((int)&ri_root);
		data = 0;
		goto end;
	}
	if ( rip->err != RIE_OK ) {
		data = 0;
		goto end;
	}
	data = rip->data;
	*w = rip->width;
	*h = rip->height;
end:
	unlock_task(ri_lock,"get_ri");
	return data;
}



void *
search_ri_org(int * len,L_CHAR * path)
{
void * ret;
int w,h;
RAW_IMAGE * rip;
	get_ri(&w,&h,0,0,path);
	lock_task(ri_lock);
	ret = _search_ri_org(len,path);
	unlock_task(ri_lock,"search_ri_org");
	return ret;
}
