/**********************************************************************
 
	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	"memory_routine.h"
#include	"gbview.h"
#include	"xl.h"
#include	"xlerror.h"
#include	"utils.h"
#include	"gif.h"
#include	"jpeg.h"
#include	"v.h"

#define BACKGROUND_COLOR	COL(0x3ff,0x3ff,0x3ff)
#define JPEG_MIN_WIDTH		20

int last_format = 0;

void gc_gb_sexp(XL_SEXP * p);
XL_SEXP * cast_string2integer(XL_SEXP * s);

XL_SEXP *
indicate2list(INDICATE * ind)
{
XL_SEXP * ret;
	gc_push(0,0,"get_indicate_list");
	ret = 0;
	for ( ; ind ; ind = ind->next ) {
		ret = append(ret, List(List(get_string(get_url_str(&ind->url)),
							get_integer(ind->flags,0),
							get_integer(ind->status,0),
							gbpoint2list(ind->result),
							-1),
						-1));
	}
	gc_pop(ret,gc_gb_sexp);
	return ret;
}

INLINE XL_SEXP *
add2status_list(XL_SEXP * list, char * name, XL_SEXP * item)
{
	return append(list, List(List(n_get_symbol(name),item,-1),-1));
}

XL_SEXP *
gvstatus2list(GBVIEW_STATUS * gs)
{
XL_SEXP * ret;
	gc_push(0,0,"xl_gv_flame_status");
	ret = cons(n_get_symbol("status"),0);
	if ( gs->flags & SF_LAYER_NOS )
		ret = add2status_list(ret, "layer-nos",get_integer(gs->layer_nos,0));
	if ( gs->flags & SF_WIDTH )
		ret = add2status_list(ret, "width",get_integer(gs->width,0));
	if ( gs->flags & SF_HEIGHT )
		ret = add2status_list(ret, "height",get_integer(gs->height,0));
	if ( gs->flags & SF_INDICATE )
		ret = add2status_list(ret, "indicate",indicate2list(gs->indicate));

	if ( gs->flags & SF_CURRENT )
		ret = add2status_list(ret, "current",get_integer(gs->current,0));

	if ( gs->flags & (SF_RESOLUTION | SF_UNIT) )
		ret = add2status_list(ret, "resolution",
			get_floating(gs->flame_base_resolution,gs->flame_base_unit));
	if ( gs->flags & SF_MAP_TYPE )
		ret = add2status_list(ret, "map-type",
			get_integer(gs->flame_base_display_map_type,0));
	if ( gs->flags & SF_PP_RADIUS )
		ret = add2status_list(ret, "pp-radius",get_floating(gs->pp_radius,0));
	if ( gs->flags & SF_REFERENCE )
		ret = add2status_list(ret, "reference",get_string(gs->reference));
	gc_pop(ret,gc_gb_sexp);
	return ret;
}

INDICATE *
list2indicate(XL_SEXP * list)
{
XL_SEXP * header, * url, * flags, * status, * result;
INDICATE * ret = 0, * ni = 0;
	for ( ; get_type(list) == XLT_PAIR ; list = cdr(list) ) {

		if ( ni )
			ni = ni->next = d_alloc(sizeof(INDICATE));
		else
			ret = ni = d_alloc(sizeof(INDICATE));
		
		url = get_el(car(list), 0);
		flags = get_el(car(list), 1);
		status = get_el(car(list), 2);
		result = get_el(car(list), 3);
		if (	   get_type(url)    != XLT_STRING
				|| get_type(flags)  != XLT_INTEGER
				|| get_type(status) != XLT_INTEGER
				|| get_type(result) != XLT_PAIR )
			goto type_missmatch;
		get_url2(&ni->url,url->string.data);
		ni->flags = flags->integer.data;
		ni->status = status->integer.data;
		ni->result = list2gbpoint(0,result);
		ni->res_object = 0;
		ni->next = 0;
	}
	return ret;
	
type_missmatch:
	print_sexp(s_stdout,list,0);
	printf("\n");
	er_panic("list2indicate");
	return 0;
}

int
list2gvstatus(XL_SEXP * list, GBVIEW_STATUS *gs)
{
XL_SEXP * header, * data;
char * n_header;

	wf_init_status(gs);
	gc_push(0,0,"list2gvstatus");

	if ( get_type(list) != XLT_PAIR )
		goto type_missmatch;
	header = car(list);
	if ( get_type(header) != XLT_SYMBOL ||
			l_strcmp(header->symbol.data, l_string(std_cm,"status")) != 0 )
		goto type_missmatch;
	
	list = cdr(list);
	for ( ; get_type(list) == XLT_PAIR ; list = cdr(list) ) {
		header = car(car(list));
		data = car(cdr(car(list)));
		if ( get_type(header) != XLT_SYMBOL )
			goto type_missmatch;
		n_header = ln_copy_str(std_cm,header->symbol.data);
		if ( strcmp(n_header,"layer-nos") == 0 ) {
			if ( get_type(data) != XLT_INTEGER )
				goto type_missmatch;
			gs->flags |= SF_LAYER_NOS;
			gs->layer_nos = data->integer.data;
		}
		else if ( strcmp(n_header,"width") == 0 ) {
			if ( get_type(data) != XLT_INTEGER )
				goto type_missmatch;
			gs->flags |= SF_WIDTH;
			gs->width = data->integer.data;
		}
		else if ( strcmp(n_header,"height") == 0 ) {
			if ( get_type(data) != XLT_INTEGER )
				goto type_missmatch;
			gs->flags |= SF_HEIGHT;
			gs->height = data->integer.data;
		}
		else if ( strcmp(n_header,"indicate") == 0 ) {
			if ( get_type(data) != XLT_PAIR && get_type(data) != XLT_NULL )
				goto type_missmatch;
			gs->flags |= SF_INDICATE;
			gs->indicate = list2indicate(data);
		}

		else if ( strcmp(n_header,"current") == 0 ) {
			if ( get_type(data) != XLT_INTEGER )
				goto type_missmatch;
			gs->flags |= SF_CURRENT;
			gs->current = data->integer.data;
		}
		else if ( strcmp(n_header,"resolution") == 0 ) {
			if ( get_type(data) != XLT_FLOAT )
				goto type_missmatch;
			gs->flags |= SF_RESOLUTION | SF_UNIT;
			gs->flame_base_resolution = data->floating.data;
			gs->flame_base_unit = ll_copy_str(data->floating.unit);
		}
		else if ( strcmp(n_header,"map-type") == 0 ) {
			if ( get_type(data) != XLT_INTEGER )
				goto type_missmatch;
			gs->flags |= SF_MAP_TYPE;
			gs->flame_base_display_map_type = data->integer.data;
		}
		else if ( strcmp(n_header,"pp-radius") == 0 ) {
			if ( get_type(data) != XLT_FLOAT )
				goto type_missmatch;
			gs->flags |= SF_PP_RADIUS;
			gs->pp_radius = data->floating.data;
		}
		else if ( strcmp(n_header,"reference") == 0 ) {
			if ( get_type(data) != XLT_STRING )
				goto type_missmatch;
			gs->flags |= SF_REFERENCE;
			gs->reference = ll_copy_str(data->string.data);
		}
		
		d_f_ree(n_header);
	}
	
	gc_pop(0,0);
	return 0;
	
type_missmatch:
	fflush(stdout);
printf("\n\n");
	s_printf(s_stdout,"invalid list of gvstatus ");
	print_sexp(s_stdout,list,0);
	s_printf(s_stdout,"\n");
	return -1;
}


void
copy_from_gbp(GBVIEW_PLANE * p, unsigned long * dest)
{
unsigned long * src;
unsigned long c,i,i_max;

	i_max = (p->r.r.br.x - p->r.r.tl.x) * (p->r.r.br.y - p->r.r.tl.y);
	src = p->r.plane;

	for ( i = 0 ; i < i_max ; i++ ) {
		c = *src ++;
		if ( c & C_TRANSPARENT )
			*dest ++ = BACKGROUND_COLOR;
		else
			*dest ++ = c;
	}
}

#define GB2RGB4_D(f,n)	\
	(((f)>>COL_BIT*(n))&COL_MASK)>>(COL_BIT-8)

#define GB2RGB4(rgb4,f)	\
	{							\
		(rgb4).r	= GB2RGB4_D(f,0);	\
		(rgb4).g	= GB2RGB4_D(f,1);	\
		(rgb4).b	= GB2RGB4_D(f,2);	\
	}

void
copy_from_gbp_rgb4(GBVIEW_PLANE * p, RGB4 * dest)
{
unsigned long * src;
unsigned long c,i,i_max;
RGB4 bg;

	i_max = (p->r.r.br.x - p->r.r.tl.x) * (p->r.r.br.y - p->r.r.tl.y);
	src = p->r.plane;
	GB2RGB4(bg, BACKGROUND_COLOR);

	for ( i = 0 ; i < i_max ; i++ ) {
		c = *src ++;
		if ( c & C_TRANSPARENT )
			*dest ++ = bg;
		else {
			GB2RGB4(*dest, c);
			dest ++;
		}
	}
}

#define RGB42GB(r,g,b)	COL((r)<<(COL_BIT-8),(g)<<(COL_BIT-8),(b)<<(COL_BIT-8))

void
copy_to_gbp_rgb4(GBVIEW_PLANE * gbp, RGB4 * rgb4, int size)
{
unsigned long * dst, i;
	dst = gbp->r.plane = d_alloc(size * sizeof(long));
	for ( i = 0 ; i < size ; i++ ) {
		* dst ++ = 	RGB42GB(rgb4[i].r, rgb4[i].g, rgb4[i].b);
	}
}

XL_SEXP *
_gbp2list(GBVIEW_PLANE  * gbp, L_CHAR * format, int jpeg_quality)
{
int x = gbp->r.r.tl.x;
int y = gbp->r.r.tl.y;
int w = gbp->r.r.br.x - x;
int h = gbp->r.r.br.y - y;
int len, f, seq = gbp->h.seq;
XL_SEXP * res_format;
unsigned char * work, * ret;
RGB4 * rgb4;
GIF_ENV ge;

	if ( gbp->h.type != GPT_REDRAW )
		return 0;
	
	if ( format != 0 ) {
		if ( l_strcmp(format,l_string(std_cm,"raw")) == 0 )
			last_format = f = 0;
		else if ( l_strcmp(format,l_string(std_cm,"gif")) == 0 )
			last_format = f = 1;
		else if ( l_strcmp(format,l_string(std_cm,"jpeg")) == 0 )
			last_format = f = 2;
		else
			f = -1;
	}
	else
		f = last_format;
	
	/* limit JPEG height & width minimum */
	if ( f == 2 && w < JPEG_MIN_WIDTH || h < JPEG_MIN_WIDTH ) {
		f = 1; // CHANGE FORMAT TO GIF
	}
	
	switch( f ) {
	case 0:
		len = w*h*sizeof(long);
		ret = mmalloc(len,gc_text);
printf("raw - size : %d\n",len);
		copy_from_gbp(gbp,(unsigned long*)ret);
		res_format = n_get_string("raw");
		break;
	
	case 1:
		rgb4 = d_alloc(w*h*sizeof(RGB4));
		copy_from_gbp_rgb4(gbp,rgb4);
		work = gif_compress(&len,&ge,0,(unsigned int *)rgb4,w,h);
printf("gif - size : %d\n",len);
		d_f_ree(rgb4);
		ret = mmalloc(len,gc_text);
		memcpy(ret, work, len);
		d_f_ree(work);
		res_format = n_get_string("gif");
		break;


	case 2:
		rgb4 = d_alloc(w*h*sizeof(RGB4));
		copy_from_gbp_rgb4(gbp,rgb4);
		work = jpeg_compress(&len,(unsigned char *)rgb4,w,h,jpeg_quality);
printf("jpeg - size : %d\n",len);
		d_f_ree(rgb4);
		ret = mmalloc(len,gc_text);
		memcpy(ret, work, len);
		free(work);
		res_format = n_get_string("jpeg");
		break;
	
	default:
		return 0;
	}

	return List(get_integer(seq, 0),
			List(get_integer(x,0),get_integer(y,0),
				get_integer(x+w,0),get_integer(y+h,0),-1),
			res_format,
			get_raw_set_data(ret, len),
			-1);
}

XL_SEXP *
gbp2list(GBVIEW_PLANE * gbp, XL_SYM_FIELD * sf)
{
L_CHAR * format;
L_CHAR * q;
int quality;
	format = get_sf_attribute(sf,l_string(std_cm,"format"));
	q = get_sf_attribute(sf,l_string(std_cm,"quality"));		
	if ( q == 0 ) {
		quality = 80;
	} else {
		quality = cast_string2integer(get_string(q))->integer.data;
		if ( quality <= 0 || quality > 100 ) {
			quality = 80;
		}
	}
	return _gbp2list(gbp, format, quality);
}

int
list2gbp(XL_SEXP * list, GBVIEW_PLANE * gbp)
{
XL_SEXP * seq,* rect,* data;
XL_SEXP * x1,* y1,* x2,* y2, *f;
L_CHAR * format;
int ret,x,y,w,h,len;
	seq = get_el(list,0);
	rect = get_el(list,1);
	f = get_el(list,2);
	data = get_el(list,3);
	if ( get_type(seq) != XLT_INTEGER || get_type(rect) != XLT_PAIR
			|| get_type(f) != XLT_STRING || get_type(data) != XLT_RAW )
		goto err;
	x1 = get_el(rect,0);
	y1 = get_el(rect,1);
	x2 = get_el(rect,2);
	y2 = get_el(rect,3);
	if ( get_type(x1) != XLT_INTEGER || get_type(y1) != XLT_INTEGER
			|| get_type(x2) != XLT_INTEGER || get_type(y2) != XLT_INTEGER )
		goto err;

	x = x1->integer.data;
	y = y1->integer.data;
	w = x2->integer.data - x;
	h = y2->integer.data - y;
	format = f->string.data;
	
	gbp->h.type = GPT_REDRAW;
	gbp->h.seq = seq->integer.data;
	gbp->r.r.tl.x = x;
	gbp->r.r.tl.y = y;
	gbp->r.r.br.x = x+w;
	gbp->r.r.br.y = y+h;
	
	if ( l_strcmp(format,l_string(std_cm,"raw")) == 0 ) {
		len = w*h*sizeof(long);
		if ( len > data->raw.size )
			goto err;
		gbp->r.plane = d_alloc(len);
		memcpy(gbp->r.plane,data->raw.data,w*h*sizeof(long));
		return seq->integer.data;
	}
	if ( l_strcmp(format,l_string(std_cm,"gif")) == 0 ) {
	GIF_ENV ge;
	unsigned int * rgb4;
		rgb4 = gif_uncompress(&w,&h,&ge,data->raw.data,data->raw.size);
		if ( rgb4 == 0 )
			return 0;
		copy_to_gbp_rgb4(gbp,(RGB4*)rgb4,w*h);
		d_f_ree(rgb4);
		return seq->integer.data;
	}
	if ( l_strcmp(format,l_string(std_cm,"jpeg")) == 0 ) {
	GIF_ENV ge;
	unsigned char * rgb4;
		rgb4 = jpeg_uncompress(&w,&h,data->raw.data,data->raw.size);
		if ( rgb4 == 0 )
			return 0;
		copy_to_gbp_rgb4(gbp,(RGB4*)rgb4,w*h);
		free(rgb4);
		return seq->integer.data;
	}
	
err:
	return 0;
}
