#include <u.h>
#include <libc.h>
#include <bio.h>
#include <thread.h>
#include <regexp.h>

#include "mail.h"

typedef struct Fn	Fn;

struct Fn {
	char *name;
	void (*fn)(Comp *, char **, int);
};

void
execmarshal(void *p)
{
	Comp *c;
	char *av[8];
	int na;

	c = p;
	rfork(RFFDG);
	dup(c->fd[0], 0);
	close(c->fd[0]);
	close(c->fd[1]);

	na = 0;
	av[na++] = "marshal";
	av[na++] = "-8";
	if(savebox != nil){
		av[na++] = "-S";
		av[na++] = savebox;
	}
	if(c->rpath != nil){
		av[na++] = "-R";
		av[na++] = c->rpath;
	}
	av[na] = nil;
	assert(na < nelem(av));
	procexec(c->sync, "/bin/upas/marshal", av);
}

static void
postmesg(Comp *c, char **, int nf)
{
	char *buf, wpath[64], *path;
	int n, fd;
	Mesg *m;

	snprint(wpath, sizeof(wpath), "/mnt/acme/%d/body", c->id);
	if(nf != 0){
		fprint(2, "Post: too many args\n");
		return;
	}
	if((fd = open(wpath, OREAD)) == -1){
		fprint(2, "open body: %r\n");
		return;
	}
	if(pipe(c->fd) == -1)
		sysfatal("pipe: %r");

	c->sync = chancreate(sizeof(ulong), 0);
	procrfork(execmarshal, c, Stack, RFNOTEG);
	recvul(c->sync);
	chanfree(c->sync);
	close(c->fd[0]);

	buf = emalloc(Bufsz);
	while((n = read(fd, buf, Bufsz)) > 0)
		if(write(c->fd[1], buf, n) != n)
			break;
	write(c->fd[1], "\n", 1);
	close(c->fd[1]);
	close(fd);
	if(n == -1)
		return;

	if(fprint(c->ctl, "name %s:Sent\n", c->path) == -1)
		sysfatal("write ctl: %r");
	if(c->replyto != nil){
		if((m = mesglookup(c->rname, c->rdigest)) == nil)
			return;
		m->flags |= Fresp;
		path = estrjoin(mbox.path, "/", m->name, "/flags", nil);
		if((fd = open(path, OWRITE)) != -1){
			fprint(fd, "+a");
			close(fd);
		}
		mbredraw(m, 0, 0);
		free(path);
	}
	fprint(c->ctl, "clean\n");
}

static void
compquit(Comp *c, char **, int)
{
    /* Read acme dirty flag from compose window ctl file */
	char d;
	pread(c->ctl, &d, 1, 58);

	if(d == '1' && c->quitting == 0)
		fprint(2, "composing message\n");
	if(d != '1')
		c->quitting++;
	c->quitting++;
}

static Fn compfn[] = {
	{"Post", postmesg},
	{"Del", compquit},
	{nil},
};

static void
compmain(void *cp)
{
	char *f[32];
	int nf;
	Event ev;
	Comp *c, **pc;
	Fn *p;

	c = cp;
	c->quitting = 0;
	c->qnext = mbox.opencomp;
	mbox.opencomp = c;
	mbox.nopen++;
	while(c->quitting < 2){
		if(winevent(c, &ev) != 'M')
			continue;
		switch(ev.type){
		case 'l':
		case 'L':
			if(matchmesg(&mbox, ev.text))
				mesgopen(ev.text, nil);
			else
				winreturn(c, &ev);
			break;
		case 'x':
		case 'X':
			if((nf = tokenize(ev.text, f, nelem(f))) == 0)
				continue;
			for(p = compfn; p->fn != nil; p++)
				if(strcmp(p->name, f[0]) == 0){
					p->fn(c, &f[1], nf - 1);
					break;
				}
			if(p->fn == nil)
				winreturn(c, &ev);
			break;
		break;
		}
	}
	for(pc = &mbox.opencomp; *pc != nil; pc = &(*pc)->qnext)
		if(*pc == c){
			*pc = c->qnext;
			break;
		}
	mbox.nopen--;
	c->qnext = nil;
	winclose(c);
	free(c->replyto);
	free(c->rname);
	free(c->rdigest);
	free(c->rpath);
	threadexits(nil);
}

static Biobuf*
openbody(Mesg *r)
{
	Biobuf *f;
	int q0, q1;
	char *s;

	assert(r->state & Sopen);

	wingetsel(r, &q0, &q1);
	if(q1 - q0 != 0){
		s = smprint("/mnt/acme/%d/xdata", r->id);
		f = Bopen(s, OREAD);
		free(s);
	}else
		f = mesgopenbody(r);
	return f;
}

int
strpcmp(void *a, void *b)
{
	return strcmp(*(char**)a, *(char**)b);
}

void
show(Biobuf *fd, char *type, char **addrs, int naddrs)
{
	char *sep;
	int i, w;

	w = 0;
	sep = "";
	if(naddrs == 0)
		return;
	qsort(addrs, naddrs, sizeof(char*), strpcmp);
	for(i = 1; i < naddrs; i++){
		if(strcmp(addrs[i-1], addrs[i]) == 0)
			addrs[i-1] = nil;
	}
	Bprint(fd, "%s: ", type);
	for(i = 0; i < naddrs; i++){
		if(addrs[i] == nil)
			continue;
		w += Bprint(fd, "%s%s", sep, addrs[i]);
		sep = ", ";
	}
	Bprint(fd, "\n");
}

void
respondto(Biobuf *fd, char *to, Mesg *r, int all)
{
	char *rpto, **addrs;
	int n;

	rpto = to;
	if(r != nil)
		rpto = (strlen(r->replyto) > 0) ? r->replyto : r->from;
	if(r == nil || !all){
		Bprint(fd, "To: %s\n", rpto);
		return;
	}

	addrs = emalloc(64*sizeof(char*));
	n = 0;
	n += tokenize(to, addrs+n, 64-n);
	n += tokenize(rpto, addrs+n, 64-n);
	n += tokenize(r->to, addrs+n, 64-n);
	show(fd, "To", addrs, n);
	n = 0;
	n += tokenize(r->cc, addrs+n, 64-n);
	show(fd, "CC", addrs, n);
	free(addrs);
}

void
compose(char *to, Mesg *r, int all)
{
	static int ncompose;
	Biobuf *rfd, *wfd;
	Comp *c;
	char *ln;

	c = emalloc(sizeof(Comp));
	if(r != nil)
		c->path = esmprint("%s%s%s.%d", mbox.path, r->name, "Reply", ncompose++);
	else
		c->path = esmprint("%sCompose.%d", mbox.path, ncompose++);
	wininit(c, c->path);

	wintagwrite(c, "Post |fmt ");
	wfd = bwinopen(c, "body", OWRITE);
	respondto(wfd, to, r, all);
	if(r == nil)
		Bprint(wfd, "Subject: ");
	else{
		if(r->messageid != nil)
			c->replyto = estrdup(r->messageid);
		c->rpath = estrjoin(mbox.path, r->name, nil);
		c->rname = estrdup(r->name);
		c->rdigest = estrdup(r->digest);
		Bprint(wfd, "Subject: ");
		if(r->subject != nil && cistrncmp(r->subject, "Re", 2) != 0)
			Bprint(wfd, "Re: ");
		Bprint(wfd, "%s\n\n", r->subject);
		Bprint(wfd, "Quoth %s:\n", r->fromcolon);
		rfd = openbody(r);
		if(rfd != nil){
			while((ln = Brdstr(rfd, '\n', 0)) != nil)
				if(Bprint(wfd, "> %s", ln) == -1)
					break;
			Bterm(rfd);
		}
	}
	Bterm(wfd);
	fprint(c->addr, "$");
	fprint(c->ctl, "dot=addr");
	threadcreate(compmain, c, Stack);
}
