/*	Copyright (c) 1988 AT&T	*/
/*	  All Rights Reserved  	*/

/*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T	*/
/*	The copyright notice above does not evidence any   	*/
/*	actual or intended publication of such source code.	*/

/*	@(#)picasso:attrs.c	1.0	*/
#include	"picasso.h"
#include	"y.tab.h"

void makeattr(int type, int sub, YYSTYPE val);

setdir(n)	/* set direction (hvmode) from LEFT, RIGHT, etc. */
	int n;
{
	switch (n) {
	case UP:	hvmode = U_DIR;		break;
	case DOWN:	hvmode = D_DIR;		break;
	case LEFT:	hvmode = L_DIR;		break;
	case RIGHT:	hvmode = R_DIR;		break;
/*	case REVERSE:	hvmode = (hvmode+2)%4;	break;	*/
	}
	return(hvmode);
}

curdir()	/* convert current dir (hvmode) to RIGHT, LEFT, etc. */
{
	switch (hvmode) {
	case R_DIR:	return RIGHT;
	case L_DIR:	return LEFT;
	case U_DIR:	return UP;
	case D_DIR:	return DOWN;
	}
	yyerror("can't happen curdir");
}

double	setrgbindex ()		/* returns integer-valued index to (r,g,b) */
{
	int	n;

	if (nexpr > 3)
		yyerror("color components are r, g, b only; excess ignored");
	for (n = 0; n < 3; ++n) {
		if (n >= nexpr)
			 exprlist[n] = 0;
		else if (exprlist[n] < 0 || exprlist[n] > 1) {
			yyerror("color component %g not between 0 and 1",
						exprlist[n]);
			nexpr = 0;
			return 0;
		}
	}
	nexpr = 0;
	for (n = nrgb; n--;)
		if (exprlist[0] == rgbtable[n].r &&	/* backwards in case  */
		    exprlist[1] == rgbtable[n].g &&	/* there's locality.  */
		    exprlist[2] == rgbtable[n].b) {
			return ((double)n);
	}
	if (nrgb >= nrgbtable)
		rgbtable = (rgb *) grow((char *)rgbtable, "rgbtable",
				nrgbtable += 100, sizeof(rgb));
	rgbtable[nrgb].r = exprlist[0];
	rgbtable[nrgb].g = exprlist[1];
	rgbtable[nrgb].b = exprlist[2];
	return ((double)nrgb++);
}

double
checkcolor (f)		/* gray level OR negative integer rgb index */
	double	f;
{
/*	if (f < 0.0 || (f > 1.0 && f != (double)((int)f)))
		{ yyerror("gray level %g not between 0 and 1", f); f = -1.0; }
*/	if (f > 1.0 || f < 0.0)
		f = (double)((int)f);
	if (f < -1.0)
		f = -1.0;	/* end of change */
	else if (f >= nrgb)
		{ yyerror("rgb color index %g not defined", f); f = -1.0; }
	return f;
}

makefattr(type, sub, f)	/* float attr */
	int type, sub;
	double f;
{
	YYSTYPE val;
	val.f = f;
	makeattr(type, sub, val);
}

makeoattr(type, o)	/* obj* attr */
	obj *o;
{
	YYSTYPE val;
	val.o = o;
	makeattr(type, 0, val);
}

makeiattr(type, i)	/* int attr */
	int i;
{
	YYSTYPE val;
	val.i = i;
	makeattr(type, 0, val);
}

int	def_textattr = CENTER;

maketattr(sub, p)	/* text attribute: takes two */
	char *p;
{
	YYSTYPE val;
	val.p = p;
	if (sub == 0)
		sub = def_textattr;
	else if (sub & (CENTER|LJUST|RJUST))
		def_textattr = (def_textattr & ~(CENTER|LJUST|RJUST)) | sub;
	else if (sub & (ABOVE|BELOW))
		def_textattr = (def_textattr & ~(ABOVE|BELOW)) | sub;
	makeattr(TEXTATTR, sub, val);
}

addtattr(sub)		/* add text attrib to existing item */
{
	attr[nattr-1].a_sub |= sub;
/*	def_textattr != sub;  looks like a bug and should be */
	def_textattr |= sub;
}

makevattr(p)	/* varname attribute */
	char *p;
{
	YYSTYPE val;

	val.p = p;
	makeattr(VARNAME, 0, val);
}

makelattr(p, q)	/* "locus" attribute; x and y coordinate lists via varnames */
	char	*p, *q;
{
	YYSTYPE val;
	val.p = p;
	makeattr(XLIST, 0, val);
	val.p = q;
	makeattr(YLIST, 0, val);
}

void
makedattr(char *name)		/* dash pattern (array of dash/space widths) */
{
	int	n;
struct	symtab	*p;
	YYSTYPE	val;

	if (name) {
		if ((p = lookup(name)) == NULL) {
			yyerror("no variable named %s", name);
			return;
		}
		n = p->s_dim+1;
	}
	else {
		n = nexpr;
		nexpr = 0;
	}
	if ((val.a = (float *)malloc(sizeof(float)*(n+1))) == NULL)
		yyerror("out of room in makedattr");
	else {
		val.a[0] = (float)n;
		while (n--)
			val.a[n+1] = (name? p->s_val.a[n] : exprlist[n]);
	}
	makeattr(DASHPAT, 0, val);
}

void
makeattr(int type, int sub, YYSTYPE val)	/* add attribute type and val */
{
	if (type == 0 && val.i == 0) {	/* clear table for next stat */
					/* happens at end of objects */
		nattr = 0;
		def_textattr = CENTER;
		return;
	}
	if (nattr >= nattrlist)
		attr = (Attr *) grow((char *)attr, "attr", nattrlist += 100,
								sizeof(Attr));
	attr[nattr].a_type = type;
	attr[nattr].a_sub = sub;
	attr[nattr].a_val = val;
	nattr++;
}

double	setattr(p, type, val)
	obj	*p;
	int	type;
	double	val;
{
	int	cw_switch;
	double	x, y;
	obj	*q;

	if (p->o_type == BLOCK) {
		for (q = p->o_next; q != p->o_val[N_VAL].o; q = q->o_next)
			setattr (q, type, val);
	}
	else if (p->o_type <= TEXT) switch (type) {

case TCOLOR:	if (val >= 0.0 || checkcolor(val) >= 0.0)
			p->o_text = val;
		break;
case LCOLOR:	if (val >= 0.0 || checkcolor(val) >= 0.0)
			p->o_color = val;
		break;
case PCOLOR:	if (val < 0.0 || checkcolor(val) < 0.0)
			p->o_attr &= ~FILLED;
		else {
			p->o_fill = val;	/* ignored for TEXT? */
			p->o_attr |= FILLED;
		}
		break;
case LAYER:	if (val < -128) val = -128; else if (val > 127) val = 127;
		p->o_layer = (short)val;
		if (val > getfval("maxlayer"))
			setfval ("maxlayer", val);
		break;
case LWEIGHT:	p->o_weight = val;
		break;
case NOEDGE:	if (val != 0.0)
			p->o_attr &= ~EDGED;
		else
			p->o_attr |= EDGED;
		break;

case CCW:
case CW:	if (p->o_type == ARC) {
			cw_switch = (p->o_attr & CW_ARC);
			if (type == CW)
				cw_switch = !cw_switch;
			if (cw_switch) {
				x = p->o_val[N_VAL+2].f;
				y = p->o_val[N_VAL+3].f;
				p->o_val[N_VAL+2] = p->o_val[N_VAL+4];
				p->o_val[N_VAL+3] = p->o_val[N_VAL+5];
    /* exchange from & to */	p->o_val[N_VAL+4].f = x;
				p->o_val[N_VAL+5].f = y;
				p->o_attr ^= (p->o_attr & CW_ARC);
			}
		}
		break;
case DIAMETER:	val /= 2;
case RADIUS:	switch (p->o_type) {
			case ARROW:
			case LINE:	
			case BOX:
			case ARC:
			case SECTOR:	p->o_val[N_VAL].f = val;
					break;
			case CIRCLE:
			case ELLIPSE:	p->o_wid = val * 2;
					break;
		}
		break;
case WIDTH:	p->o_wid = val;
		break;
case HEIGHT:	p->o_ht = val;
		break;
default:	yyerror ("can't happen setattr");
	}
	return val;
}

miscattrs(ap, obat)
	Attr	*ap;
struct	objattr	*obat;
{
	float	*fp;
	int	n;

	switch (ap->a_type) {
		case FONT:
			reset_font((double)ap->a_val.f);
			break;
		case SIZE:
			reset_size(ap->a_sub, (double)ap->a_val.f);
			break;
		case SPACE:
			reset_space(ap->a_sub, (double)ap->a_val.f);
			break;
		case TEXTATTR:
			if (ap->a_val.p != NULL)
				savetext(ap->a_sub, ap->a_val.p);
			else
	/* These type values should be propagated back to other strings */
	/* Maybe by calling reset_type, to be added to textgen.c */
				text[ntext-1].t_type = ap->a_sub;
						/* ??? can this ever happen */
						/* except after a previous  */
						/* text in the same object? */
			break;
		case NOEDGE:
			obat->a_flags &= ~EDGED;
			break;
		case LAYER:
			obat->a_layer = ap->a_val.f;
			break;
		case LWEIGHT:
			obat->a_weight = ap->a_val.f;
			break;
		case LCOLOR:
			obat->a_lcolor = ap->a_val.f;
			break;
		case PCOLOR:
			obat->a_flags |= FILLED;
			if (ap->a_sub != DEFAULT)
				obat->a_pcolor = ap->a_val.f;
			break;
		case TCOLOR:
			obat->a_tcolor = ap->a_val.f;
			break;
		case DOT:
		case DASH:
			n = ap->a_type == DOT ? 3 : 2;
			if ((fp = (float *)malloc(n * sizeof(float))) == NULL) {
				yyerror("out of space in miscattrs");
				break;
			}
			*fp = --n;
			fp[n] = (ap->a_sub == DEFAULT ? getfval("dashwid")
						      : ap->a_val.f);
			if (n == 2)
				fp[1] = -1;	/* fill in later, from weight */
			ap->a_val.a = fp;
			/* and fall through to the general case */
		case DASHPAT:
			obat->a_flags |= DOTDASH;
			obat->a_dashpat.a = ap->a_val.a;
			break;
		case HEIGHT:
			obat->a_ht = ap->a_val.f;
			break;
		case WIDTH:
			obat->a_wid = ap->a_val.f;
			break;
		case RADIUS:
			obat->a_rad = ap->a_val.f;
			break;
		case DIAMETER:
			obat->a_rad = ap->a_val.f / 2;
			break;
	}
}

float	miters[8] = {0, 0, 0, 0, 0, 0, 0, 0};

primattrs(p, obat)	/* note: ht, wid, rad and layer are set elsewhere   */
			/*	 because of nonuniformities in their design */
	obj	*p;	/*	 (this could be further rationalized!)	    */
struct	objattr	*obat;
{
	int	n;
	float	x, bnd[4];

	p->o_attr |= obat->a_flags;
	p->o_text  = obat->a_tcolor;
	checktextcolor(p);
	if (obat->a_flags & FILLED) {
		if (obat->a_pcolor == -1.)
			obat->a_pcolor = getfval ("fillcolor");
		p->o_fill = checkcolor((double)obat->a_pcolor);
	}
	if (obat->a_flags & EDGED) {
		if (obat->a_weight == -1.)
			obat->a_weight = getfval ("lineweight");
		if (obat->a_weight >= 0.)
			p->o_weight = obat->a_weight;
		if (obat->a_lcolor == -1.)
			obat->a_lcolor = getfval ("linecolor");
		p->o_color = checkcolor((double)obat->a_lcolor);
		if ((n = (int)getfval("linecap")) >= 0)
			p->o_attr |= (1 + n%3)*LINECAP;
		if ((n = (int)getfval("linejoin")) >= 0)
			p->o_attr |= (1 + n%3)*JOIN;
		if (n == 0) {				/* mitre join */
			x = getfval("miterlimit");
			if (x >= 1) {
				for (n = 1; n < 8; n++)
					if (miters[n] == 0 || miters[n] == x)
						break;
				if (n == 8)
					for (n = 1; n < 7; ++n)
						miters[n] = miters[n+1];
				miters[n] = x;
				p->o_attr |= n * MITER;
			}
		}
	}
	else
		p->o_weight = 0;
	if (obat->a_dashpat.a != NULL && obat->a_dashpat.a[1] == -1) /* dots */
		if ((obat->a_dashpat.a[1] = p->o_weight) == 0)
			obat->a_dashpat.a[1] = 1/(pgscale*2);
	p->o_ddpat = obat->a_dashpat;
}

checktextcolor (p)
	obj	*p;
{
	if (p->o_nt2 > p->o_nt1) {
		if (p->o_text == -1)
			p->o_text = getfval("textcolor");
		p->o_text = checkcolor((double)p->o_text);
	}
}
