/*
 *  fig2MF -- convert fig to mfpic METAFONT code
 *
 *  copyright (c) 1993 Anthony Starks (ajs@merck.com)
 *  You may do anything you want with this code except sell it
 *  or claim that you wrote it.
 *
 *
 *  Version 0.00 -- 	Incorporate Tobin's small suggestions
 *  Version 0.01 -- 	Change scaling to inches,
 *			default [x-y]scale is 1/8 inch
 *			slight format changes in cinit()
 *  Version 0.02 --	Fixed pen switching bug
 *  Version 0.03 --	Support new arcthree mode
 *  Version 0.04 --	Token support for text
 */

#include <stdio.h>

#define put_msg(x)	fputs(x, stderr); \
			fprintf(stderr, \
			" at line %d of file \"%s\" \n", line, curfile)
#define dofill()	1.2-((double)area_fill/(double)FILL_BLACK)
#define dopen(x)	((x-1)*PEN_INCR)+DEF_PEN
#define VERSION		0.04
#define BUF_SIZE	1024
#define O_ELLIPSE	1
#define O_POLYLINE	2
#define O_SPLINE	3
#define O_TEXT		4
#define O_ARC		5
#define O_COMP		6
#define O_ECOMP		-6
#define FILL_BLACK	21
#define N_CURVE		11
#define N_POLYLINE	12
#define N_ELLIPSE	19
#define N_ARC		20
#define DEF_PEN		0.5
#define PEN_INCR	0.10
#define DEF_BFILE	"graphbase.mf"
char buf[BUF_SIZE];

typedef struct {
	char *keyword;
	double *value;
} Options;

int line;
double ppi;
double mag;
double code;
double mfpen;
double xscale;
double yscale;
double xl;
double yl;
double xu;
double yu;
double maxy;
char *progname;
char *curfile;
char basefile[100];

Options opts[]  = {
{"-mag",	&mag},
{"-code",	&code},
{"-pen",	&mfpen},
{"-xscale",	&xscale},
{"-yscale",	&yscale},
{"-xneg",	&xl},
{"-xpos",	&xu},
{"-yneg",	&yl},
{"-ypos",	&yu},
{"-top",	&maxy},
{NULL,		NULL}
};


main(argc, argv)
int             argc;
char          *argv[];
{
	FILE           *fp;
	int i;
	progname = argv[0];
	curfile = "Standard Input";
	strcpy(basefile, DEF_BFILE);
	line=1;
	mag=1000.0;
	code=32.0;
	mfpen=0.5;
	xscale=0.125;
	yscale=0.125;
	xl=0.0;
	xu=8.0;
	yl=0.0;
	yu=8.0;
	maxy=8.0;

	if (argc < 2)
		process("-");
	else
	{
		for (i=1; i < argc; i++)
		{
			parse(argv[i]);
		}
	}

	ffin();
	exit(0);

}

parse(s)
char *s;
{
	int i, n;
	char *strchr();
	double atof();

	for (i=0; opts[i].keyword != NULL; i++)
	{
		if (strncmp(s, opts[i].keyword, strlen(opts[i].keyword)) == 0)
		{
			*opts[i].value = atof(strchr(s, '=')+1);
			return;
		}
	}
	process(s);
}

process(filename)
char *filename;
{
	FILE *fp;

	int coord_sys, object;

	if (filename[0] == '-')
	{
		curfile = "Standard Input";
		fp = stdin;
	}
	else
	{
		curfile=filename;
		if ((fp = fopen(filename, "r")) == NULL)
		{
			fprintf(stderr,
			"%s: cannot open the input file \"%s\"\n",
			progname, filename);
			return;
		}
	}

	line = 1;
	fgets(buf, sizeof(buf), fp);
	if (strncmp(buf, "#FIG 2.1", 8) != 0)
	{
		put_msg("Not a fig 2.1 file");
		return;
	}
	get_line(fp);
	if (sscanf(buf, "%lf%d\n", &ppi, &coord_sys) != 2)
	{
		put_msg("Incomplete data");
		return;
	}
	cinit();
	while (get_line(fp) > 0)
	{
		if (sscanf(buf, "%d", &object) != 1)
		{
			put_msg("Incorrect format");
			return;
		}
		switch(object)
		{
			case O_COMP:
			case O_ECOMP:
				break;
			case O_ELLIPSE:
				ellipse(fp);
				break;
			case O_POLYLINE:
				polyline(fp);
				break;
			case O_SPLINE:
				curve(fp);
				break;
			case O_TEXT:
				text(fp);
				break;
			case O_ARC:
				arc(fp);
				break;
			default:
				fprintf(stderr, "%d is an ", object);
				put_msg("unknown object");
				break;
		}
	}
	cfin();
	if (fp != stdin) fclose(fp);

}

polyline(fp)
FILE *fp;
{
	int	c,
		n,
		type,
		subtype,
		style,
		radius,
		thickness,
		color,
		depth,
		pen,
		area_fill,
		fa,
		ba,
		at,
		as,
		eflag;

	double	style_val,
		athick,
		awidth,
		aht;

	char	epsfile[100];

	c = 0;
	n = sscanf(buf, "%d%d%d%d%d%d%d%d%lf%d%d%d",
		&type,
		&subtype,
		&style,
		&thickness,
		&color,
		&depth,
		&pen,
		&area_fill,
		&style_val,
		&radius,
		&fa,
		&ba);

	if (n != N_POLYLINE)
	{
		put_msg("Malformed polyline");
		return c;
	}
	if (subtype == 5) fscanf(fp, "%d%s", &eflag, epsfile);
	if (fa) fscanf(fp, "%d%d%lf%lf%lf\n", &at, &as, &athick, &awidth, &aht);
	if (ba) fscanf(fp, "%d%d%lf%lf%lf\n", &at, &as, &athick, &awidth, &aht);
	if (thickness > 1)
		printf("  pickup pencircle scaled %.2lfpt;\n",
			dopen(thickness));
	if (area_fill == FILL_BLACK)
		printf("  cycleshade(0, false,\n");
	else if (area_fill < FILL_BLACK && area_fill > 0)
		printf("  cycleshade(%lfpt, false,\n", dofill());
	else
		printf("  curve(false, false,\n");
	c = coords(type, fp);
	if (thickness > 1)
		printf("  pickup pencircle scaled %.2lfpt;\n",
			mfpen);

	return c;
}


curve(fp)
FILE *fp;
{
	int	n,
		c,
		i,
		type,
		style,
		subtype,
		thickness,
		color,
		depth,
		pen,
		area_fill,
	        fa,
		ba,
		at,
		as;

	double	style_val,
		athick,
		awidth,
		aht,
		z;

	c = 0;
	n = sscanf(buf, "%d%d%d%d%d%d%d%d%lf%d%d",
		   &type,
		   &subtype,
		   &style,
		   &thickness,
		   &color,
		   &depth,
		   &pen,
		   &area_fill,
		   &style_val,
		   &fa,
		   &ba);
	if (n != N_CURVE)
	{
		put_msg("Malformed spline");
		return c;
	}
	if (fa) fscanf(fp, "%d%d%lf%lf%lf\n", &at, &as, &athick, &awidth, &aht);
	if (ba) fscanf(fp, "%d%d%lf%lf%lf\n", &at, &as, &athick, &awidth, &aht);
	if (thickness > 1)
		printf("  pickup pencircle scaled %.2lfpt;\n",
			dopen(thickness));
	if (area_fill == FILL_BLACK)
		printf("  cycleshade(0, true,\n");
	else if (area_fill < FILL_BLACK && area_fill > 0)
		printf("  cycleshade(%lfpt, true,\n", dofill());
	else
		printf("  curve(true, false,\n");
	c = coords(type, fp);
	if (subtype > 1)
		for (i=0; i < c*4; i++)
			fscanf(fp, "%lf", &z);
	if (thickness > 1)
		printf("  pickup pencircle scaled %.2lfpt;\n", mfpen);
	return c;
}

coords(obj, fp)
int obj;
FILE *fp;
{
	int c, n, x, y;

	c = 0;
	if ((n = fscanf(fp, "%d%d", &x, &y)) != 2)
	{
		put_msg("Incomplete object");
		return c;
	}
	printf("       (%lf, %lf)", x/ppi, maxy-(y/ppi));
	for (c = 1;;)
	{
		if (fscanf(fp, "%d%d", &x, &y) != 2)
		{
			put_msg("Incomplete object");
			fputs(buf, stderr);
			return c;
		}
		if (x == 9999) break;
		printf(",\n       (%lf, %lf)", x/ppi, maxy-(y/ppi));
		c++;
	}
	printf(");\n");
	return c;

}

ellipse(fp)
FILE *fp;
{
	int	 n,
		 type,
		 subtype,
		 style,
		 thickness,
		 color,
		 depth,
		 pen,
		 area_fill,
		 dir,
		 cx,
		 cy,
		 rx,
		 ry,
		 sx,
		 sy,
		 ex,
		 ey;

	double	style_val,
		angle;

	n = sscanf(buf, "%d%d%d%d%d%d%d%d%lf%d%lf%d%d%d%d%d%d%d%d",
		&type,
		&subtype,
		&style,
		&thickness,
		&color,
		&depth,
		&pen,
		&area_fill,
		&style_val,
		&dir,
		&angle,
		&cx,
		&cy,
		&rx,
		&ry,
		&sx,
		&sy,
		&ex,
		&ey);
	if (n != N_ELLIPSE)
	{
		put_msg("Malformed circle or ellipse");
		return;
	}

	if (thickness > 1)
		printf("  pickup pencircle scaled %.2lfpt;\n",
			dopen(thickness));

	if (subtype == 3 || subtype == 4)
	{
		if (area_fill == FILL_BLACK)
			printf("  circshade(0, ");
		else if (area_fill < FILL_BLACK && area_fill > 0)
			printf("  circshade(%lfpt, ", dofill());
		else
			printf("  circle(");
		printf("(%lf,%lf),%lf);\n",
			cx/ppi, maxy-(cy/ppi), rx/ppi);
	}
	else if (subtype == 1 || subtype == 2)
	{
		if (area_fill == FILL_BLACK)
			printf("  ellshade(0, ");
		else if (area_fill < FILL_BLACK && area_fill > 0)
			printf("  ellshade(%lfpt, ", dofill());
		else
			printf("  ellipse(");
		printf("(%lf,%lf),%lf,%lf,0);\n",
			cx/ppi, maxy-(cy/ppi), rx/ppi, ry/ppi);
	}
	if (thickness > 1)
		printf("  pickup pencircle scaled %.2lfpt;\n", mfpen);

}

arc(fp)
FILE *fp;
{

	int	n,
		type,
		subtype,
		style,
		thickness,
		color,
		depth,
		pen,
		fill,
		dir,
		fa,
		ba,
		x1,
		y1,
		x2,
		y2,
		x3,
		y3;

	double	style_val,
		cx,
		cy;


	n = sscanf(buf, "%d%d%d%d%d%d%d%d%lf%d%d%d%lf%lf%d%d%d%d%d%d",
		&type,
		&subtype,
		&style,
		&thickness,
		&color,
		&depth,
		&pen,
		&fill,
		&style_val,
		&dir,
		&fa,
		&ba,
		&cx,
		&cy,
		&x1,
		&y1,
		&x2,
		&y2,
		&x3,
		&y3);

	if (fa) get_line(fp);
	if (ba) get_line(fp);

	if (n != N_ARC)
	{
		put_msg("Malformed arc");
		return n;
	}
	if (thickness > 1)
		printf("  pickup pencircle scaled %.2lfpt;\n",
			dopen(thickness));

	printf("  arcthree((%lf,%lf), (%lf,%lf), (%lf,%lf));\n",
		x1/ppi,
		maxy-(y1/ppi),
		x2/ppi,
		maxy-(y2/ppi),
		x3/ppi,
		maxy-(y3/ppi));

	if (thickness > 1)
		printf("  pickup pencircle scaled %.2lfpt;\n",
			mfpen);

	return n;

}

text(fp)
FILE *fp;
{

	int	n,
		x,
		y;

	int	type,
		font,
		size,
		pen,
		color,
		depth,
		filler,
		angle,
		flags,
		ht,
		len;
	char 	text[80],
		junk[2],
		*p,
		*strrchr();

	n = sscanf(buf, "%*d%d%d%d%d%d%d%lf%d%d%d%d%d%[^\1]%[\1]",
			&type,
			&font,
			&size,
			&pen,
			&color,
			&depth,
			&angle,
			&flags,
			&ht,
			&len,
			&x,
			&y,
			text,
			junk);
	if ((n != 13) && (n != 14))
	{
		put_msg("Bad Text");
		return 0;
	}
	p = &text[1];
	printf("%% label((%lf,%lf),%s)\n", x/ppi, maxy-(y/ppi), p);
	return(strlen(text));
}

cinit()
{
	static int once = 0;
	static int curchar;
	if (++once == 1)
	{
		curchar = (int)code;
		printf("%%\n%% %s version %.2lf --- Preamble\n%%\n",
			progname, VERSION);
		printf("mag:=%g/1000; input %s; code:=%g;\n",
			mag, basefile, code);
		printf("mfpicenv;\ninterim hdwdr:=1; interim hdten:=1;\n");
		printf("interim penwd:=%.2lfpt;\npickup pencircle scaled penwd;\n", mfpen);
	}
	printf("%%\n%% %s (char %d)\n%%\n", curfile, ++curchar);
	printf("xscale:=%.3lf; yscale:=%.3lf; bounds(%.3lf,%.3lf,%.3lf,%.3lf);\n",
		xscale, yscale, xl, xu, yl, yu);
	printf("beginchar(incr code,xscale*(xpos-xneg)*in#,yscale*(ypos-yneg)*in#,0);\n");
	printf("  setztr;\n");
	printf("  pickup pencircle scaled %.2lfpt;\n", mfpen);
}

cfin()
{
	printf("endchar;\n");
}

ffin()
{
	if (line > 1) printf("endmfpicenv;\nend.\n");
}

get_line(fp)
	FILE           *fp;
{
	while (1)
	{
		if (NULL == fgets(buf, BUF_SIZE, fp))
		{
			return (-1);
		}
		line++;
		if (*buf != '\n' && *buf != '#')
			return (1);
		/* Skip empty and comment lines */
	}
}
