/**********************************************************************
 
	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	"v.h"
#include	"window.h"

#define DS_CACHE_SIZE	1000

typedef struct ds_cache {
	struct ds_cache *	next;
	struct ds_cache *	prev;
	L_CHAR *		str;
	V_STRING_PIC		pic;
} DS_CACHE;

extern GBVIEW_ENV env;
int dsc_length;
int dsc_hit,dsc_nh;

DS_CACHE dsc;

void
dsc_init()
{
	dsc.next = dsc.prev = &dsc;
}

void
dsc_insert(DS_CACHE * d)
{
	d->prev = &dsc;
	d->next = dsc.next;
	d->prev->next = d;
	d->next->prev = d;
}

void
dsc_delete(DS_CACHE * d)
{
	d->prev->next = d->next;
	d->next->prev = d->prev;
}

DS_CACHE *
dsc_search(L_CHAR * str)
{
DS_CACHE * d;
	for ( d = dsc.next ; d != &dsc ; d = d->next )
		if ( l_strcmp(str,d->str) == 0 )
			return d;
	return 0;
}

void
get_dsc(V_STRING_PIC * pp,L_CHAR * str)
{
DS_CACHE * d, * d1;
int w,h;

	if ( str == 0 ) {
		pp->pic = 0;
		return;
	}
	d = dsc_search(str);
	if ( d == 0 ) {
		d = d_alloc(sizeof(*d),2);
		get_string_pic(&d->pic,env.display,str,24,VSD_L2R);
		if ( d->pic.pic == 0 ) {
			d_f_ree(d);
			pp->pic = 0;
			return;
		}
		d->str = ll_copy_str(str,1499);
		dsc_insert(d);
		dsc_length ++;
		for ( ; dsc_length >= DS_CACHE_SIZE ; ) {
			dsc_length --;
			d1 = dsc.prev;
			dsc_delete(d1);
			d_f_ree(d1->str);
			d_f_ree(d1->pic.pic);
			d_f_ree(d1);
		}
	}
	else {
		dsc_delete(d);
		dsc_insert(d);
	}
	*pp = d->pic;
	w = d->pic.r.br.x - d->pic.r.tl.x;
	h = d->pic.r.br.y - d->pic.r.tl.y;
	pp->pic = d_alloc(w*h,234);
	memcpy(pp->pic,d->pic.pic,w*h);
}


void ds_sexp();


L_CHAR * 
get_attr(XL_SEXP * sym,L_CHAR * name)
{
XL_SYM_FIELD * sf;
	for ( sf = sym->symbol.field ; sf ; sf = sf->next ) {
		if ( l_strcmp(sf->name,name) == 0 )
			return sf->data;
	}
	return 0;
}

void
ds_centerize(PIC_LIST * pl)
{
int stp,endp,center;
PIC_ELEMENT * p;
	if ( pl->pic.list == 0 )
		return;
	stp = pl->pic.list->p.r.tl.x;
	endp = 0;
	for ( p = pl->pic.list ; p->next ; p = p->next )
		endp += p->p.width;
	endp += p->p.r.br.x;
	center = (stp + endp)/2;
	for ( p = pl->pic.list ; p ; p = p->next ) {
		p->p.r.tl.x -= center;
		p->p.r.br.x -= center;
	}
}

void
ds_pair_non_symbol(PIC_LIST * pl,XL_SEXP * info)
{
	for ( ; get_type(info) == XLT_PAIR ; info = cdr(info) )
		ds_sexp(pl,car(info));
}

void
ds_cr(PIC_LIST * pl)
{
	pl->line ++;
	pl->cursol.x = 0;
	if ( pl->pic.list )
		pl->ceil = pl->pic.list->p.r.br.y;
	else 	pl->ceil = 0;
	pl->cursol.y = pl->ceil;
}

void
ds_pair_symbol(PIC_LIST * pl,XL_SEXP * info)
{
XL_SEXP * cmd;
L_CHAR * data;
int c;
int flags;
L_CHAR * ref, * ref1;
	cmd = car(info);
	if ( l_strcmp(cmd->symbol.data,l_string(std_cm,"FONT")) == 0 ) {
		data = get_attr(cmd,l_string(std_cm,"COLOR"));
		if ( data ) {
			if ( data[0] == '#' )
				data++;
			sscanf(n_string(std_cm,data),"%x",&c);
			pl->color = COL(
				((c>>16)&0xff)<<(COL_BIT-8),
				((c>>8)&0xff)<<(COL_BIT-8),
				((c&0xff)<<(COL_BIT-8)));
		}
	}
	else if ( l_strcmp(cmd->symbol.data,l_string(std_cm,"BR")) == 0 ) {
		if ( pl->flags & PLF_CENTER )
			ds_centerize(pl);
		ds_cr(pl);
	}
	else if ( l_strcmp(cmd->symbol.data,l_string(std_cm,"CENTER")) == 0 ) {
		flags = pl->flags;
		pl->flags |= PLF_CENTER;
		ds_pair_non_symbol(pl,cdr(info));
		ds_centerize(pl);
		pl->flags = flags;
		ds_cr(pl);
	}
	else if ( l_strcmp(cmd->symbol.data,l_string(std_cm,"A")) == 0 ) {
		flags = pl->flags;
		ref = pl->ref;
		pl->flags |= PLF_ANCOR;
		ref1 = get_sf_attribute(cmd->symbol.field,
				l_string(std_cm,"HREF"));
		if ( ref1 == 0 )
			pl->ref = 0;
		else {
			pl->ref = ll_copy_str(ref1,1509);
		}
		ds_pair_non_symbol(pl,cdr(info));
		pl->flags = flags;
		if ( pl->ref )
			d_f_ree(pl->ref);
		pl->ref = ref;
	}
}

void
ds_pair(PIC_LIST * pl,XL_SEXP * info)
{
XL_SEXP * cmd;
	cmd = car(info);
	if ( get_type(cmd) == XLT_SYMBOL )
		ds_pair_symbol(pl,info);
	else	ds_pair_non_symbol(pl,info);
}

void
ds_string_data(PIC_LIST * pl,L_CHAR * data)
{
PIC_ELEMENT * pe;
int w,h;
int i;
int delta;
PIC_ELEMENT * pe1;
int flag;
	pe = d_alloc(sizeof(*pe),32);
	pe->ref = 0;
	get_dsc(&pe->p,data);
	if ( pe->p.pic == 0 ) {
		d_f_ree(pe);
		return;
	}
	w = pe->p.r.br.x - pe->p.r.tl.x;
	h = pe->p.r.br.y - pe->p.r.tl.y;
	pe->data = d_alloc(sizeof(int)*w*h,1503);
	pe->alpha = d_alloc(sizeof(char)*w*h,1504);
	pe->flags = 0;
	pe->line = pl->line;
	pe->p.r.tl.x += pl->cursol.x;
	pe->p.r.br.x += pl->cursol.x;
	pe->p.r.tl.y += pl->cursol.y;
	pe->p.r.br.y += pl->cursol.y;
	if ( pl->pic.list ) {
		delta = pl->ceil - pe->p.r.tl.y;
		if ( delta > 0 ) {
			pe->p.r.br.y += delta;
			pe->p.r.tl.y += delta;
			for ( pe1 = pl->pic.list;
					pe1 && pe1->line == pl->line;
					pe1 = pe1->next ) {
				pe1->p.r.br.y += delta;
				pe1->p.r.tl.y += delta;
			}
			pl->cursol.y += delta;
		}
		pe->next = pl->pic.list;
		pl->pic.list = pe;
	}
	else {
		pe->next = 0;
		pl->pic.list = pe;
	}
	pl->cursol.x += pe->p.width;

	flag = 0;
	for ( i = 0 ; i < w*h ; i ++ ) {
		if ( pe->p.pic[i] == 255 )
			pe->data[i] = C_TRANSPARENT;
		else {
			pe->data[i] = pl->color;
			if ( pe->p.pic[i] != 0 )
				flag = 1;
		}
		pe->alpha[i] = pe->p.pic[i];
	}
	if ( flag == 0 ) {
		d_f_ree(pe->alpha);
		pe->alpha = 0;
	}
	if ( pl->flags & PLF_ANCOR ) {
		for ( i = 0 ; i < w ; i ++ )
			pe->data[i + w*(h-1)] = pl->color;
		if ( pe->alpha )
			for ( i = 0 ; i < w ; i ++ )
				pe->alpha[i + w*(h-1)] = 0;
		pe->flags |= PLF_ANCOR;
		if ( pl->ref ) {
			pe->ref = ll_copy_str(pl->ref,1510);
		}
		else	pe->ref = 0;
	}
	d_f_ree(pe->p.pic);
	pe->p.pic = 0;
}

void
ds_integer(PIC_LIST * pl,XL_SEXP * info)
{
char * buf;
int len;
	if ( info->integer.unit ) {
		len = l_strlen(info->integer.unit)*5;
		buf = d_alloc(50 + len,2);
		sprintf(buf,"%i%s",info->integer.data,
			n_string(std_cm,info->integer.unit));
	}
	else {
		buf = d_alloc(50,2);
		sprintf(buf,"%i",info->integer.data);
	}
	ds_string_data(pl,l_string(std_cm,buf));
	d_f_ree(buf);
}

void
ds_float(PIC_LIST * pl,XL_SEXP * info)
{
char * buf;
int len;
	if ( info->floating.unit ) {
		len = l_strlen(info->floating.unit)*5;
		buf = d_alloc(100 + len,55);
		sprintf(buf,"%lf%s",info->floating.data,
			n_string(std_cm,info->floating.unit));
	}
	else {
		buf = d_alloc(100,55);
		sprintf(buf,"%lf",info->floating.data);
	}
	ds_string_data(pl,l_string(std_cm,buf));
	d_f_ree(buf);
}

void
ds_string(PIC_LIST * pl,XL_SEXP * info)
{
	ds_string_data(pl,info->string.data);
}

void
ds_sexp(PIC_LIST * pl,XL_SEXP * info)
{
	switch ( get_type(info) ) {
	case XLT_NULL:
		return;
	case XLT_PAIR:
		ds_pair(pl,info);
		return;
	case XLT_INTEGER:
		ds_integer(pl,info);
		return;
	case XLT_FLOAT:
		ds_float(pl,info);
		return;
	case XLT_STRING:
		ds_string(pl,info);
		return;
	default:
		return;
	}
}


PIC
get_onmap_string_pic(
	XL_SEXP * info)
{
PIC_LIST pl;
PIC_ELEMENT * pe;
GB_POINT pp;
	pl.pic.list = 0;
	pl.cursol.x = pl.cursol.y = 0;
	pl.ceil = 0;
	pl.flags = 0;
	pl.ref = 0;
	pl.line = 0;
	pl.color = COL(0x3ff,0,0);;

	ds_sexp(&pl,info);

	pl.pic.rect.tl.x = pl.pic.rect.tl.y = 0;
	pl.pic.rect.br.x = pl.pic.rect.br.y = -1;
	for ( pe = pl.pic.list ; pe ; pe = pe->next ) {
		if ( pl.pic.rect.tl.x > pl.pic.rect.br.x ) {
			pl.pic.rect.tl.x = pe->p.r.tl.x;
			pl.pic.rect.tl.y = pe->p.r.tl.y;
			pl.pic.rect.br.x = pe->p.r.br.x;
			pl.pic.rect.br.y = pe->p.r.br.y;
		}
		else {
			if ( pl.pic.rect.tl.x > pe->p.r.tl.x )
				pl.pic.rect.tl.x = pe->p.r.tl.x;
			if ( pl.pic.rect.tl.y > pe->p.r.tl.y )
				pl.pic.rect.tl.y = pe->p.r.tl.y;
			if ( pl.pic.rect.br.x < pe->p.r.br.x )
				pl.pic.rect.br.x = pe->p.r.br.x;
			if ( pl.pic.rect.br.y < pe->p.r.br.y )
				pl.pic.rect.br.y = pe->p.r.br.y;
		}
	}
	if ( pl.ref )
		d_f_ree(pl.ref);
	return pl.pic;
}


void
free_pic(PIC p)
{
PIC_ELEMENT * pe1, * pe2;
	for ( pe1 = p.list ; pe1 ;) {
		pe2 = pe1;
		pe1 = pe1->next;
		if ( pe2->data )
			d_f_ree(pe2->data);
		if ( pe2->ref )
			d_f_ree(pe2->ref);
		if ( pe2->alpha )
			d_f_ree(pe2->alpha);
		d_f_ree(pe2);
	}
}
