/* --------------------
	vmail -- move.c

	Routines to delete or move a mail item from current folder.

	Copyright (C) J. Zobel, University of Melbourne, October 1987.
-------------------- */

#include "defs.h"
#include <errno.h>

static char prevfile[LEN] = "";

extern int errno;
extern char *strerror();

/* --------------------
	Move current item to folder prevfile (get_name = false) or as read from
	terminal (get_name = true).
-------------------- */
void
move_item(get_name)
	bool	get_name;
{
	char	*s, str[LEN], str2[LEN];
	folder	f, new_folder(), create_folder();
	item	m;
	bool	redraw,						/* true if screen to be refreshed */
		change_item();
	int	fdi, fdo, i = FIRST-1, num, N;

		/* get name if required */
	if(get_name || *prevfile == '\0') {
		get_string("folder? ", str);
		if(*str == '\0') {
			addstatus("no name given for folder", true);
			return;
		}
		strcpy(prevfile, str);
	} else
		strcpy(str, prevfile);
		/* find first page of named folder */
	sprintf(str2, "refiling to %s ...", str);
	addstatus(str2, false);
	GOTO_NAME(f, str);
	if(f == (folder) NULL) {	/* create folder */
		f = create_folder(str);
		if(f == (folder) NULL)
			return;
	} else {
		if(f->name == curflr->name) {
			addstatus("can't move item to current folder", true);
			return;
		}
		if(f->valid) {		/* goto last mail item in folder */
			LAST_OF_NAME(f);
			for(i=FIRST, m=f->mail ; m->next != (item) NULL ; i++, m=m->next)
				;
		}
	}
		/* remember current location of mail */
	m = curmail;
	num = curmail->number;
	s = curflr->name;
		/* delete item from current folder, update current folder & screen */
	redraw = change_item(true);
	if(f->valid)		/* update structures of mail items */
		if(i > lines) {
				/* create new folder record */
			f = new_folder(f);
			f->mail = f->last = m;
			m->prev = m->next = (item) NULL;
			N = f->prev->last->number;
		} else {
				/* insert at end of list of mail items */
			m->prev = f->last; m->next = (item) NULL;
			f->last->next = m;
			f->last = m;
			N = m->prev->number;
		}
	else
		N = next_vacant(f);
/* to avoid race between "send" or other process in background and vmail
   foreground, compute next free slot, dont just use given value 
*/
	sprintf(str2, "%s/%s/%d", mail_dir, f->name, N);
		/* loop until unused file name found */
	for(errno=0 ; (fdo = open(str2, O_WRONLY|O_CREAT|O_EXCL, 0644)) < 0
				&& errno == EEXIST ; errno=0) {
		N = N + 1;
		sprintf(str2, "%s/%s/%d", mail_dir, f->name, N);
	}
	if(f->valid)
		m->number = N;
	sprintf(str, "%s/%s/%d", mail_dir, s, num);
	fdi = open(str, O_RDONLY);
	while((i = read(fdi, str2, LEN)) > 0)	/* copy original to new */
		write(fdo, str2, i);
	close(fdi);
	if (close(fdo) < 0) {
		char errbuf[BUFSIZ];
		sprintf (errbuf, "error copying file %s to %s/%s/%d: close(): %s\n", str, mail_dir, f->name, N, strerror(errno)); 
		addstatus(errbuf, true);
		sleep (1);
		(void) find_mail (curflr, false);
		(void) find_mail (f, false);
		display_page();
	}
	else
	{
		unlink(str);							/* remove original */
		if(redraw)
			display_page();
		else {
			add_page_header(str);
			move(y, 0);
			refresh();
		}
		addstatus("refiled", true);
		if(curflr == (folder) NULL) {
			to_normal();
			exit(1);
		}
	}
}


/* --------------------
	Delete count items.
-------------------- */
void
delete_item(count)
	int		count;
{
	bool	redraw = false, change_item();
	item	m;
	char	str[LEN];

	for( ; count > 0 ; count--) {
		m = curmail->next;
		redraw = change_item(false);
		if(redraw || m != curmail) /* on new page, or last item deleted */
			break;
	}
	if(redraw)
		display_page();
	else {
		add_page_header(str);
		move(y, 0);
		refresh();
	}
}


/* --------------------
	Structure for deleted item.
-------------------- */
struct {
	folder flr;
	int number;
} deleted = {(folder) NULL, 0};


/* --------------------
	Either delete (do_move = false) or prepare to move (do_move = true) item.
	Delete item from current folder, update screen, find new current folder
	if current folder has become empty.
-------------------- */
bool
change_item(do_move)
	bool	do_move;
{
	item	tmp, m = curmail;
	folder	F, p, f = curflr, pval, nval;
	char	s1[LEN], s2[LEN];
	bool	redraw, doexit = false;

	if(curmail->next == (item) NULL && curmail->prev == (item) NULL) {
			/* have last item in page */
		redraw = true;
		pval = curflr->prev; PREV_VALID(pval);
		nval = curflr->next; NEXT_VALID(nval);
		if(pval == (folder) NULL && nval == (folder) NULL) {
						/* no more active pages */
			if(! do_move)
				addstatus("Deleting last active mail item -- bye", true);
			doexit = true;
		} else {
				/* update pages, pagenum */
			for(p=curflr->prev ; p != (folder) NULL && p->name == curflr->name
																	; p=p->prev)
				p->pages -= 1;
			for(p=curflr->next ; p != (folder) NULL && p->name == curflr->name
																	; p=p->next)
				p->pages -= 1, p->pagenum -= 1;
				/* find next active page, drop current page from list */
			if(curflr->prev != (folder) NULL)
				curflr->prev->next = curflr->next;
			else
				folders = curflr->next;
			if(curflr->next != (folder) NULL)
				curflr->next->prev = curflr->prev;
			curflr = (nval == (folder) NULL) ? pval : nval;
			curmail = curflr->mail;
		}
	} else {
		redraw = false;
		deleteline();
			/* move first item from next to current */
		if(curflr->next != (folder) NULL && curflr->name == curflr->next->name)
			show_title(s1, FIRST+lines-1, curflr->next->mail);
		for(p=curflr->next ; p != (folder) NULL && p->name == curflr->name ;
																	p=p->next) {
			tmp = p->mail;
			p->mail = tmp->next;
			if(p->mail == (item) NULL) { /* remove folder from list */
				p->prev->next = p->next;
				if(p->next != (folder) NULL)
					p->next->prev = p->prev;
					/* update page counts */
				for(F=p->prev ; F != (folder) NULL && F->name == p->name
																	; F=F->prev)
					F->pages -= 1;
			} else
				p->mail->prev = (item) NULL;
			p->prev->last->next = tmp;
			tmp->prev = p->prev->last;
			tmp->next = (item) NULL;
			p->prev->last = tmp;
		}
			/* delete item from linked list of items */
		if(m->prev == (item) NULL)
			curflr->mail = m->next;
		else
			m->prev->next = m->next;
		if(m->next == (item) NULL) {
			curflr->last = m->prev;
			curmail = m->prev;
			y--;
		} else {
			m->next->prev = m->prev;
			curmail = m->next;
		}
		move(y, 0);
	}
	if(! do_move) {
		deleted.flr = f;
		deleted.number = m->number;
		sprintf(s1, "%s/%s/%d", mail_dir, f->name, m->number);
		sprintf(s2, "%s/%s/.#%d", mail_dir, f->name, m->number);
		rename(s1, s2);
		if(doexit) {
			to_normal();
			exit(0);
		}
	}
	return(redraw);
}


/* --------------------
	Create folder of given name if user agrees, creating directory and
	entry in linked list of folders.
-------------------- */
folder
create_folder(str)
	char	*str;
{
	struct stat statbuf;
	char	str2[LEN], c;
	folder	fnew, p, f;

	squash(str);
	sprintf(str2, "%s does not exist (or is empty) - create? ", str);
	mvaddstr(STATUS, 0, str2); refresh();
	c = getchar();
	move(STATUS, 0); clrtoeol(); move(y, 0); refresh();
	if(c != 'y')
		return((folder) NULL);
	for(p=(folder) NULL, f=folders ; f != (folder) NULL &&
							strcmp(str, f->name) > 0 ; p=f, f=f->next)
		;
		/* create physical folder */
	sprintf(str2, "%s/%s", mail_dir, str);
	if(stat(str2, &statbuf)) {		/* doesn't exist */
		if(mkdir(str2, folder_protect)) {
			addstatus("Cannot make folder", true);
			return((folder) NULL);
		}
	} else
		if(!(statbuf.st_mode & S_IREAD) || !(statbuf.st_mode & S_IWRITE)
									|| !(statbuf.st_mode & S_IEXEC)) {
				addstatus("Cannot write in folder", true);
				return((folder) NULL);
		}
			/* make a new folder record, insert it */
	fnew = NEW(mail_folder);
	fnew->name = NEWSTR(strlen(str)+1);
	strcpy(fnew->name, str);
	fnew->mail = fnew->last = (item) NULL;
	fnew->next = fnew->prev = (folder) NULL;
	fnew->pages = fnew->pagenum = 1;
	fnew->valid = false;
	if(p == (folder) NULL) {
		fnew->next = folders;
		folders->prev = fnew;
		folders = fnew;
	} else {
		p->next = fnew;
		if(f != (folder) NULL)
			f->prev = fnew;
		fnew->prev = p;
		fnew->next = f;
	}
	return(fnew);
}


/* --------------------
	Crude undo.  Sophisticated undo rather too painful to code.
-------------------- */
void
undo()
{
	folder	f = deleted.flr, p;
	char	s1[LEN], s2[LEN];

	if(f == (folder) NULL) {
		addstatus("nothing to undo", true);
		return;
	} else {
		addstatus("undoing ...", true);
		deleted.flr = (folder) NULL;
	}
	sprintf(s1, "%s/%s/.#%d", mail_dir, f->name, deleted.number);
	sprintf(s2, "%s/%s/%d", mail_dir, f->name, deleted.number);
	rename(s1, s2);
		/* find first page of folder */
	p = f; FRST_OF_NAME(p);
		/* find last page of folder */
	LAST_OF_NAME(f);
	curflr = p;
	p->next = f->next;
/* should free old folder/mail records */
/*	p->valid = false; */
	p->mail = p->last = (item) NULL;
	p->pagenum = p->pages = 1;
	if(p->next != (folder) NULL)
		p->next->prev = p;
	find_mail(curflr, false);
	curmail = curflr->mail;
	y = FIRST;
	display_page();
}


/* --------------------
	Pack current folder.  Unsets "deleted" if last removed record was
	on current folder.
-------------------- */
void
pack_folder()
{
	folder	f = curflr;
	item	m;
	char	path[LEN], s1[LEN], s2[LEN];
	bool	found;
	int		newnum = 1;

	addstatus("Packing folder ...", false);
	FRST_OF_NAME(f);
		/* unset undo if packing that folder */
	if(deleted.flr != (folder) NULL && deleted.flr->name == curflr->name)
		deleted.flr = (folder) NULL;
	sprintf(path, "%s/%s/", mail_dir, curflr->name);
	for( ; f != (folder) NULL && f->name == curflr->name ; f=f->next)
		for(m=f->mail ; m != (item) NULL ; m=m->next) {
			for(found=false ; !found && newnum < m->number ; ) {
				sprintf(s1, "%s%d", path, newnum);
				if(! access(s1, R_OK))
					newnum++;
				else
					found = true;
			}
			if(found) {
				sprintf(s2, "%s%d", path, m->number);
				rename(s2, s1);
				m->number = newnum;
			}
		}
	display_page();
}
