/* $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 */

#ifndef lint
static char rcsid[] = "$Header: io.c 2.0 86/01/28 $";
#endif

/*
 * io.c: font file I/O subroutines for fed.
 */

#include "fed.h"

getglyph(c)
char c;
{
	register int i, j;
	int windno;
	int vertoff, horoff;
	char *tmp;

	if (trace)
		fprintf(trace, "\n\ngetglyph(%s)\n", rdchar(c));
	if (disptable[c].nbytes == 0) {
		if (trace)
			fprintf(trace, "no such char: %s\n", rdchar(c));
		sprintf(msgbuf, "no such character: %s", rdchar(c));
		message(msgbuf);
		return;
	}

	curchar = c;
	turnofcurs();
	if (cht[curchar].wherewind >= 0) {
		/* It's already in a window.  Don't have to do much. */
		if (trace)
			fprintf(trace, "already in %d\n", cht[curchar].wherewind);
		windno = cht[curchar].wherewind;
		/* Put a box around the current window */
		if (windno != curwind) {
			drawbox(base[curwind].r-1, base[curwind].c-1, 0, GLROW+2, GLCOL+2);
			drawbox(base[windno].r-1, base[windno].c-1, 1, GLROW+2, GLCOL+2);
		}
		curwind = windno;
		syncwind(windno);
		/* should center base */
	} else {
		/*
		 * Not on screen.  First find a suitable window,
		 * using round robin.
		 */
		windno = nextwind;
		if (trace)
			fprintf(trace, "chose window %d\n", windno);
		if (++nextwind >= NWIND)
			nextwind = 0;
#ifdef TWOWIND
		/*
		 * This is for debugging what happens when we run out
		 * of windows.
		 */
		if (nextwind >= 2)
			nextwind = 0;
#endif

		/* Put a box around the current window */
		if (windno != curwind) {
			if (trace)
				fprintf(trace, "drawbox (%d %d)\n", base[windno].r-1, base[windno].c-1);
			drawbox(base[curwind].r-1, base[curwind].c-1, 0, GLROW+2, GLCOL+2);
			drawbox(base[windno].r-1, base[windno].c-1, 1, GLROW+2, GLCOL+2);
		}

		/* Print the char at the lower left of the window */
		sprintf(msgbuf, "%s", rdchar(curchar));
		dispmsg(msgbuf, base[windno].c, base[windno].r-11, 2);
		
		/* Now make room in the window */
		if (wind[windno].onscreen == NULL) {
			/* Brand new window, have to allocate space */
			wind[windno].onscreen = newmat(GLROW, GLCOL);
		} else {
			/* Save prev glyph for later */
			cht[wind[windno].used].whereat = wind[windno].val;
			cht[wind[windno].used].wherewind = -2;
			if (trace)
				fprintf(trace, "windno=%s, wind[windno].used=%d, cht[..].wherewind set to -2\n", rdchar(windno), wind[windno].used);
		}
		if (wind[windno].undval != NULL) {
			if (trace)
				fprintf(trace, "getglyph frees undo: %x\n", wind[windno].undval);
			free(wind[windno].undval);
		}
		wind[windno].undval = NULL;
		wind[windno].used = curchar;

		/*
		 * Vertical & horizontal offsets.  Line up the baseline
		 * of the char at BASELINE from bottom, but center
		 * horizontally.
		 */
		vertoff = GLROW - BASELINE - disptable[curchar].up;
		/* Check to see if the glyph is being nosed off the edge. */
		if (vertoff < 0) {
			vertoff = 0;
		} else if (vertoff + disptable[curchar].up + disptable[curchar].down >= GLROW) {
			vertoff = GLROW - disptable[curchar].up - disptable[curchar].down;
		}
		horoff = (GLCOL-(disptable[curchar].left+disptable[curchar].right)) / 2;
		wind[windno].val = findbits(curchar, GLROW, GLCOL, horoff, vertoff, &curs_r, &curs_c);
		cht[curchar].rcent = curs_r;
		cht[curchar].ccent = curs_c;
		curwind = windno;
		cht[curchar].wherewind = windno;
		syncwind(windno);
	}
}

/*
 * writeback: write the font back to the file at the end of editing.
 * Also have to write width table.
 */
writeback()
{
	writefont(fontfile);
}

/*
 * writefont: write current font on file fname.
 */
writefont(fname)
char *fname;
{
	register int i, j;
	register int c;
	FILE *fntout;
	int bytes;
	bitmat tmp;
	int nextoff = 0;
	int charcount, bytecount;
	extern char *sys_errlist[];
	extern int errno;

	if (trace)
		fprintf(trace, "writefont(%s)\n", fname);
	/*
	 * The following unlink is important because we are about to
	 * do an fopen( , "w") on fname.  We still have fontdes open
	 * for reading.  If we don't do the unlink the fopen will truncate
	 * the file and subsequent reads will fail.  If we do the unlink
	 * the file won't go away until it is closed, so we can still
	 * read from the old version.
	 */
	if (strcmp(fname, fontfile)==0 && unlink(fname) < 0) {
		sprintf(msgbuf, "unlink %s: %s", fname, sys_errlist[errno]);
		error(msgbuf);
	}

	fntout = fopen(fname, "w");
	if (fntout == NULL) {
		sprintf(msgbuf, "%s: %s", fname, sys_errlist[errno]);
		if (trace)
			fprintf(trace, "%s\n", msgbuf);
		error(msgbuf);
	}
	sprintf(msgbuf, "\"%s\"", fname);
	message(msgbuf);
	fflush(stdout);

	fwrite(&FontHeader, sizeof FontHeader, 1, fntout);
	fwrite(&disptable[0], sizeof disptable, 1, fntout);
	charcount = 0; bytecount = fbase;
	for (c=0; c<256; c++)
		if (disptable[c].nbytes || cht[c].wherewind != -1) {
			if (trace)
				fprintf(trace, "char %s, nbytes %d, wherewind %d.. ", rdchar(c), disptable[c].nbytes, cht[c].wherewind);
			packmat(c, &tmp, &bytes);
			disptable[c].addr = nextoff;
			disptable[c].nbytes = bytes;
			if (trace)
				fprintf(trace, "offset %d size %d\n", nextoff, bytes);
			nextoff += bytes;
			fwrite(tmp, bytes, 1, fntout);
			charcount++;
			bytecount += bytes;
		}
	FontHeader.size = nextoff;
	fseek(fntout, 0L, 0);
	fwrite(&FontHeader, sizeof FontHeader, 1, fntout);
	fwrite(&disptable[0], sizeof disptable, 1, fntout);

	/* Should fix the width tables here */
	fclose(fntout);
	sprintf(msgbuf, "%s %d glyphs, %d bytes", fname, charcount, bytecount);
	message(msgbuf);
	changes = 0;
}

/*
 * make a packed matrix of the bits for char c.
 * return the matrix ptr in result and the size in bytes in nbytes.
 */
packmat(c, result, nbytes)
int c;
bitmat *result;
int *nbytes;
{
	register int i, j;
	bitmat wp;
	int nb, nr, nc;
	int rmin, cmin, rmax, cmax;
	static char tmp[WINDSIZE];

	if (cht[c].wherewind == -1) {
		/* It has never been read from file.  Just copy from file. */
		nb = disptable[c].nbytes;
		fseek(fontdes, (long) fbase+disptable[c].addr, 0);
		fread(tmp, nb, 1, fontdes);
	} else {
		if (cht[c].wherewind == -2)
			wp = cht[c].whereat;
		else
			wp = wind[cht[c].wherewind].val;
		minmax(wp, GLROW, GLCOL, &rmin, &cmin, &rmax, &cmax);
		nr = rmax-rmin+1; nc = cmax-cmin+1;
		zermat(tmp, nr, nc);
		for (i=rmin; i<=rmax; i++)
			for (j=cmin; j<=cmax; j++) {
				setmat(tmp, nr, nc, i-rmin, j-cmin,
					mat(wp, GLROW, GLCOL, i, j));
			}
		nb = ((nc + 7) >> 3) * nr;
		disptable[c].up = cht[c].rcent - rmin;
		disptable[c].down = rmax - cht[c].rcent + 1;
		disptable[c].left = cht[c].ccent - cmin;
		disptable[c].right = cmax - cht[c].ccent + 1;
		if (trace) {
			fprintf(trace, "rmax=%d, rcent=%d, rmin=%d, cmax=%d, ccent=%d, cmin=%d, ", rmax, cht[c].rcent, rmin, cmax, cht[c].ccent, cmin);
			fprintf(trace, "up=%d, down=%d, left=%d, right=%d\n", disptable[c].up, disptable[c].down, disptable[c].left, disptable[c].right);
		}
	}
	*result = tmp;
	*nbytes = nb;
	if (trace)
		fprintf(trace, "nbytes = %d, ", nb);
	return;
}

/*
 * editfont: make the file fname be the current focus of attention,
 * including reading it into the buffer.
 */
editfont(fname)
char *fname;
{
	register char *cp;

	clearfont();
	editing = 1;
	truename(fname, fontfile);
	fontdes = fopen(fontfile, "r");
	readfont(fontfile, 0, 255);

	/*
	 * Figure out the point size, and make a guess as to the
	 * appropriate width of the heavy pen.
	 */
	for (cp=fontfile; *cp && *cp!='.'; cp++)
		;
	if (*cp) {
		pointsize = atoi(++cp);
		setpen(pointsize>30?3 : pointsize>15?2 : pointsize>8?1 : 0);
	} else {
		pointsize = 0;
		setpen(2);
	}
}

/*
 * readfont: read in a font, overlaying the current font.
 * also used to edit a font by clearing first.
 *
 * Conflicts are handled interactively.
 */
readfont(fname, c1, c2)
char *fname;
int c1, c2;
{
	register int i;
	register char *cp;
	struct dispatch d;
	char choice, mode = 0;
	FILE *hold_fontdes;
	int horoff, vertoff;
	long ftsave;

	hold_fontdes = fontdes;
	fontdes = fopen(fname, "r");
	if (fontdes == NULL) {
		sprintf(msgbuf, "%s not found", fname);
		fontdes = hold_fontdes;
		error(msgbuf);
	}
	fread(&FontHeader, sizeof FontHeader, 1, fontdes);
	fseek(fontdes, c1*sizeof d, 1);	/* skip over unread chars */
	ftsave = ftell(fontdes);
	for (i=c1; i<=c2; i++) {
		fseek(fontdes, ftsave, 0);
		fread(&d, sizeof d, 1, fontdes);
		ftsave = ftell(fontdes);
		/* Decide which of the two to take */
		if (d.nbytes == 0)
			continue;	/* We take the one in the buffer */
		if (disptable[i].nbytes > 0) {
			/* Conflict */
			switch(mode) {
			case 'f':
				/* fall through */
				break;
			case 'b':
				continue;
			default:
				sprintf(msgbuf, "%s <file> or <buffer>", rdchar(i));
				message(msgbuf);
				choice = inchar();
				switch(choice) {
				case 'F':
					mode = 'f';
				default:
				case 'f':
					break;
				case 'B':
					mode = 'b';
				case 'b':
					continue;
				}
			}
		}
		disptable[i] = d;	/* We take the one in the file */
		cht[i].nrow = d.up + d.down;
		cht[i].ncol = d.left + d.right;
		if (!editing && disptable[i].nbytes) {
			horoff = (GLCOL-(disptable[i].left+disptable[i].right))/2;
			vertoff = GLROW - BASELINE - disptable[i].up;
			/* Check to see if the glyph is being nosed off the edge. */
			if (vertoff < 0) {
				vertoff = 0;
			} else if (vertoff + disptable[curchar].up + disptable[curchar].down >= GLROW) {
				vertoff = GLROW - disptable[curchar].up - disptable[curchar].down;
			}
			if (cht[i].wherewind >= 0) {
				/* The old glyph is in a window - destroy it */
				wind[cht[i].wherewind].used = -1;
			}
			cht[i].wherewind = -1;
			cht[i].whereat = findbits(i, GLROW, GLCOL, horoff, vertoff, &cht[i].rcent, &cht[i].ccent);
			cht[i].wherewind = -2;
			if (trace)
				fprintf(trace, "setting cht[%d].wherewind to -2 in readfont\n", i);
		} else
			cht[i].wherewind = -1;
	}
	fbase = sizeof FontHeader + sizeof disptable;	/* ftell(fontdes) */

	sprintf(msgbuf, "\"%s\", raster data %d bytes, width %d, height %d, xtend %d", fname, FontHeader.size, FontHeader.maxx, FontHeader.maxy, FontHeader.xtend);

	fontdes = hold_fontdes;
	message(msgbuf);
}

/*
 * Figure out the true name of the font file, given that
 * the abbreviated name is fname.  The result is placed
 * in the provided buffer result.
 */
truename(fname, result)
char *fname;
char *result;
{
	FILE *t;

	strcpy(result, fname);
	if ((t = fopen(result, "r")) == NULL) { 
		sprintf(result,"/usr/lib/vfont/%s",fname);
		if ((t = fopen(result, "r")) == NULL) { 
			strcpy(result, fname);
			sprintf(msgbuf, "Can't find %s\n",fname);
			error(msgbuf);
		}
	}
	fclose(t);
}


/*
 * clearfont: delete all characters in the current font.
 */
clearfont()
{
	register int i;

	if (fontdes)
		fclose(fontdes);
	for (i=0; i<256; i++) {
		cht[i].wherewind = -1;
		disptable[i].addr = 0;
		disptable[i].nbytes = 0;
		disptable[i].up = 0;
		disptable[i].down = 0;
		disptable[i].left = 0;
		disptable[i].right = 0;
		disptable[i].width = 0;
	}
}

/*
 * fileiocmd: do a file I/O command.  These all take optional file
 * names, defaulting to the current file.
 */
fileiocmd(cmd)
char cmd;
{
	char fname[100], truefname[100];

	readline("file: ", fname, sizeof fname);
	if (fname[0] == 0 || fname[0] == ' ')
		strcpy(fname, fontfile);
	switch(cmd) {
	case 'E':
		confirm();
		editfont(fname);
		break;

	case 'N':
		if (changes)
			writeback();
		editfont(fname);
		break;

	case 'R':
		editing = 0;
		truename(fname, truefname);
		readfont(truefname, 0, 255);
		changes++;
		break;

	case 'W':
		editing = 0;
		writefont(fname);
		break;
	}
	if (editing)
		changes = 0;
}

/*
 * readchars: read in a partial font (the P command).
 */
readchars()
{
	int c1, c2;
	char fnamebuf[100];
	char truebuf[100];
	char buf[5];

	message("Partial read <firstchar>");
	c1 = inchar();
	sprintf(msgbuf, "Partial read %s thru <lastchar>", rdchar(c1));
	message(msgbuf);
	c2 = inchar();
	strcpy(buf, rdchar(c1));
	sprintf(msgbuf, "Partial read %s thru %s from file: ", buf, rdchar(c2));
	readline(msgbuf, fnamebuf, sizeof fnamebuf);
	editing = 0;
	if (fnamebuf[0] == 0 || fnamebuf[0] == ' ')
		strcpy(fnamebuf, fontfile);
	truename(fnamebuf, truebuf);
	changes++;
	readfont(truebuf, c1, c2);
}
