/*
  
Title:	MsgEd
  
File:	Editmail.c
  
Author:	Jim Nutt
	
Copr:	1988 by Jim Nutt
  
Description:
	
	routines to do text editing on messages
  
Support Files:
  
	msged.h
  
*/

#include "msged.h"

#define EDITMAIL
#include "editmail.h"

extern void refresh(LINE * curline, int i);

#define clearline(l,c)	memset(l,0,c)
#define BUFLEN 256

static	int	down1(int y);
static	int	wrap(LINE *cl,char *r,int y);
static	void	reformat(LINE *cl);
static	void	insert_char(char c);
static	void	wrapline(void);

static int done = FALSE;
static int insert = ON;
static int x = 1, y = 1;

static char line[BUFLEN];
static LINE *current = NULL;
static LINE *first = NULL;
static LINE *last = NULL;
static LINE *pastebuf = NULL;
static LINE *pasteend = NULL;
static int changed = 0;
static int direction = 0;
static int noteflag;

LINE *anchorpt = NULL;
extern LINE *top;
extern LINE *bottom;

int editmsg(void);

void	go_bol()

{
	x = 1;
}

static int down1(int y)
{
        if (++y > (maxy - 6)) {
                y = maxy - 6;
                scrollup(1, 7, maxx, maxy, 1);
	}
	gotoxy(1, y + 6);
	return (y);
}

static int wrap(LINE *cl,char *r,int y)

{
        char *l = NULL;
        char *s = NULL;
        LINE *nl = NULL;
        int i = 0;
        int j = 0;
        int x2 = 0;

        if (cl == NULL)
                return(x);

        if (r != NULL)
                j = i = strlen(r);

        if (cl->text != NULL)
                i += strlen(cl->text);

        if (i != 0)
                l = (char *) calloc(1,i+5);

        if (r != NULL) {
                strcpy(l,r);
                free(r);
        }

        if (cl->text != NULL) {
                strcat(l,cl->text);
                free(cl->text);
                cl->text = NULL;
        }

        x2 = strlen(l);

        if (x2 < rm) {
                cl->text = l;
		if ((y + 6) <= maxy) {
                        gotoxy(1,y + 6);
                        clreol();
                        bputs(l);
                }
                return(x);
        }

        s = l + rm;

        while (isspace(*s))
                s--;

        while (!isspace(*s) && (s > l))
                s--;

        if (s <= l) {
                s = l + rm;
                while (!ispunct(*s) && (s > l))
                        s--;
        }

        if (s <= l)
                s = l + rm - 1;
        else
                s++;

        r = strdup(s);
        assert(r);

        *s = '\0';

        if ((x + l) > (s - j))
                x2 = (x + j) - (s - l);
        else
                x2 = x;

        cl->text = (char *) realloc(l,strlen(l)+1);
        if ((y + 6) <= maxy) {
                gotoxy(1,y + 6);
                clreol();
                bputs(cl->text);
                y++;
        }

        if (strchr(r,'\n') != NULL) {
                if ((y+6) <= maxy) {
                        if ((y+6) < maxy)
                                scrolldown(1,y+6,maxx,maxy,1);
                        gotoxy(1,y+6);
                        clreol();
                        bputs(r);
                }
                nl = (LINE *) calloc(1,sizeof(LINE));
                assert(nl);
                nl->next = cl->next;
                if (cl->next != NULL)
                        cl->next->prev = nl;
                else
                        last = nl;
                nl->prev = cl;
                cl->next = nl;
                nl->text = r;
                return(x2);
        }

        if (cl->next == NULL) {
                cl->next = (LINE *) calloc(1,sizeof(LINE));
                assert(cl->next);
                cl->next->prev = cl;
                cl->next->text = NULL;
                last = cl->next;
        }

        assert(cl->next);
	wrap(cl->next,r,y);
        return(x2);
}

void    insert_char(char c)
{
	strins(line, c, x);
	if (strlen(line) > rm) {
		wrapline();
		return;
	}
	gotoxy(x, y + 6);
        clreol();
	assert(line + x - 1);
	bputs((line + x - 1));
	x++;
}

void    delete_character()

{
	LINE *l;
        char *t,*s;

	if (current->text != NULL)
		free(current->text);
	current->text = strdup(line);
	assert(current->text);
	changed = TRUE;

        if ((*(line + x) == '\0') && (current->next != NULL)) {
                t = current->text;
                s = strchr(t,'\n');
                if (s != NULL)
                        *s = '\0';
		current->text = current->next->text;
		l = current->next;
                if (current->next->next != NULL)
                        current->next->next->prev = current;
                current->next = current->next->next;
		free(l);
                if ((y+6) < maxy)
                        scrollup(1,(y+6),maxx,maxy,1);
		wrap(current,t,y);
		clearline(line,BUFLEN);
		if (current->text != NULL)
			strcpy(line,current->text);
                return;
        }

	strdel(line, x);

        clreol();
        assert(line + x - 1);
	bputs((line + x - 1));
}

void    backspace()
{
	changed = TRUE;

	if (x == 1) {
		go_up();
		go_eol();
		delete_character();
		return;
	}
	strdel(line, --x);
	gotoxy(x, y + 6);
	clreol();
	assert(line + x - 1);
	bputs((line + x - 1));
}

void    wrapline()

{
        int x2;

        if (changed) {
                if (current->text != NULL)
                        free(current->text);
                current->text = strdup(line);
                assert(current->text);
                changed = FALSE;
        }

	x2 = wrap(current,NULL,y);
        if (x2 != x) {
                current = current->next;
                x = x2;
                y = down1(y);
                gotoxy(1,y+6);
                clreol();
                bputs(current->text);
	}
	x++;
	clearline(line,BUFLEN);
	if (current->text != NULL)
		strcpy(line,current->text);
}

void    go_eol()
{
	x = strlen(line);

	if (*(line + x - 1) != '\n')
		x++;

	x = max(1, x);
}

void	go_up()
{
	if (current->prev == NULL)
		return;

        direction = UP;

        if (changed) {
                if (current->text != NULL)
                        free(current->text);

                current->text = strdup(line);
                assert(current->text);
                changed = FALSE;
        }

	if (y == 1)
		scrolldown(1, y + 6, maxx, maxy, 1);
	else
		y--;

	current = current->prev;

	assert(current);

	clearline(line, BUFLEN);

	if (current->text != NULL)
		strcpy(line, current->text);

	if (x > strlen(line))
		go_eol();

	gotoxy(1, y + 6);
	bputs(line);
}

void    go_down()
{
	if      (current->next == NULL)
		        return;

        if (changed) {
                if (current->text != NULL)
                        free(current->text);

                current->text = strdup(line);
                assert(current->text);
                changed = FALSE;
        }

        direction = DOWN;

	if (y > (maxy - 7))
		scrollup(1, 7, maxx, maxy, 1);
	else
		y++;

	current = current->next;

	assert(current);

	clearline(line, BUFLEN);

	if (current->text != NULL)
		strcpy(line, current->text);

	if (x > strlen(line))
		go_eol();

	gotoxy(1, y + 6);
	bputs(line);
	x = max(1, x);
}

void	go_left()
{
	if (x == 1) {
		if (current->prev != NULL) {
		        go_up();
			go_eol();
		}
		return;
	}
	x--;
}

void    go_right()
{
	if (*(line + x - 1) == '\0') {
		if (current->next != NULL) {
			go_down();
			x = 1;
		}
		return;
	}

	if (*(line + x - 1) == '\n') {
		if (current->next != NULL) {
			go_down();
			x = 1;
		}
		return;
	}
	x++;
}

void    go_word_right()
{
	if (*(line + x - 1) == '\0') {
		if (current->next != NULL) {
			go_down();
			x = 1;
		}
		return;
	}

	if (*(line + x - 1) == '\n') {
		if (current->next != NULL) {
			go_down();
			x = 1;
		}
		return;
	}

	while (isspace(*(line + x - 1)) && (x <= strlen(line)))
		x++;
	while (!isspace(*(line + x - 1)) && (x <= strlen(line)))
		x++;
	while (isspace(*(line + x - 1)) && (x <= strlen(line)))
		x++;

	if (*(line + x - 2) == '\n')
		x--;
}

void    go_word_left()
{
	if (x == 1) {
		if (current->prev != NULL) {
		        go_up();
			go_eol();
		}
		return;
	}

	while (isspace(*(line + x - 1)) && (x > 1))
		x--;
	while (!isspace(*(line + x - 1)) && (x > 1))
		x--;
	while (isspace(*(line + x - 1)) && (x > 1))
		x--;
	while (!isspace(*(line + x - 1)) && (x > 1))
		x--;

	if (x != 1)
		x++;
}

void    newline()
{
	char    l[BUFLEN];
	char   *t = line + x - 1;

        changed = FALSE;
	if (current->text != NULL)
		free(current->text);

	clearline(l, BUFLEN);
	strncpy(l, line, x - 1);
	strcat(l, "\n");
        current->text = strdup(l);
        assert(current->text);

	clreol();
	x = 1;
	y = down1(y);
	gotoxy(x, y + 6);

	if (current->next == NULL) {
		if ((current->next = (LINE *) calloc(1, sizeof(LINE))) == NULL)
			return;
                current->next->text = strdup(t);
                assert(current->next->text);
		current->next->next = NULL;
		current->next->prev = current;
		current = current->next;
                last = current;
                assert(current->text);
                bputs(current->text);
                msgbuf.first = first;
                msgbuf.last = last;
		clearline(line, BUFLEN);
		if (current->text != NULL)
			strcpy(line, current->text);
		return;
	}

	if (strchr(t, '\n') != NULL) {
		LINE   *nl;
		if ((nl = (LINE *) calloc(1, sizeof(LINE))) == NULL)
                                return;

                nl->text = strdup(t);
                assert(nl->text);
                nl->next = current->next;
                nl->next->prev = nl;
                current->next = nl;
		nl->prev = current;
                current = nl;

                if ((y + 6) < maxy)
                        scrolldown(1, y + 6, maxx, maxy, 1);

                gotoxy(1, y + 6);
                clreol();
                assert(current->text);
                bputs(current->text);
                msgbuf.first = first;
                msgbuf.last = last;
		clearline(line, BUFLEN);
		if (current->text != NULL)
			strcpy(line, current->text);
		return;
	}

	clearline(l, BUFLEN);
        strcpy(l, t);
        assert(current->next);
	current = current->next;
	if (current->text) {
		strcat(l, current->text);
		free(current->text);
		current->text = NULL;
	}

	clearline(line, BUFLEN);
        x = 0;
        changed = TRUE;
	strcpy(line, l);
	wrapline();
}

void    go_pgup()
{
	int	count = y - 1;
	LINE   *l = current;
	LINE   *t = current;

        if (changed) {
                if (current->text != NULL)
                        free(current->text);

                current->text = strdup(line);
		assert(current->text);
		clearline(line,sizeof(line));
                changed = FALSE;
	}

	while (count-- && current->prev) {
		current = current->prev;
	}

	count = maxy - 7;
	while (count-- && current->prev) {
		current = current->prev;
	}

	if (l != current) {
		y = 1;
		l = current;
		count = 0;
		clrwnd(1,7,maxx,maxy);
		while (((count + 7) < maxy) && (l->next)) {
			gotoxy(1,count++ + 7);
			bputs(l->text);
			l = l->next;
		}
		gotoxy(1,count + 7);
		bputs(l->text);
	}
	if (current->text != NULL)
		strcpy(line,current->text);
}

void    go_pgdown()
{
	int	count = maxy - 7;
	LINE   *l = current;

        if (changed) {
                if (current->text != NULL)
                        free(current->text);

                current->text = strdup(line);
                assert(current->text);
		changed = FALSE;
		clearline(line,sizeof(line));
        }

	while (count-- && current->next) {
		current = current->next;
	}

	if (l != current) {
		count = 0;
		l = current;
		clrwnd(1,7,maxx,maxy);
		while (((count + 7) < maxy) && l->next) {
			gotoxy(1,++count + 6);
			bputs(l->text);
			l = l->next;
		}
		gotoxy(1,++count + 6);
		bputs(l->text);
	}
	y = 1;
	if (current->text != NULL)
		strcpy(line,current->text);
}

void    delete_line()
{
	LINE   *t = current;
	LINE   *l = current;
	int	y2;

	changed = FALSE;

	if (current->next == NULL) {
		if (current->text != NULL)
			free(current->text);
		current->text = NULL;
		gotoxy(x=1,y+6);
		clearline(line,sizeof(line));
		clreol();
		return;
	}

        if ((y+6) < maxy)
                scrollup(1, y + 6, maxx, maxy, 1);
        else
                scrollup(1,maxy,maxx,maxy,0);

        t = current;

	clearline(line, BUFLEN);

	assert(current);

        if (current->next == NULL) {

		if (current->text != NULL)
			free(current->text);
		assert(current);
                current->text = NULL;

                if (current->prev != NULL) {
                        current = current->prev;
                        free(current->next);
                        current->next = NULL;
                        y--;
                        if (y < 1) {
                                y = 1;
                                clreol();
                        }

                        gotoxy(1,y + 6);
			clearline(line,sizeof(line));
			if (current->text != NULL)
				strcpy(line,current->text);
                        bputs(line);
			x = min(x,strlen(line));
                }
                else {
			first = current;
			x = y = 1;
                }

                msgbuf.first = first;
                msgbuf.last = last = current;
                current->next = NULL;
		return;
	}

        assert(current->next);

        if (current->prev != NULL) {     /* first line? */
                current->next->prev = current->prev; /* no */
                current->prev->next = current->next;
        }
        else {
                current->next->prev = NULL; /* yes */
                first = current->next;
        }

        current = current->next;

        msgbuf.first = first;
        msgbuf.last = last;

        if (t->text != NULL)
		free(t->text);

        t->text = NULL;

        free(t);
        t = NULL;

	if (current->text != NULL)
		strcpy(line, current->text);
	else
		clearline(line,sizeof(line));

        if (x > strlen(line))
		go_eol();

	l = current; y2 = y;
	while (((y2 + 6) < maxy) && l->next && l->next->text) {
		y2++;
		l = l->next;
	}

	gotoxy(1,y2 + 6);
	bputs(l->text);
	x = max(1,x);
}

void    anchor()
{
        if (anchorpt != NULL) {
                intense(OFF);
                refresh(current,y=1);
		anchorpt = NULL;
		while (pastebuf) {
			free(pastebuf->text);
			if (pastebuf->next) {
				pastebuf = pastebuf->next;
				free(pastebuf->prev);
			}
			else {
				free(pastebuf);
				pastebuf = NULL;
			}
		}
                return;
        }
	anchorpt = current;
	current->block = 1;
}

void    cut()
{
	LINE *f = current;
	LINE *l = current;

	if (changed) {
		if (current->text != NULL)
			free(current->text);
		current->text = strdup(line);
		assert(current->text);
	}

	while (l->next && (l != anchorpt))
		l = l->next;

	if (l != anchorpt) {
		l = current;
		while (f->prev && (f != anchorpt))
			f = f->prev;
		if (f != anchorpt)
			return;
	}

	if (f->prev == NULL)
		first = l->next;
	else
		f->prev->next = l->next;

	if (l->next == NULL)
		last = f->prev;
	else
		l->next->prev = f->prev;

	if (l->next != NULL)
		current = l->next;
	else
		current = f->prev;

	l->next = NULL;
	f->prev = NULL;

	if ((first == NULL) && (last == NULL))
		first = last = current = (LINE *) calloc(1,sizeof(LINE));

	pastebuf = f;

	clearline(line,sizeof(line));
	if (current->text != NULL)
		strcpy(line,current->text);

	refresh(current,1);
	y = x = 1;
}

void    paste()
{
	LINE *l = pastebuf;
	LINE *t,*s = NULL;
	LINE *t1 = current->prev;

	if (changed) {
		if (current->text != NULL)
			free(current->text);
		current->text = strdup(line);
		assert(current->text);
	}

	while (l) {
		t = (LINE *) calloc(1,sizeof(LINE));
		if (s == NULL)
			s = t;
		current->prev = t;
		t->next = current;
		t->prev = t1;
		if (t1 == NULL)
			first = t;
		else
			t1->next = t;
		t->text = strdup(l->text);
		l = l->next;
		t1 = t;
	}

	current = s;
	if (current->text != NULL)
		strcpy(line,current->text);

	refresh(current,1);
	y = x = 1;
}


static void reformat(LINE *cl)

{
	char *t;
	LINE *l = cl->next;
        int y2 = y;

        if (cl->next == NULL)
                return;

        while ((cl->next->next != NULL) & (strchr(cl->text,'\n') == NULL)) {
                t = cl->text;
                cl->text = cl->next->text;
                cl->next->next->prev = cl;
                cl->next = cl->next->next;
		free(l);
		wrap(cl,t,y2);
                if (cl->next == NULL)
                        return;
                cl = cl->next;
                y2++;
        }
}

void	quit()

{
	anchorpt = NULL;
	intense(OFF);
	shownotes = noteflag;
	if (changed) {
		if (current->text) {
			if ((current->text = realloc(current->text,strlen(line)+1)) == NULL) {
				gotoxy(9,1);
				clreol();
				intense(TRUE);
				blink(TRUE);
				bputs("*warning* ");
				blink(FALSE);
				bputs("memory error! message truncated, press a key");
				getkey();
			}
			memset(current->text,0,strlen(line)+1);
			strcpy(current->text, line);
		}
		else {
			current->text = strdup(line);
			assert(current->text);
		}
	}

	if (first == NULL)
		first = last = current;

	msgbuf.first = first;
	if (last->next != NULL)
		msgbuf.last = last->next;
	else
		msgbuf.last = last;

	done =	SAVE;
}


void	abort()

{
	if (!confirm())
		showheader(message);
	else
		done = ABORT;
}

void	imptxt()

{
	msgbuf.last = last;
	msgbuf.first = first;
	if (current->text != NULL)
		free(current->text);
	if (strlen(line) > 0) {
		current->text = strdup(line);
		assert(current->text);
	}
	else
		current->text = NULL;
	import(current);
	first = msgbuf.first;
	last = msgbuf.last;
	refresh(current, 1);
	x = y = 1;
	if (current->text != NULL)
		strcpy(line, current->text);
	else
		clearline(line,sizeof(line));
}

void	outtext()

{
	if (pastebuf != NULL)
		export(pastebuf);
	else if (anchorpt != NULL)
		export(anchorpt);
	else if (current != NULL)
		export(current);
}


void	shellos()

{
	if (changed) {
		if (current->text != NULL)
			free(current->text);
		current->text = strdup(line);
		assert(current->text);
	}
	cls();
	fputs("Type EXIT to return to msged.\n",stderr);
	system("command");
	video_init();
	showheader(message);
	x = y = 1;
	refresh(current, 1);
}

void	format()

{
	if (changed) {
		if (current->text)
			free(current->text);
		current->text = strdup(line);
		assert(current->text);
	}
	reformat(current);
	clearline(line,BUFLEN);
	if (current->text)
		strcpy(line,current->text);
	x = 1;
}

void	toggle_ins()

{
	insert = !insert;
	if (insert) {
		gotoxy((maxx - 4), 6);
		bputs("ins");
	}
	else {
		gotoxy((maxx - 4), 6);
		bputs("   ");
	}
}

void	tabit()

{
	changed = TRUE;
	insert_char(' ');
	while (x % tabsize)
		insert_char(' ');
}

void	go_tos()

{
	int	count = y - 1;

        if (changed) {
                if (current->text != NULL)
                        free(current->text);

                current->text = strdup(line);
                assert(current->text);
                changed = FALSE;
	}

	while (count-- && current->prev) {
		current = current->prev;
	}
	x = y = 1;
	clearline(line,sizeof(line));
	if (current->text != NULL)
		strcpy(line,current->text);
}

void	go_bos()

{
        if (changed) {
                if (current->text != NULL)
                        free(current->text);

                current->text = strdup(line);
                assert(current->text);
                changed = FALSE;
	}

	while (((y + 6) < maxy) && current->next) {
		current = current->next;
		y++;
	}
	x = 1;
	clearline(line,sizeof(line));
	if (current->text != NULL)
		strcpy(line,current->text);
}

void go_tom()

{
        if (changed) {
                if (current->text != NULL)
                        free(current->text);

                current->text = strdup(line);
                assert(current->text);
                changed = FALSE;
	}

	current = first;
	clearline(line,sizeof(line));
	if (current->text != NULL)
		strcpy(line,current->text);
	refresh(current,1);
	x = y = 1;
}

void go_bom()

{
        if (changed) {
                if (current->text != NULL)
                        free(current->text);

                current->text = strdup(line);
                assert(current->text);
                changed = FALSE;
	}

	current = last;
	x = y = 1;

	clearline(line,sizeof(line));
	if (current->text != NULL)
		strcpy(line,current->text);
	if (current->prev) {
		refresh(current->prev,1);
		y = 2;
	}
	else
		refresh(current,1);
}

void killeol()

{
	char *s;

	if (strchr(line,'\n') == NULL)
		*(s = (line + x - 1)) = '\0';
	else {
		*(line + x - 1) = '\n';
		*(s = (line + x)) = '\0';
	}
	memset(s,0,sizeof line - (s - line) - 1);
	changed = TRUE;
	clreol();
}

int     editmsg()
{
	unsigned int	 ch;

	noteflag = shownotes;
        anchorpt = NULL;
	shownotes = TRUE;
	x = y = 1;
        current = first = msgbuf.first;
	last = msgbuf.last;
	clearline(line, BUFLEN);

	if (insert) {
		gotoxy((maxx-4), 6);
		bputs("ins");
	}

	if (first == NULL) {
		if ((first = (LINE *) calloc(1, sizeof(LINE))) == NULL) {
			gotoxy(9,1);
			clreol();
			intense(TRUE);
			blink(TRUE);
			bputs("*warning* ");
			blink(FALSE);
			bputs("ran out of memory!  press a key");
			getkey();
			return(ABORT);
		}

		first->text = NULL;
		first->prev = NULL;
		last = current = first;
	}
	else if (current->text != NULL)
		strcpy(line, current->text);
	else
		clearline(line,sizeof(line));

	refresh(current, 1);
	gotoxy(1, 7);
	done = FALSE;

	while (!done) {
		gotoxy(x,y+6);
		video_update();
		ch = getkey();
		if (ch & 0xff) {
			if (editckeys[(ch & 0xff)] == NULL) {
                                changed = TRUE;
				if (insert)
					insert_char((char) (ch & 0xff));
				else {
					delete_character();
					insert_char((char) (ch & 0xff));
				}
			}
			else
				editckeys[(ch & 0xff)]();
		}
		else if (editakeys[(ch >> 8)] != NULL)
			editakeys[(ch >> 8)]();
		else
			continue;
	}

	shownotes = noteflag;
	return(done);
}
