#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include "pic.h"
#include "y.tab.h"

obj *linegen(int type)
{
	static double prevdx = HT;
	static double prevdy = 0;
	static double prevw = HT10;
	static double prevh = HT5;
	int i, j, some, head, ddtype, invis, chop, battr, with;
	double ddval, chop1, chop2, x0, y0, x1, y1;
	double fillval = 0;
	double theta;
	double defx, defy, xwith, ywith;
	obj *p, *ppos;
	static int xtab[] = { 1, 0, -1, 0 };	/* R=0, U=1, L=2, D=3 */
	static int ytab[] = { 0, 1, 0, -1 };
	double dx[500], dy[500];
	int ndxy;
	double nx, ny;
	Attr *ap, *chop_ap[4];

	nx = curx;
	ny = cury;
	defx = getfval("linewid");
	defy = getfval("lineht");
	prevh = getfval("arrowht");
	prevw = getfval("arrowwid");
	dx[0] = dy[0] = ndxy = some = head = invis = battr = with = 0;
	chop = chop1 = chop2 = 0;
	ddtype = ddval = xwith = ywith = 0;
	for (i = 0; i < nattr; i++) {
		ap = &attr[i];
		switch (ap->a_type) {
		case TEXTATTR:
			savetext(ap->a_sub, ap->a_val.p);
			break;
		case HEAD:
			head += ap->a_val.i;
			break;
		case INVIS:
			invis = INVIS;
			break;
		case NOEDGE:
			battr |= NOEDGEBIT;
			break;
		case DOT:
		case DASH:
			ddtype = ap->a_type==DOT ? DOTBIT : DASHBIT;
			if (ap->a_sub == DEFAULT)
				ddval = getfval("dashwid");
			else
				ddval = ap->a_val.f;
			break;
		case SAME:
			dx[ndxy] = prevdx;
			dy[ndxy] = prevdy;
			some++;
			break;
		case LEFT:
			dx[ndxy] -= (ap->a_sub==DEFAULT) ? defx : ap->a_val.f;
			some++;
			hvmode = L_DIR;
			break;
		case RIGHT:
			dx[ndxy] += (ap->a_sub==DEFAULT) ? defx : ap->a_val.f;
			some++;
			hvmode = R_DIR;
			break;
		case UP:
			dy[ndxy] += (ap->a_sub==DEFAULT) ? defy : ap->a_val.f;
			some++;
			hvmode = U_DIR;
			break;
		case DOWN:
			dy[ndxy] -= (ap->a_sub==DEFAULT) ? defy : ap->a_val.f;
			some++;
			hvmode = D_DIR;
			break;
		case HEIGHT:	/* length of arrowhead */
			prevh = ap->a_val.f;
			break;
		case WIDTH:	/* width of arrowhead */
			prevw = ap->a_val.f;
			break;
		case TO:
			if (some) {
				nx += dx[ndxy];
				ny += dy[ndxy];
				ndxy++;
				dx[ndxy] = dy[ndxy] = some = 0;
			}
			ppos = attr[i].a_val.o;
			if (ppos == NULL)
				ERROR "no tag defined for `to'" FATAL;
			dx[ndxy] = ppos->o_x - nx;
			dy[ndxy] = ppos->o_y - ny;
			some++;
			break;
		case BY:
			if (some) {
				nx += dx[ndxy];
				ny += dy[ndxy];
				ndxy++;
				dx[ndxy] = dy[ndxy] = some = 0;
			}
			ppos = ap->a_val.o;
			if (ppos == NULL)
				ERROR "no tag defined for `by'" FATAL;
			dx[ndxy] = ppos->o_x;
			dy[ndxy] = ppos->o_y;
			some++;
			break;
		case THEN:	/* turn off any previous accumulation */
			if (some) {
				nx += dx[ndxy];
				ny += dy[ndxy];
				ndxy++;
				dx[ndxy] = dy[ndxy] = some = 0;
			}
			break;
		case FROM:
		case AT:
			ppos = ap->a_val.o;
			if (ppos == NULL)
				ERROR "no tag defined for `from' or `at'" FATAL;
			nx = curx = ppos->o_x;
			ny = cury = ppos->o_y;
			break;
		case WITH:
			with = ap->a_val.i;
			break;
		case CHOP:
			if (ap->a_sub != PLACENAME) {
				if( chop == 0)
					chop1 = chop2 = ap->a_val.f;
				else
					chop2 = ap->a_val.f;
			}
			chop_ap[chop++] = ap;
			break;
		case FILL:
			battr |= FILLBIT;
			if (ap->a_sub == DEFAULT)
				fillval = getfval("fillval");
			else
				fillval = ap->a_val.f;
			break;
		}
	}
	if (with) {	/* this doesn't work at all */
		switch (with) {
		case CENTER:
			xwith = (dx[1] - dx[0]) / 2; ywith = (dy[1] - dy[0]) / 2; break;
		}
		for (i = 0; i < ndxy; i++) {
			dx[i] -= xwith;
			dy[i] -= ywith;
		}
		curx += xwith;
		cury += ywith;
	}
	if (some) {
		nx += dx[ndxy];
		ny += dy[ndxy];
		ndxy++;
		defx = dx[ndxy-1];
		defy = dy[ndxy-1];
	} else {
		defx *= xtab[hvmode];
		defy *= ytab[hvmode];
		dx[ndxy] = defx;
		dy[ndxy] = defy;
		ndxy++;
		nx += defx;
		ny += defy;
	}
	prevdx = defx;
	prevdy = defy;
	if (chop) {
		if (chop == 1 && chop1 == 0)	/* just said "chop", so use default */
			chop1 = chop2 = getfval("circlerad");
		theta = atan2(dy[0], dx[0]);
		x0 = chop1 * cos(theta);
		y0 = chop1 * sin(theta);
		curx += x0;
		cury += y0;
		dx[0] -= x0;
		dy[0] -= y0;

		theta = atan2(dy[ndxy-1], dx[ndxy-1]);
		x1 = chop2 * cos(theta);
		y1 = chop2 * sin(theta);
		nx -= x1;
		ny -= y1;
		dx[ndxy-1] -= x1;
		dy[ndxy-1] -= y1;
		dprintf("chopping %g %g %g %g; cur=%g,%g end=%g,%g\n",
			x0, y0, x1, y1, curx, cury, nx, ny);
	}
	p = makenode(type, 5 + 2 * ndxy);
	curx = p->o_val[0] = nx;
	cury = p->o_val[1] = ny;
	if (head || type == ARROW) {
		p->o_nhead = getfval("arrowhead");
		p->o_val[2] = prevw;
		p->o_val[3] = prevh;
		if (head == 0)
			head = HEAD2;	/* default arrow head */
	}
	p->o_attr = head | invis | ddtype | battr;
	p->o_fillval = fillval;
	p->o_val[4] = ndxy;
	nx = p->o_x;
	ny = p->o_y;
	for (i = 0, j = 5; i < ndxy; i++, j += 2) {
		p->o_val[j] = dx[i];
		p->o_val[j+1] = dy[i];
		if (type == LINE || type == ARROW)
			extreme(nx += dx[i], ny += dy[i]);
		else if (type == SPLINE && i < ndxy-1) {
			/* to compute approx extreme of spline at p,
			/* compute midway between p-1 and p+1,
			/* then go 3/4 from there to p */
			double ex, ey, xi, yi, xi1, yi1;
			xi = nx + dx[i]; yi = ny + dy[i];	/* p */
			xi1 = xi + dx[i+1]; yi1 = yi + dy[i+1];	/* p+1 */
			ex = (nx+xi1)/2; ey = (ny+yi1)/2;	/* midway */
			ex += 0.75*(xi-ex); ey += 0.75*(yi-ey);
			extreme(ex, ey);
			nx = xi; ny = yi;
		}
			
	}
	p->o_ddval = ddval;
	if (dbg) {
		printf("S or L from %g %g to %g %g with %d elements:\n", p->o_x, p->o_y, curx, cury, ndxy);
		for (i = 0, j = 5; i < ndxy; i++, j += 2)
			printf("%g %g\n", p->o_val[j], p->o_val[j+1]);
	}
	extreme(p->o_x, p->o_y);
	extreme(curx, cury);
	return(p);
}
