#include "a.h"

enum
{
	BoxSubChunk = 16,
	BoxChunk = 64,
	MsgChunk = 256,
	PartChunk = 4,
	PartSubChunk = 4
};

Box **boxes;
uint nboxes;
Box *rootbox;
int boxid;

Box*
boxbyname(char *name)
{
	int i;

	/* LATER: replace with hash table */
	for(i=0; i<nboxes; i++)
		if(boxes[i] && strcmp(boxes[i]->name, name) == 0)
			return boxes[i];
	return nil;
}

Box*
subbox(Box *b, char *elem)
{
	int i;
	
	for(i=0; i<b->nsub; i++)
		if(b->sub[i] && strcmp(b->sub[i]->elem, elem) == 0)
			return b->sub[i];
	return nil;
}

Box*
boxbyid(uint id)
{
	int i;
	
	/* LATER: replace with binary search */
	for(i=0; i<nboxes; i++)
		if(boxes[i] && boxes[i]->id == id)
			return boxes[i];
	return nil;
}

Box*
boxcreate(char *name)
{
	char *p;
	Box *b, *bb;
	
	if((b = boxbyname(name)) != nil)
		return b;
	
	b = emalloc(sizeof *b);
	b->id = ++boxid;
	b->time = time(0);
	b->name = estrdup(name);
	b->uidnext = 1;
	p = strrchr(b->name, '/');
	if(p){
		*p = 0;
		bb = boxcreate(b->name);
		*p = '/';
		b->elem = p+1;
	}else{
		bb = rootbox;
		b->elem = b->name;
	}
	if(nboxes%BoxChunk == 0)
		boxes = erealloc(boxes, (nboxes+BoxChunk)*sizeof boxes[0]);
	boxes[nboxes++] = b;
	if(bb->nsub%BoxSubChunk == 0)
		bb->sub = erealloc(bb->sub, (bb->nsub+BoxSubChunk)*sizeof bb->sub[0]);
	bb->sub[bb->nsub++] = b;
	b->parent = bb;
	return b;
}

void
boxfree(Box *b)
{
	int i;
	
	if(b == nil)
		return;
	for(i=0; i<b->nmsg; i++)
		msgfree(b->msg[i]);
	free(b->msg);
	free(b);
}

Part*
partcreate(Msg *m, Part *pp)
{
	Part *p;
	
	if(m->npart%PartChunk == 0)
		m->part = erealloc(m->part, (m->npart+PartChunk)*sizeof m->part[0]);
	p = emalloc(sizeof *p);
	p->msg = m;
	p->ix = m->npart;	
	m->part[m->npart++] = p;
	if(pp){
		if(pp->nsub%PartSubChunk == 0)
			pp->sub = erealloc(pp->sub, (pp->nsub+PartSubChunk)*sizeof pp->sub[0]);
		p->pix = pp->nsub;
		p->parent = pp;
		pp->sub[pp->nsub++] = p;
	}
	return p;
}

void
partfree(Part *p)
{
	int i;
	
	if(p == nil)
		return;
	for(i=0; i<p->nsub; i++)
		partfree(p->sub[i]);
	free(p->sub);
	hdrfree(p->hdr);
	free(p->type);
	free(p->idstr);
	free(p->desc);
	free(p->encoding);
	free(p->charset);
	free(p->raw);
	free(p->rawheader);
	free(p->rawbody);
	free(p->mimeheader);
	free(p->body);
	free(p);
}

void
msgfree(Msg *m)
{
	int i;
	
	if(m == nil)
		return;
	for(i=0; i<m->npart; i++)
		free(m->part[i]);
	free(m->part);
	free(m);
}

void
msgplumb(Msg *m, int delete)
{
	static int fd = -1;
	Plumbmsg p;
	Plumbattr a[10];
	char buf[256], date[40];
	int ai;
	
	if(m == nil || m->npart < 1 || m->part[0]->hdr == nil)
		return;
	if(m->box && strcmp(m->box->name, "mbox") != 0)
		return;

	p.src = "mailfs";
	p.dst = "seemail";
	p.wdir = "/";
	p.type = "text";

	ai = 0;
	a[ai].name = "filetype";
	a[ai].value = "mail";
	
	a[++ai].name = "mailtype";
	a[ai].value = delete?"delete":"new";
	a[ai-1].next = &a[ai];

	if(m->part[0]->hdr->from){
		a[++ai].name = "sender";
		a[ai].value = m->part[0]->hdr->from;
		a[ai-1].next = &a[ai];
	}

	if(m->part[0]->hdr->subject){
		a[++ai].name = "subject";
		a[ai].value = m->part[0]->hdr->subject;
		a[ai-1].next = &a[ai];
	}

	if(m->part[0]->hdr->digest){
		a[++ai].name = "digest";
		a[ai].value = m->part[0]->hdr->digest;
		a[ai-1].next = &a[ai];
	}
	
	strcpy(date, ctime(m->date));
	date[strlen(date)-1] = 0;	/* newline */
	a[++ai].name = "date";
	a[ai].value = date;
	a[ai-1].next = &a[ai];
	
	a[ai].next = nil;
	
	p.attr = a;
#ifdef PLAN9PORT
	snprint(buf, sizeof buf, "Mail/%s/%ud", m->box->name, m->id);
#else
	snprint(buf, sizeof buf, "/mail/fs/%s/%ud", m->box->name, m->id);
#endif
	p.ndata = strlen(buf);
	p.data = buf;
	
	if(fd < 0)
		fd = plumbopen("send", OWRITE);
	if(fd < 0)
		return;

	plumbsend(fd, &p);
}


Msg*
msgcreate(Box *box)
{
	Msg *m;
	
	m = emalloc(sizeof *m);
	m->box = box;
	partcreate(m, nil);
	m->part[0]->type = estrdup("message/rfc822");
	if(box->nmsg%MsgChunk == 0)
		box->msg = erealloc(box->msg, (box->nmsg+MsgChunk)*sizeof box->msg[0]);
	m->ix = box->nmsg++;
	box->msg[m->ix] = m;
	m->id = ++box->msgid;
	return m;
}

Msg*
msgbyimapuid(Box *box, uint uid, int docreate)
{
	int i;
	Msg *msg;

	if(box == nil)
		return nil;
	/* LATER: binary search or something */
	for(i=0; i<box->nmsg; i++)
		if(box->msg[i]->imapuid == uid)
			return box->msg[i];
	if(!docreate)
		return nil;
	msg = msgcreate(box);
	msg->imapuid = uid;
	return msg;
}

Msg*
msgbyid(Box *box, uint id)
{
	int i;

	if(box == nil)
		return nil;
	/* LATER: binary search or something */
	for(i=0; i<box->nmsg; i++)
		if(box->msg[i]->id == id)
			return box->msg[i];
	return nil;
}

Part*
partbyid(Msg *m, uint id)
{
	if(m == nil)
		return nil;
	if(id >= m->npart)
		return nil;
	return m->part[id];
}

Part*
subpart(Part *p, uint a)
{
	if(p == nil || a >= p->nsub)
		return nil;
	return p->sub[a];
}

void
hdrfree(Hdr *h)
{
	if(h == nil)
		return;
	free(h->date);
	free(h->subject);
	free(h->from);
	free(h->sender);
	free(h->replyto);
	free(h->to);
	free(h->cc);
	free(h->bcc);
	free(h->inreplyto);
	free(h->messageid);
	free(h->digest);
	free(h);
}

void
boxinit(void)
{
	rootbox = emalloc(sizeof *rootbox);
	rootbox->name = estrdup("");
	rootbox->time = time(0);
}