/*
output language from troff:
all numbers are character strings

sn	size in points
fn	font as number from 1-n
cx	ascii character x
Cxyz	funny char xyz. terminated by white space
Nn	absolute character number n on this font.  ditto
Hn	go to absolute horizontal position n
Vn	go to absolute vertical position n (down is positive)
hn	go n units horizontally (relative)
vn	ditto vertically
nnc	move right nn, then print c (exactly 2 digits!)
		(this wart is an optimization that shrinks output file size
		 about 35% and run-time about 15% while preserving ascii-ness)
Dt ...\n	draw operation 't':
	Dl x y		line from here by x,y
	Dc d		circle of diameter d with left side here
	De x y		ellipse of axes x,y with left side here
	Da dx dy dx dy	arc counter-clockwise, center at dx,dx, end at dx,dy
	D~ x y x y ...	wiggly line by x,y then x,y ...
nb a	end of line (information only -- no action needed)
w	paddable word space -- no action needed
	b = space before line, a = after
p	new page begins -- set v to 0
#...\n	comment
x ...\n	device control functions:
	x i	init
	x T s	name of device is s
	x r n h v	resolution is n/inch
		h = min horizontal motion, v = min vert
	x p	pause (can restart)
	x s	stop -- done for ever
	x t	generate trailer
	x f n s	font position n contains font s
	x H n	set character height to n
	x S n	set slant to N

	Subcommands like "i" are often spelled out like "init".
*/

#include <u.h>
#include <libc.h>
#include <draw.h>
#include <memdraw.h>
#include <bio.h>

#define hmot(n)	hpos += n
#define hgoto(n)	hpos = n
#define vmot(n)	vgoto(vpos + n)
#define vgoto(n)	vpos = n

#define	putchar(x)	Bprint(&bout, "%C", x)

int	hpos;	/* horizontal position where we are supposed to be next (left = 0) */
int	vpos;	/* current vertical position (down positive) */
char	*fontfile	= "/lib/font/bit/pelm/unicode.9x24.font";

char	*pschar(char *, char *hex, int *wid, int *ht);
void	memopenfont(char *);
int	kanji(char *);
void	Bgetstr(Biobuf *bp, char *s);
void	Bgetline(Biobuf *bp, char *s);
void	Bgetint(Biobuf *bp, int *n);

Biobuf bin, bout;
Memimage *white;

void
main(void)
{
	int c, n;
	char str[100], *args[10];
	int jfont, curfont;

	Binit(&bin, 0, OREAD);
	Binit(&bout, 1, OWRITE);
	memimageinit();
	white = allocmemimage(Rect(0, 0, 1, 1), GREY1);
	if(white == nil)
		sysfatal("allocmemimage: %r");
	memfillcolor(white, DWhite);
	white->flags |= Frepl;
	memopenfont(fontfile);

	jfont = -1;
	curfont = 1;
	while ((c = Bgetc(&bin)) >= 0) {
		switch (c) {
		case '\n':	/* when input is text */
		case ' ':
		case '\0':		/* occasional noise creeps in */
			putchar(c);
			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 */
			putchar(c);	/* digit 1 */
			n = (c-'0')*10;
			c = Bgetc(&bin);
			putchar(c);	/* digit 2 */
			n += c - '0';
			hmot(n);
			putchar(Bgetc(&bin));	/* char itself */
			break;
		case 'c':	/* single character */
			c = Bgetrune(&bin);
			if(c==' ')	/* why does this happen? it's troff - bwk */
				break;
			else if(jfont == curfont){
				Bungetrune(&bin);
				Bgetstr(&bin, str);
				kanji(str);
			}else{
				putchar('c');
				putchar(c);
			}
			break;
		case 'C':
			Bgetstr(&bin, str);
			Bprint(&bout, "C%s", str);
			break;
		case 'f':
			Bgetstr(&bin, str);
			curfont = atoi(str);
			if(curfont < 0 || curfont > 20)
				curfont = 1;	/* sanity */
			Bprint(&bout, "%c%s", c, str);
			break;
		case 'N':	/* absolute character number */
		case 's':
		case 'p':	/* new page */
			Bgetint(&bin, &n);
			Bprint(&bout, "%c%d", c, n);
			break;
		case 'H':	/* absolute horizontal motion */
			Bgetint(&bin, &n);
			Bprint(&bout, "%c%d", c, n);
			hgoto(n);
			break;
		case 'h':	/* relative horizontal motion */
			Bgetint(&bin, &n);
			Bprint(&bout, "%c%d", c, n);
			hmot(n);
			break;
		case 'V':
			Bgetint(&bin, &n);
			Bprint(&bout, "%c%d", c, n);
			vgoto(n);
			break;
		case 'v':
			Bgetint(&bin, &n);
			Bprint(&bout, "%c%d", c, n);
			vmot(n);
			break;

		case 'w':	/* word space */
			putchar(c);
			break;

		case 'x':	/* device control */
			Bgetline(&bin, str);
			Bprint(&bout, "%c%s", c, str);
			if(tokenize(str, args, 10)>2 && args[0][0]=='f' && ('0'<=args[1][0] && args[1][0]<='9')){
				if(strncmp(args[2], "Jp", 2) == 0)
					jfont = atoi(args[1]);
				else if(atoi(args[1]) == jfont)
					jfont = -1;
			}
			break;

		case 'D':	/* draw function */
		case 'n':	/* end of line */
		case '#':	/* comment */
			Bgetline(&bin, str);
			Bprint(&bout, "%c%s", c, str);
			break;
		default:
			fprint(2, "mnihongo: unknown input character %o %c\n", c, c);
			exits("error");
		}
	}
}

struct {
	int s, e;
	char *path;
	Memsubfont *f;
} fonts[128];
static int nfonts;

void
memopenfont(char *name)
{
	Biobuf *b;
	int i;
	char *s, *p, *p2;

	b = Bopen(name, OREAD);
	for(i = 0; s = Brdline(b, '\n'); i++){
		if(i == 0)
			continue;
		s[Blinelen(b)-1] = '\0';
		fonts[i].s = strtol(s, &p, 0);
		if(s == p || *p == '\0')
			goto Invalid;
		p++;
		fonts[i].e = strtol(p, &p2, 0);
		if(p == p2 || *p2 == '\0')
			goto Invalid;
		p = p2 + 1;
		if(*p == '/')
			fonts[i].path = strdup(p);
		else {
			fonts[i].path = smprint("/lib/font/bit/pelm/%s", p);
			cleanname(fonts[i].path);
		}
	}
	nfonts = i;
	if(nfonts == 0){
Invalid:
		sysfatal("%s is an invalid font file", name);
	}
	Bterm(b);
}

Memsubfont*
memgetfont(Rune r)
{
	int i;

	for(i = 0; i < nfonts; i++){
		if(r >= fonts[i].s && r < fonts[i].e){
			if(fonts[i].f == nil){
				fonts[i].f = openmemsubfont(fonts[i].path, fonts[i].s);
				if(fonts[i].f == nil)
					sysfatal("failed to open subfont: %r");
			}
			return fonts[i].f;
		}
	}
	return nil;	
}

int
kanji(char *s)		/* very special pleading */
{			/* dump as kanji char if looks like one */
	Rune r;
	char hex[500];
	int size = 10, ht, wid;

	chartorune(&r, s);
	pschar(s, hex, &wid, &ht);
	Bprint(&bout, "x X PS save %d %d m\n", hpos, vpos);
	Bprint(&bout, "x X PS currentpoint translate %d %d scale ptsize dup scale\n", size, size);
	Bprint(&bout, "x X PS %d %d true [%d 0 0 -%d 0 %d]\n",
		wid, ht, wid, wid, ht-2);	/* kludge; ought to use ->ascent */
	Bprint(&bout, "x X PS {<%s>}\n", hex);
	Bprint(&bout, "x X PS imagemask restore\n");
	return 1;
}

char*
pschar(char *s, char *hex, int *wid, int *ht)
{
	Point chpt, spt;
	Memimage *b;
	Memsubfont *f;
	uchar rowdata[100];
	char *hp = hex;
	int y, i;
	Rune r;

	chartorune(&r, s);
	f = memgetfont(r);
	chpt = memsubfontwidth(f, s);		/* bounding box of char */
	*wid = ((chpt.x+7) / 8) * 8;
	*ht = chpt.y;
	/* postscript is backwards to video, so draw white (ones) on black (zeros) */
	white->clipr = Rpt(ZP, chpt);
	b = allocmemimage(white->clipr, GREY1);
	if(b == nil)
		sysfatal("allocmemimage: %r");
	memfillcolor(b, DBlack);
	spt = memimagestring(b, Pt(0,0), white, ZP, f, s);	/* put it there */
/* Bprint(&bout, "chpt %P, spt %P, wid,ht %d,%d\n", chpt, spt, *wid, *ht);
/* Bflush(&bout); */
	for (y = 0; y < chpt.y; y++) {	/* read bits a row at a time */
		memset(rowdata, 0, sizeof rowdata);
		unloadmemimage(b, Rect(0, y, chpt.x, y+1), rowdata, sizeof rowdata);
		for (i = 0; i < spt.x; i += 8) {	/* 8 == byte */
			sprint(hp, "%2.2x", rowdata[i/8]);
			hp += 2;
		}
	}
	*hp = 0;
	freememimage(b);
	return hex;
}


void
Bgetstr(Biobuf *bp, char *s)	/* get a string */
{
	int c;

	while ((c = Bgetc(bp)) >= 0) {
		if (c == ' ' || c == '\t' || c == '\n') {
			Bungetc(bp);
			break;
		}
		*s++ = c;
	}
	*s = 0;
}

void
Bgetline(Biobuf *bp, char *s)	/* get a line, including newline */
{
	int c;

	while ((c = Bgetc(bp)) >= 0) {
		*s++ = c;
		if (c == '\n')
			break;
	}
	*s = 0;
}

void
Bgetint(Biobuf *bp, int *n)	/* get an integer */
{
	double d;

	Bgetd(bp, &d);
	*n = d;
}
