#ifndef lint
static char *sccsid = "@(#)dterm.c	1.22 (UKC) 13/2/89";
static char *copyright = "@(#)Copyright Martin Guy, UKC, October 1988";
#endif  lint
/*
 *	Dterm: yet another ditroff previewer, this time for
 *	Atari ST with Peter Collinson's terminal emulator.
 *
 *	Written from scratch rather than continuing the august line
 *	of plagiarism dcan - dsan - dbbc - djet.
 *
 *	Need to do:
 *
 *	- Send Ade values for some funny characters
 *	- Send little bitmaps for others
 *	- On exit, reset point size according to $TERM
 */

#include <stdio.h>
#include <ctype.h>
#include <signal.h>
#include "dev.h"
#include "term.h"	/* Definitions for for users of term.c */
#include "term_font.h"	/* Font description structure definitions */

/*
 *	Data imported by drawing functions, set in term_init().
 */
int DX;		/* horizontal plotting increment */

#ifdef OPTCOUNT
/* How many times various optimisations saved us some I/O */
static int cnt_drawdot = 0;	/* how many times it was called */
static int opt_drawdot = 0;	/* how many times the optimisation struck */
static int cnt_set_pos = 0;
static int opt_set_pos = 0;
#endif

/*
 *	Data for ditroffy bits
 */
static int paperwidth, paperlength; /* size of paper in target device goobies */
static int res;			/* resolution of device in goobies per inch */
static char fontdir[] = "/usr/lib/font"; /* where the font tables live */
static char devname[20];		 /* name of target device (eg "can") */
int hpos, vpos;		/* where the printing head should be, in device coordinates */

/*
 *	Macros to do simple things
 */
#define hgoto(x)	hpos = (x)
#define vgoto(x)	vpos = (x)
#define hmot(x)		hpos += (x)
#define vmot(x)		vpos += (x)

/*
 * Buffer for stdout so it's not unbuffered.
 * Not too big, to avoid latency problems.
 */

char stdbuf[1024];

char *progname;			/* argv[0] for error reporting */

/* option flags */
int unity_aspect = 0;		/* overflows the bottom of the screen */

main(argc, argv)
char **argv;
{
	register int i;
	int fatalsig(), suspend();

	progname = argv[0];

	(void) signal(SIGINT, fatalsig);
	(void) signal(SIGTSTP, suspend);
	setbuffer(stdout, stdbuf, sizeof(stdbuf));	/* buffered for speed */

	for (i=1; i<argc; i++) {
		if (argv[i][0] == '-') {
			switch(argv[i][1]) {
			case '\0':
				conv(stdin, "stdin");
				break;
			case 'f':
				term_cslop = atoi(&argv[i][2]);
				if (term_cslop <= 0) term_cslop = 1;
				break;
			case 'o':
				/* do o_list stuff */
				fprintf(stderr, "%s: -o not implemented yet.\n", progname);
				exit(1);
			case 'u':
				unity_aspect = 1;
				break;
			default:
				fputs("Usage: dterm [-u] [-f[#]] [file] ...\n", stderr);
				exit(1);
			}
		} else {
			FILE *fp;
			char *filename = argv[i];

			fp = fopen(filename, "r");
			if (fp == NULL) {
				fprintf(stderr, "%s: Cannot open ", progname);
				perror(filename);
				continue;	/* with arg loop */
			}
			conv(fp, filename);
			(void) fclose(fp);
		}
	}

	waitkey();
	term_deinit();

#ifdef OPTCOUNT
	fprintf(stderr, "Optimisation counts: drawdot=%d/%d, setpos=%d/%d\n",
		opt_drawdot, cnt_drawdot, opt_set_pos, cnt_set_pos);
#endif
	exit(0);
}

/*
 *	Various other ways of quitting dterm
 */
fatal(fmt, a1, a2, a3)
char *fmt;
{
	set_bitmap(0);
	putchar(7);	/* Bling! */
	waitkey();
	term_deinit();
	fprintf(stderr, "%s: ", progname);
	fprintf(stderr, fmt, a1, a2, a3);
	putc('\n', stderr);
	exit(1);
}

fatalsig()
{
	/* A second signal would stop us from resetting the terminal */
	signal(SIGINT, SIG_IGN);
	term_deinit();
	exit(1);
}

suspend()
{
	term_save();	/* save settings of bitmap mode etc */
	term_deinit();
	fflush(stdout);
	
	kill(getpid(), SIGSTOP);
	/* stops here */
	(void) signal(SIGTSTP, suspend);

	term_init();
	term_restore();
}

waitkey()
{
	/* wait for a keypress before displaying the next page */
	char ch;

	set_pos(0, 0);
	set_bitmap(0);
	set_cursor(1);
	(void) fflush(stdout);

	/* Try to get it right even if they are outputting into a file,
	 * or reading DIL from stdin */
	if (isatty(1)) {
		(void) read(1, &ch, 1);
	}

	/* t_page wil turn ithe cursor off again if there is another page */
}

conv(fp, filename)
FILE *fp;		/* open to ditroff intermediate code file */
char *filename;		/* associated filename for error reporting */
{
	register int c, k;
	int m, n, m1, n1;
	char str[300];	/* Why 300?  Cos that's what all the others use. */

	/*
	 *	get first line separately, to check that the input is a
	 *	file of ditroff intermediate code.
	 */
	if (fscanf(fp, "x T %s\n", devname) != 1) {
		fatal("Input file %s does not contain ditroff intermediate code.", filename);
		/* NOTREACHED */
	}

	/* open tables for the device */
	fileinit();

	/* Oh no, it's that dil parser again. */
	while ((c = getc(fp)) != EOF) {
		switch (c) {
		case '\n':	/* when input is text */
		case ' ':
		case 0:		/* occasional noise creeps in */
			break;
		case '0': case '1': case '2': case '3': case '4':
		case '5': case '6': case '7': case '8': case '9':
			/* two motion digits plus a character */
			hmot((c-'0')*10 + getc(fp)-'0');
			put1(getc(fp));
			break;
		case 'c':	/* single ascii character */
			put1(getc(fp));
			break;
		case 'C':
			(void) fscanf(fp, "%s", str);
			put1s(str);
			break;
		case 'D':	/* draw function */
			(void) fgets(str, sizeof(str), fp);
			switch (str[0]) {
			case 'l':	/* draw a line */
				(void) sscanf(str+1, "%d %d", &n, &m);
				drawline(n, m);
				break;
			case 'c':	/* circle */
				(void) sscanf(str+1, "%d", &n);
				drawcirc(n);
				break;
			case 'e':	/* ellipse */
				(void) sscanf(str+1, "%d %d", &m, &n);
				drawellip(m, n);
				break;
			case 'a':	/* arc */
				(void) sscanf(str+1, "%d %d %d %d", &n, &m, &n1, &m1);
				drawarc(n, m, n1, m1);
				break;
			case '~':	/* wiggly line */
				drawwig(str+1);
				break;
			}
			break;
		case 's':
			(void) fscanf(fp, "%d", &n);	/* ignore fractional sizes */
			setsize(n);
			break;
		case 'f':
			(void) fscanf(fp, "%s", str);
			break;
		case 'H':	/* absolute horizontal motion */
			/* (void) fscanf(fp, "%d", &n); */
			while ((c = getc(fp)) == ' ')
				;
			k = 0;
			do {
				k = 10 * k + c - '0';
			} while (isdigit(c = getc(fp)));
			(void) ungetc(c, fp);
			hgoto(k);
			break;
		case 'h':	/* relative horizontal motion */
			/* (void) fscanf(fp, "%d", &n); */
			while ((c = getc(fp)) == ' ')
				;
			k = 0;
			do {
				k = 10 * k + c - '0';
			} while (isdigit(c = getc(fp)));
			(void) ungetc(c, fp);
			hmot(k);
			break;
		case 'w':	/* word space */
			break;
		case 'V':
			(void) fscanf(fp, "%d", &n);
			vgoto(n);
			break;
		case 'v':
			(void) fscanf(fp, "%d", &n);
			vmot(n);
			break;
		case 'p':	/* new page */
			(void) fscanf(fp, "%d", &n);
			t_page(n,fp);
			break;
		case 'n':	/* end of line */
		case 'x':	/* device control */
			devctrl(fp);
		case '#':	/* comment */
			while ((c=getc(fp)) != '\n')
				if (c == EOF) break;
			break;
		default:
			fatal("Unknown input character 0%o '%c'", c, c);
		}
	}
}

devctrl(fp)
FILE *fp;
{
	register int ch;

	ch = getc(fp);
	if (ch == ' ') ch = getc(fp);	/* Dump the space after the x */

	switch (ch) {
	case 's':	/* x stop */
		/* Included for tail +0f file.i | dterm.c */
		(void) fflush(stdout);
		break;
	}
	/* Caller must gobble rest of line */
}

/*
 *	Read in font and code files etc.
 */
fileinit()
{
	int fin;
	char temp[60];
	struct dev dev;

	/* open table for device, and set params */
	(void) sprintf(temp, "%s/dev%s/DESC.out", fontdir, devname);
	if ((fin = open(temp, 0)) < 0) {
		fprintf(stderr, "%s: Can't open tables for device \"%s\".\n",
			progname, devname);
		exit(1);
	}
	(void) read(fin, (char *)&dev, sizeof(struct dev));

	/* copy things out into globals */
	res		= dev.res;
	paperwidth	= dev.paperwidth;
	paperlength	= dev.paperlength;

	if (paperwidth==0 || paperlength==0) {
		fprintf(stderr, "%s: Size of paper not specified in DESC file for device \"%s\" (please report)", progname, devname);
		exit(1);
	}

	(void) close(fin);
}

/* Here beginneth the device-dependent bits */

/*
 *	Here is the model of the terminal's behaviour presented by the
 *	term_* functions:
 *
 *	Motions are expressed as (hpos, vpos), where both are in original
 *	device goobies, origin == 0.
 *
 *	You can set text at arbitrary bit boundaries on the screen.
 *	This is untrue - Term version 6.7 adjusts the vertical position when
 *	you switch from bitmap to character mode to try to align with a
 *	character line.
 */

/* Macros to convert original device coordinates to cursor motion coordinates
 *	device: origin == (0,0) == top left corner
 *	screen: origin == (1,1) == top left corner
 */
#define HCONVERT(h) ((h) * term_width / paperwidth + 1)
#define VCONVERT(v) (unity_aspect ? HCONVERT(v) \
				  : ((v) * term_height / paperlength + 1))


/* What to do at the start of a new page, including the first one */
t_page(n, fp)
int n;		/* page number */
FILE *fp;	/* for gobbling pages we don't want to display */
{
	static int firstpage = 1; /* is this the first page we are displaying? */

#ifdef lint
	/* I *know* I don't do anything with these yet */
	n = n;
	fp = fp;
#endif

	if (firstpage) {
		/*
		 * Calculate drawing steps.  Rounding error truncates,
		 * which makes the points fall too close together.
		 * This is better than gaps, and overstriking
		 * is eliminated by the pixel output optimiser.
		 */
		DX = paperwidth / term_width;
		term_init();
		set_bg(1);		/* select white background */

		term_clear();		/* also initialises term_[hv]pos */
		firstpage = 0;
	} else {
		waitkey();

		/* clear screen and off we go again */
		term_clear();
	}

	/*
	 * If this was the first page, we need to turn the cursor off.
	 * If it wasn't the first page, waitkey will hav turned it on.
	 */
	set_cursor(0);
}

/*
 * Print a single ascii character on the screen with its reference point at
 * (hpos, vpos)
 */
put1(c)
char c;
{
	int row, col;
	extern uchar *cur_hoff33;
	extern int cur_voff;

	if (c == ' ') return;		/* It happens */

	if (c < 33 || c > 127) return;	/* range checking for hoffset[] */

	/* Proper code, using dcumented interface to term.c: 
	col = HCONVERT(hpos) - term_font[term_curfont]->tf_hoffset[c-33];
	row = VCONVERT(vpos) - term_font[term_curfont]->tf_voffset;
	 * faster code using a couple of special-purpose externals which
	 * term.c maintains specifically for this here. */
	col = HCONVERT(hpos) - cur_hoff33[c];
	row = VCONVERT(vpos) - cur_voff;

	term_char(col, row, c);
}

put1s(s)
char *s;
{
	int i;
	int h, v;	/* screen coordinates of line-drawing loops */

	/* Ade values and little bitmaps one day ... */

	/* Fake box rule and underline so tbl output works */
	/* and rules so that ideal works */

	/* The sizes and positioning are experminental on a "normal" table
	 * being reviewed on a full-size layer.
	 */
	switch (s[0]) {
	case 'r':
		switch (s[1]) {
		case 'u': /* \(ru */
			v = VCONVERT(vpos);
			for (i = 6, h = HCONVERT(hpos); i>0; i--, h++) {
				term_dot(h, v);
			}
			break;
		}
		break;
	case 'u':
		switch (s[1]) {
		case 'l': /* \(ul */
			v = VCONVERT(vpos);
			for (i = 6, h = HCONVERT(hpos); i>0; i--, h++) {
				term_dot(h, v);
			}
			break;
		}
		break;
	case 'b':
		switch (s[1]) {
		case 'r': /* \(br */
			h = HCONVERT(hpos);
			for (i = (unity_aspect ? 11 : 5), v = VCONVERT(vpos); i>0; i--, v--) {
				term_dot(h, v);
			}
			break;
		}
		break;
	}
	
	return;
}

/* Point size change, called from ditroff parser */
setsize(size)
{
	/* arbitrary hack to give good impression most of the time */
	if (unity_aspect) {
		if (size >= 12)		set_font(TF_8x16);
		else if (size >= 10)	set_font(TF_7x10);
		else			set_font(TF_6x8);
	} else {
		if (size >= 14)		set_font(TF_8x16);
		else if (size >= 12)	set_font(TF_7x10);
		else			set_font(TF_6x8);
	}
}

/* plot a point at (hpos, vpos). Called from drawing functions in draw.c */
drawdot()
{
	term_dot(HCONVERT(hpos), VCONVERT(vpos));
}
