#include <u.h>
#include <libc.h>
#include <draw.h>
#include <thread.h>
#include <bio.h>
#include <cursor.h>
#include "page.h"

int resizing;
int mknewwindow;
int doabort;
int chatty;
int reverse = -1;
int goodps = 1;
int ppi = 100;
int teegs = 0;
int truetoboundingbox;
int textbits=4, gfxbits=4;
int stdinfd;
int truecolor;
int imagemode;
int notewatcher;
int notegp;
int fitwin;
char tempfile[40];

int
watcher(void *v, char *x)
{
	USED(v);
	if(strcmp(x, "die") != 0)
		postnote(PNGROUP, notegp, x);
	threadexitsall(0);
	return 0;
}

void
watcherproc(void *v)
{
	threadnotify(watcher, 1);
	for(;;)
		sleep(1000);
}

int
bell(void *u, char *x)
{
	if(x && strcmp(x, "hangup") == 0)
		threadexitsall(0);

	if(x && strstr(x, "die") == nil)
		fprint(2, "postnote %d: %s\n", getpid(), x);

	/* alarms come from the gs monitor */
	if(x && strstr(x, "alarm")){
		postnote(PNGROUP, getpid(), "die (gs error)");
		postnote(PNPROC, notewatcher, "die (gs error)");
	}

	/* function mentions u so that it's in the stack trace */
	if((u == nil || u != x) && doabort)
		abort();

/*	fprint(2, "exiting %d\n", getpid()); */
	wexits("note");
	return 0;
}

static int
afmt(Fmt *fmt)
{
	char *s;

	s = va_arg(fmt->args, char*);
	if(s == nil || s[0] == '\0')
		return fmtstrcpy(fmt, "");
	else
		return fmtprint(fmt, "%#q", s);
}

void
usage(void)
{
	fprint(2, "usage: page [-biRrwf] [-p ppi] file...\n");
	wexits("usage");
}

void
cleanup(void)
{
	remove(tempfile);
}

void
threadmain(int argc, char **argv)
{
	Document *doc;
	Biobuf *b;
	enum { Ninput = 16 };
	uchar buf[Ninput+1];
	int readstdin, haveppi;

	haveppi = 0;
	ARGBEGIN{
	/* "temporary" debugging options */
	case 'P':
		goodps = 0;
		break;
	case 'v':
		chatty++;
		break;
	case 'V':
		teegs++;
		break;
	case 'a':
		doabort++;
		break;
	case 'T':
		textbits = atoi(EARGF(usage()));
		gfxbits = atoi(EARGF(usage()));
		break;

	/* real options */
	case 'R':
		resizing = 1;
		break;
	case 'r':
		reverse = 1;
		break;
	case 'p':
		haveppi = 1;
		ppi = atoi(EARGF(usage()));
		break;
	case 'b':
		truetoboundingbox = 1;
		break;
	case 'w':
		fprint(2, "warning: page -w only supported on x11 systems\n");
		resizing = 1;
		break;
	case 'i':
		imagemode = 1;
		break;
	case 'W':
		winsize = EARGF(usage());
		break;
	case 'f':
		fitwin = 1;
		break;
	default:
		usage();
	}ARGEND;

	notegp = getpid();

	notewatcher = proccreate(watcherproc, NULL, 1024);
	if(notewatcher == -1){
		sysfatal("proccreate");
		threadexitsall(0);
	}

	rfork(RFNOTEG);
	threadnotify(bell, 1);

	readstdin = 0;
	if(imagemode == 0 && argc == 0){
		readstdin = 1;
		stdinfd = dup(0, -1);
		close(0);
		open("/dev/tty", OREAD);
	}

	quotefmtinstall();
	fmtinstall('a', afmt);

	fmtinstall('R', Rfmt);
	fmtinstall('P', Pfmt);
	/*
	if(mknewwindow)
		newwin(); */

	if(readstdin){
		b = nil;
		if(readn(stdinfd, buf, Ninput) != Ninput){
			fprint(2, "page: short read reading %s\n", argv[0]);
			wexits("read");
		}

		atexit(cleanup);
	}else if(argc != 0){
		if(!(b = Bopen(argv[0], OREAD))) {
			fprint(2, "page: cannot open \"%s\"\n", argv[0]);
			wexits("open");
		}

		if(Bread(b, buf, Ninput) != Ninput) {
			fprint(2, "page: short read reading %s\n", argv[0]);
			wexits("read");
		}
	}else
		b = nil;

	if(initdraw(0, 0, "page") < 0){
		fprint(2, "page: initdraw failed: %r\n");
		wexits("initdraw");
	}
	display->locking = 1;
	ppi = scalesize(display, ppi);

	buf[Ninput] = '\0';
	if(imagemode)
		doc = initgfx(nil, 0, nil, nil, 0);
	else if(strncmp((char*)buf, "%PDF-", 5) == 0)
		doc = initpdf(b, argc, argv, buf, Ninput);
	else if(strncmp((char*)buf, "\x04%!", 2) == 0)
		doc = initps(b, argc, argv, buf, Ninput);
	else if(buf[0] == '\x1B' && strstr((char*)buf, "@PJL"))
		doc = initps(b, argc, argv, buf, Ninput);
	else if(strncmp((char*)buf, "%!", 2) == 0)
		doc = initps(b, argc, argv, buf, Ninput);
	else if(strcmp((char*)buf, "\xF7\x02\x01\x83\x92\xC0\x1C;") == 0)
		doc = initdvi(b, argc, argv, buf, Ninput);
	else if(strncmp((char*)buf, "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1", 8) == 0)
		doc = initmsdoc(b, argc, argv, buf, Ninput);
	else if(strncmp((char*)buf, "x T ", 4) == 0)
		doc = inittroff(b, argc, argv, buf, Ninput);
	else {
		if(haveppi) {
			fprint(2, "page: you can't specify -p with graphic files\n");
			wexits("-p and graphics");
		}
		doc = initgfx(b, argc, argv, buf, Ninput);
	}

	if(doc == nil) {
		fprint(2, "page: error reading file: %r\n");
		wexits("document init");
	}

	if(doc->npage < 1 && !imagemode) {
		fprint(2, "page: no pages found?\n");
		wexits("pagecount");
	}

	if(reverse == -1) /* neither cmdline nor ps reader set it */
		reverse = 0;

	truecolor = screen->depth > 8;
	viewer(doc);
	wexits(0);
}

void
wexits(char *s)
{
	if(s && *s && strcmp(s, "note") != 0 && mknewwindow)
		sleep(10*1000);
	postnote(PNPROC, notewatcher, "die");
	postnote(PNGROUP, getpid(), "die");
	threadexitsall(s);
}
