/* ======================================================= *
 * Copyright 1998-2005 Stephen C. Grubb                    *
 * http://ploticus.sourceforge.net                         *
 * Covered by GPL; see the file ./Copyright for details.   *
 * ======================================================= */

/* PROC LEGEND - Render an automatic legend. Plotting procs supply legend entries */

#include "pl.h"

#define MAXLEGENT 80            /* max # of legend entries */
#define MAXLEGTEXT 2000         /* max amount of legend text, including labels
                                        and details attributes */

#define DOWN 0
#define ACROSS 1

extern int PLG_init_bb2(), PLG_get_bb2();
 
static int NLE = 0;
static int LEavail = 0;
static int LEtype[MAXLEGENT];
static int LEparm1[MAXLEGENT];
static int LEparm2[MAXLEGENT];
static int LEparm3[MAXLEGENT];
static int LElabel[MAXLEGENT];
static int LEtag[MAXLEGENT];
static char Ltext[MAXLEGTEXT];

/* ================================ */
int
PLP_legend_initstatic()
{
NLE = 0;
LEavail = 0;
return(0);
}

/* ================================ */
int
PLP_legend()
{
int i;
char attr[NAMEMAXLEN], val[256];
char *line, *lineval;
int nt, lvp;
int first;

int align;
double adjx, adjy;

char buf[256];
double x, y;
char format;
int outline;
int nlines, maxlen;
double yy;
char textdetails[256];
int reverseorder;
int j;
double seglen;
double hsep, msep;
int do_outline;
int colortext;
int specifyorder;
int ix, ixx, k;
double swatchsize;
int noclear;
char holdstdcolor[COLORLEN];
char color[COLORLEN];
int buflen;
char url[MAXPATH];
char *s;
char symcode[80];
double radius;
double extent;
double startx, starty;
double sampwidth, colchunksep, colbreak;
double rowchunksep;
int nlinesym, maxtwidth, maxthi;
int wraplen;
double orig_x, orig_y, bx1, by1, bx2, by2;
int dobox, bstate;
char frame[128];
char backcolor[COLORLEN];
double bmx1, bmy1, bmx2, bmy2;
char title[128], titledetails[128];
double titx, tity;

TDH_errprog( "pl proc legend" );

/* initialize */
x = -9999.0;
y = -9999.0;
format = DOWN;
strcpy( textdetails, "" );
reverseorder = 0;
seglen = 0.5;
msep = 0.0;
hsep = 0.3; /* was 1.2; changed scg 8/12/05 */
do_outline = 0;
colortext = 0;
strcpy( PL_bigbuf, "" );
specifyorder = 0;
ix = 0;
swatchsize = 0.1;
noclear = 0;
extent = -1.0;
colchunksep = 0.35;
rowchunksep = 0.1;
nlinesym = 2;
wraplen = 0;
dobox = 0;
strcpy( frame, "no" );
strcpy( backcolor, Ecurbkcolor );
bmx1 = bmy1 = bmx2 = bmy2 = 0.08;
strcpy( title, "" );
strcpy( titledetails, "" );

/* get attributes.. */
first = 1;
while( 1 ) {
	line = getnextattr( first, attr, val, &lvp, &nt );
	if( line == NULL ) break;
	first = 0;
	lineval = &line[lvp];

	if( stricmp( attr, "location" )==0 ) {
		/* note: location x = beginning of LABEL; y = top */
		if( lineval[0] != '\0' ) getcoords( "location", lineval, &x, &y );
		}
	else if( stricmp( attr, "textdetails" )==0 ) strcpy( textdetails, lineval );
	else if( stricmp( attr, "seglen" )==0 ) seglen = atof( val );
	else if( stricmp( attr, "sep" )==0 ) ; /* superseded by the 'separation' attribute */
	else if( stricmp( attr, "separation" )==0 ) {
		hsep = atof( val );
		msep = atof( val );
		if( PLS.usingcm ) { hsep /= 2.54; msep /= 2.54; }
		}
	else if( stricmp( attr, "colortext" )==0 ) {
		if( strnicmp( val, YESANS, 1 )==0 ) colortext = 1;
		else colortext = 0;
		}
		
	else if( stricmp( attr, "outlinecolors" )==0 ) {
		if( strnicmp( val, YESANS, 1 )==0 ) do_outline = 1;
		else do_outline = 0;
		}
	else if( stricmp( attr, "noclear" )==0 ) {
		if( strnicmp( val, YESANS, 1 )==0 ) noclear = 1;
		else noclear = 0;
		}
	else if( stricmp( attr, "format" )==0 ) {
		format = tolower( val[0] );
		if( format == 'd' || format == 'm' ) format = DOWN;
		else if( format == 'a' || format == 's' ) format = ACROSS;
		}
	else if( stricmp( attr, "specifyorder" )==0 ) {
		getmultiline( "specifyorder", lineval, MAXBIGBUF, PL_bigbuf );
		specifyorder = 1;
		}

	else if( stricmp( attr, "swatchsize" )==0 ) {
		if( val[0] != '\0' ) {
			swatchsize = atof( val );
			if( PLS.usingcm ) swatchsize /= 2.54;
			}
		else swatchsize = 0.1;
		}

	else if( stricmp( attr, "reverseorder" )==0 ) {
		if( strnicmp( val, YESANS, 1 )==0 ) reverseorder = 1;
		else reverseorder = 0;
		}
	else if( stricmp( attr, "reset" )==0 ) {
		NLE = 0;
		return( 0 );
		}

	/* the following attributes added for 2.32 - scg 8/12/05 ....... */

	else if( stricmp( attr, "extent" )==0 ) {  
		extent = atof( val ); 
		if( PLS.usingcm ) extent /= 2.54;
		}
	else if( stricmp( attr, "chunksep" )==0 ) {  /* additional amount of separation between column chunks or row chunks */
		colchunksep = atof( val );
		rowchunksep = atof( val );
		if( PLS.usingcm ) { colchunksep /= 2.54; rowchunksep /= 2.54; }
		}
	else if( stricmp( attr, "nlinesym" )== 0 ) {  /* 1 2 or 0 */
		nlinesym = atoi( val );
		if( nlinesym > 2 ) nlinesym = 2;
		}
	else if( stricmp( attr, "wraplen" )==0 ) wraplen = atoi( val );

	else if( stricmp( attr, "frame" )==0 ) {
		dobox = 1;
		strcpy( frame, lineval );
		}
	else if( stricmp( attr, "backcolor" )==0 ) {
		dobox = 1;
		strcpy( backcolor, val );
		}
	else if( stricmp( attr, "boxmargin" )==0 ) {
		char foo1[40], foo2[40];
		nt = sscanf( lineval, "%s %s", foo1, foo2 );
		if( nt == 1 ) {
			if( PLS.usingcm ) bmx1 /= 2.54;
			bmy1 = bmx2 = bmy2 = bmx1;
			}
		else PL_getbox( "boxmargin", lineval, &bmx1, &bmy1, &bmx2, &bmy2 );
		}
	else if( stricmp( attr, "title" )==0 ) { 
		dobox = 1;
		strcpy( title, lineval );
		}
	else if( stricmp( attr, "titledetails" )==0 ) strcpy( titledetails, lineval );

	else Eerr( 1, "attribute not recognized", attr );
	}


if( NLE < 1 ) return( 0 ); /* silent is better here ... scg 5/5/04 */



/*********** now do the plotting work.. **********/

/* box init stuff.. */
if( dobox ) {
	bstate = 0;
	PLG_init_bb2();
	Esquelch( "on" );
	}


/* default location */
if( x < -9000.0 ) { 
	if( scalebeenset() ) { x = EXhi - 1.5; y = EYhi - 0.1; }
	else { x = 6.0; y = 2.0; }
	}

orig_x = x; orig_y = y;

RENDER:

ix = 0;
buflen = strlen( PL_bigbuf );

startx = x; starty = y;
if( format == DOWN ) {
	if( extent < 0.0 ) colbreak = 0.0; /* always at y = 0 */
	else colbreak = starty - extent;  /* extent was specified.. do it */
	}
maxtwidth = 0; /* keep track of the max text width in column */
maxthi = 0;  /* keep track of the max text #lines in column (since individual entries can wrap with "\n" */


textdet( "textdetails", textdetails, &align, &adjx, &adjy, -2, "R", 1.0 );
y -= Ecurtextheight; 
for( i = 0; i < NLE; i++ ) {

	/* fprintf( stderr, "%d|%s|%s|%s\n", LEtype[i], &Ltext[LElabel[i]], &Ltext[LEparm1[i]], &Ltext[LEparm2[i]] ); */

	if( specifyorder ) {
		/* get next line in orderspec.. */
		NEXTORDERLINE:
		GL_getchunk( buf, PL_bigbuf, &ix, "\n" );
		if( ix >= buflen ) break;

		/* now search for matching entry.. */
		for( k = 0; k < NLE; k++ ) {
			if( strnicmp( buf, &Ltext[LElabel[k]], strlen(buf) )==0) { 
				j = k;
				break;
				}
			}
		if( k == NLE ) {
			/* following changed, scg 2/27/02 */
			/* Eerr( 2894, "No legend entry matched", buf ); */ 
			goto NEXTORDERLINE;
			/* continue; */
			}
		}
	else if( reverseorder ) j = ((NLE-1)-i);
	else j = i;
		
	yy = y+(Ecurtextheight*0.4);

	/* draw swatch(es), depending on type */
	if( LEtype[j] == LEGEND_COLOR ) {
		sampwidth = swatchsize+0.1;
		if( format == ACROSS && x > startx ) x += sampwidth;
		sscanf( &Ltext[LEparm1[j]], "%s", color );
		if( strcmp( color, backcolor ) ==0 ) outline = 1;
		else outline = do_outline;
		if( outline ) { Elinetype( 0, 0.5, 1.0 ); Ecolor( Estandard_color ); }
		Ecblock( x-(swatchsize+0.1), y, x-0.1, y+swatchsize, color, outline );
		}
	else if( LEtype[j] == LEGEND_LINE ) {
		sampwidth = seglen+0.1;
		if( format == ACROSS && x > startx ) x += sampwidth;
		linedet( &Ltext[LElabel[j]], &Ltext[LEparm1[j]], 1.0 );
		Emov( x-(seglen+0.1), yy );
		Elin( x-0.1, yy );
		}
	else if( LEtype[j] == LEGEND_LINEMARK ) {  /* tiny line marks that can be used in scatterplots */
		sampwidth = 0.2;
		if( format == ACROSS && x > startx ) x += sampwidth;
		linedet( &Ltext[LElabel[j]], &Ltext[LEparm1[j]], 1.0 );
		if( strcmp( &Ltext[LEparm2[j]], "v" )==0 ) { Emov( x-0.15, yy+0.05 ); Elin( x-0.15, yy-0.05 ); } /* vertical */
		else	{  Emov( x-0.2, yy ); Elin( x-0.1, yy ); } /* horizontal */
		}
	else if( LEtype[j] == LEGEND_SYMBOL ) {
		symdet( "symbol", &Ltext[LEparm1[j]], symcode, &radius );
		sampwidth = 0.17 + radius;
		if( format == ACROSS && x > startx ) x += sampwidth;
		Emark( x-0.17, yy, symcode, radius );
		}
	else if( LEtype[j] == LEGEND_TEXT ) {
		sampwidth = 0.8; /* just a guess */
		if( format == ACROSS && x > startx ) x += sampwidth;
		/* parm1 is text, parm2 is textdetails */
		textdet( &Ltext[LElabel[j]], &Ltext[LEparm2[j]], &align, &adjx, &adjy, -2, "R", 1.0 );
		Emov( x-0.1, y );
		Erightjust( &Ltext[LEparm1[j]] ); 
		}
	else if( LEtype[j] == (LEGEND_LINE + LEGEND_SYMBOL) ) {
		sampwidth = seglen+0.1;
		if( format == ACROSS && x > startx ) x += sampwidth;
		/* parm1 is linedetails, parm2 is symboldetails */
		linedet( &Ltext[LElabel[j]], &Ltext[LEparm1[j]], 1.0 );
		Emov( x-(seglen+0.1), yy );
		Elin( x-0.1, yy );
		symdet( "symbol", &Ltext[LEparm2[j]], symcode, &radius );
		if( nlinesym == 1 ) Emark( x-((seglen/2.0)+0.1), yy, symcode, radius );
		else if ( nlinesym == 2 ) {
			Emark( x-(seglen), yy, symcode, radius );
			Emark( x-0.18, yy, symcode, radius );
			}
		}
	else if( LEtype[j] == (LEGEND_TEXT + LEGEND_SYMBOL) ) {
		sampwidth = 0.8; /* just a guess */
		if( format == ACROSS && x > startx ) x += sampwidth;
		/* parm1 is text, parm2 is textdetails, parm3 is symboldetails */
		symdet( "symbol", &Ltext[LEparm3[j]], symcode, &radius );
		Emark( x-0.17, yy, symcode, radius );
		textdet( &Ltext[LElabel[j]], &Ltext[LEparm2[j]], &align, &adjx, &adjy, -2, "R", 1.0 );
		Emov( x-0.17, y+(Ecurtextheight*0.2) );
		Ecentext( &Ltext[LEparm1[j]] ); 
		}

	s = &Ltext[ LElabel[ j ]];

	/* check for embedded url.. */
	url[0] = '\0';
	if( strnicmp( s, "url:", 4 )==0 ) {
		ixx = 0;
		strcpy( url, GL_getok( &s[4], &ixx ) );
		s = &Ltext[ LElabel[j] + ixx + 4 + 1 ];
		}

	if( wraplen ) GL_wraptext( s, wraplen );

	/* get #lines and maxlen of label */
	measuretext( s, &nlines, &maxlen );
	if( maxlen  > maxtwidth ) maxtwidth = maxlen;
	if( nlines > maxthi ) maxthi = nlines;

	/* render label */
	if( colortext ) {
		if( LEtype[j] == LEGEND_COLOR ) Ecolor( color );
		strcpy( holdstdcolor, Estandard_color );
		strcpy( Estandard_color, "" ); /* this prevents textdet() from changing the color 7/12/01 */
		}
	textdet( "textdetails", textdetails, &align, &adjx, &adjy, -2, "R", 1.0 );

	Emov( x + adjx, y + adjy );
	Etext( s );
	if( colortext ) strcpy( Estandard_color, holdstdcolor );

	if( PLS.clickmap && url[0] ) {
		clickmap_entry( 'r', url, 0, x+adjx, y+adjy, 
			x+adjx+(maxlen*Ecurtextwidth), y+adjy+(nlines*Ecurtextheight), 1, 0, "" );
		}


	/* determine position for next legend entry.. */
	if( format == DOWN ) { 
		y -= ((Ecurtextheight * nlines) + 0.03 ) + msep;

		if(  y < colbreak ) { /* start a new column - added scg 8/12/05 */
			/* x = x + ((double)maxtwidth*Ecurtextwidth) + sampwidth + colchunksep;  */
			x = x + ((double)maxtwidth*Ecurtextwidth) + sampwidth + colchunksep;  
			y = starty - Ecurtextheight;
			maxtwidth = 0;
			}
		}

	else if( format == ACROSS ) { /* single line */
		/* if( hsep > 0.0 ) x += ((Ecurtextwidth * maxlen ) + sampwidth + hsep); */ /* sampwidth added scg 8/12/05 */
		if( hsep > 0.0 ) x += ((Ecurtextwidth * maxlen ) + hsep);  
		else x += ((Ecurtextwidth * maxlen ) + 1.2);

		if( extent > 0.0 && ((x-startx) > extent) ) { /* start a new row - added scg 8/12/05 */
			y -= ((Ecurtextheight * maxthi) + rowchunksep);
			x = startx;
			maxthi = 0;
			}
		}
	}


if( dobox ) {
	PLG_get_bb2( &bx1, &by1, &bx2, &by2 ); /* get bb so we know where to place the title.. */
	if( title[0] != '\0' ) { /* do invisible title 1st time thru to influence bb, then really draw it 2nd time thru.. */
		textdet( "titledetails", titledetails, &align, &adjx, &adjy, 0, "R", 1.0 );
		if( align == '?' ) align = 'C';
		convertnl( title );
		measuretext( title, &nlines, &maxlen );
		if( bstate == 0 ) { titx = bx1+((bx2-bx1)/2.0)+adjx;  tity = by2+(nlines*Ecurtextheight)+adjy; }
		Emov( titx, tity );
		Edotext( title, align );
		}
	Esquelch( "off" );
	if( bstate == 0 ) {
		/* now that we know the extent of the legend, do box now; 
		 * then we'll go back and draw the legend on top of it */
		PLG_get_bb2( &bx1, &by1, &bx2, &by2 ); /* may have changed if there was a title.. */
		if( stricmp( frame, "bevel" )==0 ) {
			Ecblock( bx1-bmx1, by1-bmy1, bx2+bmx2, by2+bmy2, backcolor, 0 );
			Ecblockdress( bx1-bmx1, by1-bmy1, bx2+bmx2, by2+bmy2, 0.07, "0.6", "0.8", 0.0, "" );
			}
		else if( stricmp( frame, "no" ) == 0 ) Ecblock( bx1-bmx1, by1-bmy1, bx2+bmx2, by2+bmy2, backcolor, 0 );
		else	{
			linedet( "frame", frame, 1.0 );
			Ecblock( bx1-bmx1, by1-bmy1, bx2+bmx2, by2+bmy2, backcolor, 1 );
			}
		bstate = 1;
		x = orig_x; y = orig_y;
		goto RENDER;
		}
	}


/* now reset so a new legend can be accumulated.. */
if( !noclear ) NLE = 0;
return( 0 );
}


/* ===================================== */
/* ADD_LEGENT - used by procs to add a legend entry */
int
PL_add_legent( typ, label, tag, parm1, parm2, parm3 )
int typ; 
char *label, *tag, *parm1, *parm2, *parm3;
{
char *errmsg;

errmsg = "Sorry, too much legend content";

convertnl( label );

LEtype[ NLE ] = typ;
LElabel[ NLE ] = LEavail;
if( LEavail + strlen( label ) >= MAXLEGTEXT ) 
	return( Eerr( 8478, errmsg, "" ) );
strcpy( &Ltext[ LEavail ], label );
LEavail += (strlen( label ) + 1);

LEtag[ NLE ] = LEavail;
if( LEavail + strlen( tag ) >= MAXLEGTEXT ) 
	return( Eerr( 8478, errmsg, "" ) );
strcpy( &Ltext[ LEavail ], tag );
LEavail += (strlen( tag ) + 1);

if( LEavail + strlen( parm1 ) >= MAXLEGTEXT ) 
	return( Eerr( 8478, errmsg, "" ) );
LEparm1[ NLE ] = LEavail;
strcpy( &Ltext[ LEavail ], parm1 );
LEavail += (strlen( parm1 ) + 1);

if( LEavail + strlen( parm2 ) >= MAXLEGTEXT ) 
	return( Eerr( 8478, errmsg, "" ) );
LEparm2[ NLE ] = LEavail;
strcpy( &Ltext[ LEavail ], parm2 );
LEavail += (strlen( parm2 ) + 1);

if( LEavail + strlen( parm3 ) >= MAXLEGTEXT ) 
	return( Eerr( 8478, errmsg, "" ) );
LEparm3[ NLE ] = LEavail;
strcpy( &Ltext[ LEavail ], parm3 );
LEavail += (strlen( parm3 ) + 1);


NLE++;
return( 0 );
}

/* ========================================= */
/* GET_LEGENT - get a legend entry based on tag */
int
PL_get_legent( tag, parm1, parm2, parm3 )
char *tag, *parm1, *parm2, *parm3;
{
int i;
for( i = 0; i < NLE; i++ ) if( strcmp( tag, &Ltext[ LEtag[i] ] )==0 ) break;
if( i == NLE ) return( 1 ); /* tag not found.. */

if( parm1 != NULL ) strcpy( parm1, &Ltext[ LEparm1[i] ] );
if( parm2 != NULL ) strcpy( parm2, &Ltext[ LEparm2[i] ] );
if( parm3 != NULL ) strcpy( parm3, &Ltext[ LEparm3[i] ] );
return( 0 );
}
/* ========================================= */
/* GET_LEGENT_RG - get a legend entry based numeric comparison with tag */
int
PL_get_legent_rg( val, parm1, parm2, parm3 )
double val;
char *parm1, *parm2, *parm3;
{
int i;
double atof();

for( i = 0; i < NLE; i++ ) if( val >= atof( &Ltext[ LEtag[i] ] ) ) break;
	
if( i == NLE ) return( 1 ); /* tag not found.. */

if( parm1 != NULL ) strcpy( parm1, &Ltext[ LEparm1[i] ] );
if( parm2 != NULL ) strcpy( parm2, &Ltext[ LEparm2[i] ] );
if( parm3 != NULL ) strcpy( parm3, &Ltext[ LEparm3[i] ] );
return( 0 );
}

/* ======================================================= *
 * Copyright 1998-2005 Stephen C. Grubb                    *
 * http://ploticus.sourceforge.net                         *
 * Covered by GPL; see the file ./Copyright for details.   *
 * ======================================================= */


syntax highlighted by Code2HTML, v. 0.9.1