/*
 * mail.c
 *
 * manage telgrams and announcements in the style of mail
 *
 * written as a tool by Ken Stevens, 1993
 * hacked into empire by ts, 1993
 */

#include <stdio.h>
#include <fcntl.h>
#ifdef aix
#include <sys/types.h>
#endif /* aix */
#include <sys/uio.h>
#include "misc.h"
#include "player.h"
#include "nat.h"
#include "tel.h"
#include "news.h"
#include "deity.h"
#include "file.h"
#include "queue.h"

#define MAX_COMMAND_LENGTH   800

struct mlist {
	struct	qelem queue;
	struct	telstr tgm;
	s_char	*text;
};
  
struct qelem	telegrams;
int		start_message = 1;
int		num_teles = 0;

mail()
{
	FILE	*telfp;
	int     n, num;
	long	cur_time;
	struct	qelem *qp, *next;
	s_char	*mbox;
	s_char	kind[80];
	s_char	buf[1024];

	initque(&telegrams);
	if (player->god && player->argp[1] != 0) {
		if ((n = natarg(player->argp[1], "")) < 0)
			return RET_SYN;
		num = n;
	}else
		num = player->cnum;
	bzero(kind,80);
	if (*player->argp[0] == 'w') {
		sprintf(kind,"announcement");
		mbox = wirebox(buf, num);
	}else{
		sprintf(kind,"telegram");
		mbox = mailbox(buf, num);
	}

	if ((telfp = fopen(mbox, "r+")) == 0) {
		logerror("telegram file %s", mbox);
		return RET_FAIL;
	}

	/* read all teles from the dawn of time */
	(void) time(&cur_time);
	num_teles = read_telegrams(telfp,(long)0);
	fclose(telfp);
	print_headers(start_message);

	if (prompt()) {
		num_teles = count_teles();
		if (num_teles)
			pr("Saving changes to %s...",
				(*player->argp[0] == 'm' ?
				"telegrams" : "announcements"));
		/* read any that came in while we were out */

		if ((telfp = fopen(mbox, "r+")) == 0) {
			logerror("telegram file %s", mbox);
			return RET_FAIL;
		}
		num_teles = read_telegrams(telfp,cur_time);
		if (num_teles)
			pr("%d new message%s %s come in...",num_teles,
				splur(num_teles),
				(num_teles == 1 ? "has" : "have"));
		fclose(telfp);
    		num_teles = write_telegrams(mbox);
		if (num_teles){
			struct natstr *natp;
			pr("%d message%s written\n",num_teles,
				splur(num_teles));
			natp = getnatp(player->cnum);
			natp->nat_tgms = num_teles;
		}else{
			struct natstr *natp;
			natp = getnatp(player->cnum);
			natp->nat_tgms = num_teles;
		}
	}

	/* Free up the list */
	for(qp=telegrams.q_forw;qp!=(&telegrams);qp=next){
		next = qp->q_forw;
		free(qp);
	}

	return RET_OK;
}

read_telegrams(telfile, cur_time)
FILE	*telfile;
long	cur_time;
{
	int	teles = 0, nbytes, slen, mesg=0;
	int	lastdate, lastcnum, lasttype, new;
	struct	telstr tgm, save;
	struct	mlist *mlp;
	s_char	*text;
  
	text = (s_char *)0;
	teles = 0;
	fseek(telfile, 0L, 0);
	lastdate = 0;
	lastcnum = -1;
	lasttype = -1;

	while (fread((s_char *) &tgm, sizeof(tgm), 1, telfile) == 1) {
		if (tgm.tel_date < cur_time){
			/* already have this one.. skip it */
			fseek(telfile,tgm.tel_length,1);
			continue;
		}

		if (tgm.tel_length < 0) {
			logerror("bad telegram file header in %s", telfile);
			return RET_FAIL;
		}
		new = 0;

		if (tgm.tel_type != lasttype || tgm.tel_from != lastcnum)
			new++;

		if (abs((int)(tgm.tel_date - (long)lastdate)) > 60)
			new++;

		if (new && (lasttype != -1) ) {

			mlp = (struct mlist *) malloc(sizeof(struct mlist));
			bzero((s_char *)mlp,sizeof(struct mlist));
			if (mlp == (struct mlist *)0)
				logerror("Malloc failed in mail!\n");

			bcopy((s_char *)&save,(s_char *)&mlp->tgm,
				sizeof(struct telstr));

			if ((text != (s_char *)0) && (*text)){
				mlp->text = (s_char *)malloc(strlen(text)+1);
				bzero(mlp->text,strlen(text)+1);
				bcopy(text,mlp->text,strlen(text));
			}

			inseque(&mlp->queue,&telegrams);
		}

		if (new){
			lastcnum = tgm.tel_from;
			lasttype = tgm.tel_type;
			lastdate = tgm.tel_date;
			bcopy(&tgm,&save,sizeof(struct telstr));
			teles++;
			if (text != (s_char *)0)
				free(text);
			text = (s_char *)0;
		}

		slen=0;

		mesg = 1;
		nbytes = tgm.tel_length;
		if (text == (s_char *)0){
			text = (s_char *)malloc(tgm.tel_length);
			bzero(text,tgm.tel_length);
		}else{
			s_char	*realloc();
			slen = strlen(text);
			text = realloc(text,slen+tgm.tel_length+1);
		}

		(void) fread(text+slen,sizeof(s_char),nbytes,telfile);
		*(text+slen+nbytes) = (s_char)0;
	}

	if (mesg){
		mlp = (struct mlist *) malloc(sizeof(struct mlist));
		bzero((s_char *)mlp,sizeof(struct mlist));
		if (mlp == (struct mlist *)0)
			logerror("Malloc failed in mail!\n");

		bcopy((s_char *)&save,(s_char *)&mlp->tgm,
			sizeof(struct telstr));

		if ((text != (s_char *)0) && (*text)){
			mlp->text = (s_char *)malloc(strlen(text)+1);
			bzero(mlp->text,strlen(text)+1);
			bcopy(text,mlp->text,strlen(text));
		}

		inseque(&mlp->queue,&telegrams);
	}

	return teles;
}

print_headers(cm)
int	cm;
{
	extern	s_char *telnames[];
	int	m=0, money, st, nn;
	s_char	type_mark[5], subj[80], date[20], name[13];
	struct	mlist *mlp;
	struct	qelem *qp, *find_message();
	struct	natstr *natp;

	st = cm;
	nn = count_teles();
	if (nn < 22)
		st = 1;
	if ((nn-st) < 22)
		st = (nn-21);

	if (st < 1)
		st = 1;
		
	qp = find_message(st);
	while((qp != (struct qelem *)0) && (qp != (&telegrams)) && (m <= 20)){
		mlp = (struct mlist *)qp;

		bzero(date,20);
		bcopy(ctime(&mlp->tgm.tel_date),date,4);
		bcopy(ctime(&mlp->tgm.tel_date)+8,date+4,8);
		bzero(name,13);
		natp = getnatp(mlp->tgm.tel_from);
		bcopy(natp->nat_cnam,name,12);
		bzero(type_mark,5);
		bzero(subj,80);

		if (mlp->tgm.tel_type == TEL_UPDATE){
			int	x;
			sprintf(type_mark,"UPDA");
			
			x=strlen(mlp->text)-2;
			while(x && (*(mlp->text+x) != '\n'))
				x--;
			if (x) x++;
			if (sscanf(mlp->text+x,
				"money delta was $%d for this update",
				&money) == 1)
          			sprintf(subj, "money delta:  $%d", money);
			sprintf(name,"            ");
		}

		if (mlp->tgm.tel_type == TEL_BULLETIN){
			s_char *s;
			sprintf(type_mark,"*");
			s = mlp->text;
			while(s && ((*s == ' ') || (*s == '\n') || (*s== '\t')))
				s++;

			if (s)
				scopy(s,subj,42);
		}

		if ((mlp->tgm.tel_type == TEL_NORM) ||
			(mlp->tgm.tel_type == TEL_ANNOUNCE)){
			s_char *s;

			sprintf(type_mark,"#%-3d",mlp->tgm.tel_from);
			s = mlp->text;
			while(s && ((*s == ' ') || (*s == '\n') || (*s== '\t')))
				s++;
			if (s)
				scopy(s,subj,42);
		}

		pr("%3d %c %4s %12s %12s %s\n", st+m, (st+m) == cm ?'>':' ',type_mark,name,date,subj);

		m++;
		qp = qp->q_forw;
	}
}

write_telegrams(telfile)
s_char	*telfile;
{
	struct	iovec iov[2];
	struct	qelem *qp;
	struct	mlist *mlp;
	int	m=0;
	int	fd;

	if ((fd = open(telfile, O_WRONLY|O_TRUNC, 0)) < 0) {
		logerror("mail 'open' of %s failed", telfile);
		return RET_FAIL;
	}
	qp = telegrams.q_forw;
	while(qp != (&telegrams)){
		mlp = (struct mlist *)qp;
		mlp->tgm.tel_length = (mlp->text == (s_char *)0 ?
			0 : strlen(mlp->text));
		iov[0].iov_base = (caddr_t) &mlp->tgm;
		iov[0].iov_len = sizeof(struct telstr);
		iov[1].iov_base = mlp->text;
		iov[1].iov_len = (mlp->text == (s_char *)0 ?
			0 : strlen(mlp->text));
		if (writev(fd, iov, 2) < iov[0].iov_len + iov[1].iov_len)
			logerror("mail 'write' to %s failed", telfile);
		qp = qp->q_forw;
		m++;
	}
	(void) close(fd);
	return m;
}

prompt()
{
	register int i;
	s_char	prompt[80];
	s_char	*s, lastcomm=' ';
	int	newm;
	s_char	buf[1024];
	int	m = start_message, n=count_teles();

	if (m > n)
		m = n;
	while (1){
		sprintf(prompt,"(%d/%d): ", m, n);
		pr("\n");
		if ((s = getstring(prompt, buf)) == 0)
			return 0;
		if (*s != '\0'){
			/* strip out all the spaces & newlines */
			for(i=strlen(s)-1;i&&s[i]==' '||s[i]=='\n';
				s[i--]=(s_char)0);
			if (*s == 'q')
				return 1;
			else if (*s == 'x')
				return 0;
			else if (*s == '?')
				mail_help();
			else if (*s == 'h'){
				if (lastcomm == 'h'){
					m += 10;
					if (m > n)
						m = n;
				}
				print_headers(m);
    			}else if (*s == 'd'){
				m = process_delete(s + 1, n, m);
				n = count_teles();
				if (m > n)
					m = n;
    			}else if (sscanf(s, "%d", &newm) == 1){
				if (newm < 1 || newm > n)
					pr("No such message: %d\n", newm);
      				else
					print_message(m = newm);
			}
			lastcomm = *s;
		}else{
			if (m == n)
				pr("No more messages.\n");
      			else {
				++m;
				print_message(m);
			}
		}
	}
}

print_message(m)
int	m;
{
	register int i, lines;
	struct	qelem *qp, *find_message();
	struct	mlist *mlp;

	qp = find_message(m);
	if (qp == (struct qelem *)0){
		pr("Message is empty\n");
		return RET_OK;
	}

	mlp = (struct mlist *)qp;

	if (mlp->text == (s_char *)0){
		pr("Message is empty\n");
		return RET_OK;
	}

	lines = 1;
	for(i=0;i<strlen(mlp->text);i++)
		if (*(mlp->text+i) == '\n')
			lines++;

	pr("Message #%d (%d lines) from %s (#%d), dated %s", m, lines,
		cname(mlp->tgm.tel_from), mlp->tgm.tel_from,
		ctime(&mlp->tgm.tel_date));
	prnf(mlp->text);

	return RET_OK;
}

struct del{
	struct	qelem queue;
	struct	qelem *delete_me;
};

process_delete(s,n,cm)
s_char *s;
int n,cm;
{
	struct	qelem delete_list;
	struct	qelem *qp, *next, *find_message();
	struct	del *d;
	s_char	*sp = s;
	int	m, p, q;

	initque(&delete_list);

	if (!sp || !*sp){
		d = (struct del *)
			malloc(sizeof(struct del));
		bzero(d, sizeof(struct del));
		if ((qp = find_message(cm)) != (struct qelem *)0){
			d->delete_me = qp;
			insque(&d->queue,&delete_list);
		}
	}

	/* build list of those to delete */
	while (*s){
		while (*sp == ' ') ++sp;
		s = sp;

		if (sscanf(s, "%d-%d", &p, &q) == 2){
			if (p > q || p < 1 || q > n)
				pr("Bad range: %d-%d\n", p, q);
			else{
				for (m = p; m <= q; ++m){
					d = (struct del *)
						malloc(sizeof(struct del));
					bzero(d, sizeof(struct del));
					if ((qp = find_message(m)) !=
						(struct qelem *)0){
						d->delete_me = qp;
						insque(&d->queue,&delete_list);
					}
				}
			}
		}else if (sscanf(s, "%d", &m) == 1){
			if (m < 1 || m > n)
				pr("Bad message number: %d\n", m);
			else{
				d = (struct del *)
					malloc(sizeof(struct del));
				bzero(d, sizeof(struct del));
				if ((qp = find_message(m)) !=
					(struct qelem *)0){
					d->delete_me = qp;
					insque(&d->queue,&delete_list);
				}
			}
		}else if (!strcmp(s, "b")) {
			for (m = 1; m <= n; ++m){
				struct	mlist *mlp;
				if ((qp = find_message(m)) !=
					(struct qelem *)0){
					mlp = (struct mlist *)qp;
					if (mlp->tgm.tel_type !=
						TEL_BULLETIN)
						continue;
				}else
					continue;

				d = (struct del *)
					malloc(sizeof(struct del));
				bzero(d, sizeof(struct del));
				d->delete_me = qp;
				insque(&d->queue,&delete_list);
			}
		}else if (!strcmp(s, "p")) {
			for (m = 1; m <= n; ++m){
				struct	mlist *mlp;
				if ((qp = find_message(m)) !=
					(struct qelem *)0){
					mlp = (struct mlist *)qp;
					if (mlp->tgm.tel_type !=
						TEL_UPDATE)
						continue;
				}else
					continue;

				d = (struct del *)
					malloc(sizeof(struct del));
				bzero(d, sizeof(struct del));
				d->delete_me = qp;
				insque(&d->queue,&delete_list);
			}
		}else if (!strcmp(s, "*")){
			for (m = 1; m <= n; ++m){
				if ((qp = find_message(m)) ==
					(struct qelem *)0)
					continue;

				d = (struct del *)
					malloc(sizeof(struct del));
				bzero(d, sizeof(struct del));
				d->delete_me = qp;
				insque(&d->queue,&delete_list);
			}
		}
		while (*sp && *sp != ' ') ++sp;
		s = sp;
	}

	/* delete the marked message */
	for(qp=delete_list.q_forw;qp!=(&delete_list);qp=next){
		d = (struct del *)qp;
		next = qp->q_forw;
		remque(d->delete_me);
		free(d->delete_me);
	}

	/* Free up the delete list */
	for(qp=delete_list.q_forw;qp!=(&delete_list);qp=next){
		next = qp->q_forw;
		free(qp);
	}

	return m;
}

count_teles()
{
	register int i=0;
	struct	qelem *qp;

	for(qp=telegrams.q_forw;qp!=(&telegrams);qp=qp->q_forw)
		i++;
	
	return i;
}

struct qelem *
find_message(m)
int	m;
{
	register int i=1;
	struct	qelem *qp;

	for(qp=telegrams.q_forw;qp!=(&telegrams);qp=qp->q_forw)
		if ((i++) == m)
			return qp;
	
	return (struct qelem *)0;
}

mail_help()
{
	pr("Commands are:\n");
	pr("h                     print the list of headers\n");
	pr("<n>                   print message number <n>\n");
	pr("d <message list>      delete message list\n");
	pr("q                     quit and save changes\n");
	pr("x                     quit without saving changes\n\n");
	pr("For delete, if no <message list> is specified, then the\n");
	pr("current message is used.");
	pr("Other choices for <message list> are:\n\n");
	pr("  <n>                 message number <n>\n");
	pr("  <n>-<m>             messages <n> through <m>\n");
	pr("  <n1> <n2> ... <nm>  messages <n1>, <n2>, ..., and <nm>\n");
	pr("  p                   all Production Reports\n");
	pr("  b                   all BULLETINS\n");
	pr("  *                   all messages\n");
}

/* Insert something at the END of a queue */
inseque(elem, queue)
struct  qelem *elem;
struct  qelem *queue;
{
        struct  qelem *next;

	next = queue->q_back;
	queue->q_back = elem;
	elem->q_forw = queue;
	elem->q_back = next;
	next->q_forw = elem;
}

scopy(s1,s2,len)
s_char *s1,*s2;
int len;
{
    while (len)
	if (*s1 == '\n')
		s1++;
	else{
		*(s2++) = *(s1++);
		len--;
	}
}
