#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <bio.h>
#include "proof.h"

char	fname[NFONT][20];	/* font names */
char lastload[NFONT][20];	/* last file name prefix loaded for this font */
Font	*fonttab[NFONT][NSIZE];	/* pointers to fonts */
int	fmap[NFONT];		/* what map to use with this font */

static void	loadfont(int, int);
static void	fontlookup(int, char *);
static void	buildxheight(Biobuf*);
static void	buildmap(Biobuf*);
static void	buildtroff(char *);
static void	addmap(int, char *, int);
static char	*map(Rune*, int);
static void	scanstr(char *, char *, char **);

int	specfont;	/* somehow, number of special font */

#define	NMAP	5
#define	QUICK	2048	/* char values less than this are quick to look up */
#define	eq(s,t)	strcmp((char *) s, (char *) t) == 0

int	curmap	= -1;	/* what map are we working on */

typedef struct Link Link;
struct Link	/* link names together */
{
	uchar	*name;
	int	val;
	Link	*next;
};

typedef struct Map Map;
struct Map	/* holds a mapping from uchar name to index */
{
	double	xheight;
	Rune	quick[QUICK];	/* low values get special treatment */
	Link	*slow;	/* other stuff goes into a link list */
};

Map	charmap[5];

typedef struct Fontmap Fontmap;
struct Fontmap	/* mapping from troff name to filename */
{
	char	*troffname;
	char	*prefix;
	int	map;		/* which charmap to use for this font */
	char	*fallback;	/* font to look in if can't find char here */
};

Fontmap	fontmap[100];
int	pos2fontmap[NFONT];	/* indexed by troff font position, gives Fontmap */
int	nfontmap	= 0;	/* how many are there */


void
dochar(Rune r[])
{
	char *s, *fb;
	Font *f;
	Point p;
	int fontno, fm, i;
	char buf[32];

	fontno = curfont;
	if((s = map(r, curfont)) == 0){		/* not on current font */
		if ((s = map(r, specfont)) != 0)	/* on special font */
			fontno = specfont;
		else{
			/* look for fallback */
			fm = pos2fontmap[curfont];
			fb = fontmap[fm].fallback;
			if(fb){
				/* see if fallback is mounted */
				for(i = 0; i < NFONT; i++){
					if(eq(fb, fontmap[pos2fontmap[i]].troffname)){
						s = map(r, i);
						if(s){
							fontno = i;
							goto found;
						}
					}
				}
			}
			/* no such char; use name itself on defont */
			/* this is not a general solution */
			p.x = hpos/DIV + xyoffset.x + offset.x;
			p.y = vpos/DIV + xyoffset.y + offset.y;
			p.y -= font->ascent;
			snprint(buf, sizeof buf, "%S", r);
			string(screen, p, display->black, ZP, font, buf);
			return;
		}
	}
    found:
	p.x = hpos/DIV + xyoffset.x + offset.x;
	p.y = vpos/DIV + xyoffset.y + offset.y;
	while ((f = fonttab[fontno][cursize]) == 0)
		loadfont(fontno, cursize);
	p.y -= f->ascent;
	dprint(2, "putting %S at %d,%d font %d, size %d\n", r, p.x, p.y, fontno, cursize);
	string(screen, p, display->black, ZP, f, s);
}

/* imported from libdraw/arith.c to permit an extern log2 function */
static int log2[] = {
	-1, 0, 1, -1, 2, -1, -1, -1, 3, -1, -1, -1, -1, -1, -1, -1, 4,
	-1, -1, -1, -1, -1, -1, -1, 4 /* BUG */, -1, -1, -1, -1, -1, -1, -1, 5
};

static void
loadfont(int n, int s)
{
	char file[256];
	int i, fd, t, deep;
	static char *try[3] = {"", "times/R.", "pelm/"};
	Subfont *f;
	Font *ff;

	try[0] = fname[n];
	for (t = 0; t < 3; t++){
		i = s * mag * charmap[fmap[n]].xheight/0.72;	/* a pixel is 0.72 points */
		if (i < MINSIZE)
			i = MINSIZE;
		dprint(2, "size %d, i %d, mag %g\n", s, i, mag);
		for(; i >= MINSIZE; i--){
			/* if .font file exists, take that */
			snprint(file, sizeof file, "%s/%s%d.font",
				libfont, try[t], i);
			ff = openfont(display, file);
			if(ff != 0){
				fonttab[n][s] = ff;
				dprint(2, "using %s for font %d %d\n", file, n, s);
				return;
			}
			/* else look for a subfont file */
			for (deep = log2[screen->depth]; deep >= 0; deep--){
				snprint(file, sizeof file, "%s/%s%d.%d",
					libfont, try[t], i, deep);
				dprint(2, "trying %s for %d\n", file, i);
				if ((fd = open(file, 0)) >= 0){
					f = readsubfont(display, file, fd, 0);
					if (f == 0) {
						fprint(2, "can't rdsubfontfile %s: %r\n", file);
						exits("rdsubfont");
					}
					close(fd);
					ff = mkfont(f, 0);
					if(ff == 0){
						fprint(2, "can't mkfont %s: %r\n", file);
						exits("rdsubfont");
					}
					fonttab[n][s] = ff;
					dprint(2, "using %s for font %d %d\n", file, n, s);
					return;
				}
			}
		}
	}
	fprint(2, "can't find font %s.%d or substitute, quitting\n", fname[n], s);
	exits("no font");
}

void
loadfontname(int n, char *s)
{
	int i;
	Font *f, *g = 0;

	if (strcmp(s, fname[n]) == 0)
		return;
	if(fname[n] && fname[n][0]){
		if(lastload[n] && strcmp(lastload[n], fname[n]) == 0)
			return;
		strcpy(lastload[n], fname[n]);
	}
	fontlookup(n, s);
	for (i = 0; i < NSIZE; i++)
		if (f = fonttab[n][i]){
			if (f != g) {
				freefont(f);
				g = f;
			}
			fonttab[n][i] = 0;
		}
}

void
allfree(void)
{
	int i;

	for (i=0; i<NFONT; i++)
		loadfontname(i, "??");
}


void
readmapfile(char *file)
{
	Biobuf *fp;
	char *p, cmd[100];

	if ((fp=Bopen(file, OREAD)) == 0){
		fprint(2, "proof: can't open map file %s\n", file);
		exits("urk");
	}
	while((p=Brdline(fp, '\n')) != 0) {
		p[Blinelen(fp)-1] = 0;
		scanstr(p, cmd, 0);
		if(p[0]=='\0' || eq(cmd, "#"))	/* skip comments, empty */
			continue;
		else if(eq(cmd, "xheight"))
			buildxheight(fp);
		else if(eq(cmd, "map"))
			buildmap(fp);
		else if(eq(cmd, "special"))
			buildtroff(p);
		else if(eq(cmd, "troff"))
			buildtroff(p);
		else
			fprint(2, "weird map line %s\n", p);
	}
	Bterm(fp);
}

static void
buildxheight(Biobuf *fp)	/* map goes from char name to value to print via *string() */
{
	char *line;

	line = Brdline(fp, '\n');
	if(line == 0){
		fprint(2, "proof: bad map file\n");
		exits("map");
	}
	charmap[curmap].xheight = atof(line);
}

static void
buildmap(Biobuf *fp)	/* map goes from char name to value to print via *string() */
{
	uchar *p, *line, ch[100];
	int val;
	Rune r;

	curmap++;
	if(curmap >= NMAP){
		fprint(2, "proof: out of char maps; recompile\n");
		exits("charmap");
	}
	while ((line = Brdline(fp, '\n'))!= 0){
		if (line[0] == '\n')
			return;
		line[Blinelen(fp)-1] = 0;
		scanstr((char *) line, (char *) ch, (char **) &p);
		if (ch[0] == '\0') {
			fprint(2, "bad map file line '%s'\n", (char*)line);
			continue;
		}
		val = strtol((char *) p, 0, 10);
dprint(2, "buildmap %s (%x %x) %s %d\n", (char*)ch, ch[0], ch[1], (char*)p, val);
		chartorune(&r, (char*)ch);
		if(utflen((char*)ch)==1 && r<QUICK)
			charmap[curmap].quick[r] = val;
		else
			addmap(curmap, strdup((char *) ch), val);	/* put somewhere else */
	}
}

static void
addmap(int n, char *s, int val)	/* stick a new link on */
{
	Link *p = (Link *) malloc(sizeof(Link));
	Link *prev = charmap[n].slow;

	if(p == 0)
		exits("out of memory in addmap");
	p->name = (uchar *) s;
	p->val = val;
	p->next = prev;
	charmap[n].slow = p;
}

static void
buildtroff(char *buf)	/* map troff names into bitmap filenames */
{				/* e.g., R -> times/R., I -> times/I., etc. */
	char *p, cmd[100], name[200], prefix[400], fallback[100];

	scanstr(buf, cmd, &p);
	scanstr(p, name, &p);
	scanstr(p, prefix, &p);
	while(*p!=0 && isspace(*p))
		p++;
	if(*p != 0){
		scanstr(p, fallback, &p);
		fontmap[nfontmap].fallback = strdup(fallback);
	}else
		fontmap[nfontmap].fallback = 0;
	fontmap[nfontmap].troffname = strdup(name);
	fontmap[nfontmap].prefix = strdup(prefix);
	fontmap[nfontmap].map = curmap;
	dprint(2, "troff name %s is bitmap %s map %d in slot %d fallback %s\n",
		name, prefix, curmap, nfontmap, fontmap[nfontmap].fallback?
		fontmap[nfontmap].fallback: "<null>");
	nfontmap++;
}

static void
fontlookup(int n, char *s)	/* map troff name of s into position n */
{
	int i;

	for(i = 0; i < nfontmap; i++)
		if (eq(s, fontmap[i].troffname)) {
			strcpy(fname[n], fontmap[i].prefix);
			fmap[n] = fontmap[i].map;
			pos2fontmap[n] = i;
			if (eq(s, "S"))
				specfont = n;
			dprint(2, "font %d %s is %s\n", n, s, fname[n]);
			return;
		}
	/* god help us if this font isn't there */
}


static char *
map(Rune rp[], int font)	/* figure out mapping for char in this font */
{
	static char s[100];
	unsigned m;
	char c[32];
	Link *p;
	Rune r;

	if((unsigned)font >= NFONT) {
		dprint(2, "map: font %ud >= NFONT (%d)\n", font, NFONT);
		return 0;
	}
	m = fmap[font];
	if(m >= nelem(charmap)) {
		dprint(2, "map: fmap[font] %ud >= nelem(charmap) (%d)\n",
			m, nelem(charmap));
		return 0;
	}
	if(rp[1] == 0 && rp[0] < QUICK)		/* fast lookup */
		r = charmap[m].quick[rp[0]];
	else {			/* high-valued or compound character name */
		snprint(c, sizeof c, "%S", rp);
		r = 0;
		for (p = charmap[m].slow; p; p = p->next)
			if(eq(c, p->name)){
				r = p->val;
				break;
			}
	}
	if(r == 0){	/* not there */
		dprint(2, "didn't find %S font# %d\n", rp, font);
		return 0;
	}
	dprint(2, "map %S to %s font# %d\n", rp, s, font);
	s[runetochar(s, &r)] = 0;
	return s;
}

static void
scanstr(char *s, char *ans, char **ep)
{
	for (; isspace((uchar) *s); s++)
		;
	for (; *s!=0 && !isspace((uchar) *s); )
		*ans++ = *s++;
	*ans = 0;
	if (ep)
		*ep = s;
}
