/*
 * Program:	Main Pine Composer routines
 *
 * Modifier:	Michael Seibel
 *		Networks and Distributed Computing
 *		Computing & Communications
 *		University of Washington
 *		Administration Building, AG-44
 *		Seattle, WA  98195
 *		Internet: mikes@cac.washington.edu
 *
 * Date:	Nov 1989
 * Last Edited:	9 September 1991
 *
 * Copyright 1991 by the University of Washington
 *
 *  Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted, provided
 * that the above copyright notice appears in all copies and that both the
 * above copyright notice and this permission notice appear in supporting
 * documentation, and that the name of the University of Washington not be
 * used in advertising or publicity pertaining to distribution of the software
 * without specific, written prior permission.  This software is made
 * available "as is", and
 * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
 * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
 * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
 * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 *
 * WEEMACS/PICO NOTES:
 *
 * 01 Nov 89 - MicroEmacs 3.6 vastly pared down and becomes a function call 
 * 	       weemacs() and plugged into the Pine mailer.  Lots of unused
 *	       MicroEmacs code laying around.
 *
 * 17 Jan 90 - weemacs() became weemacs() the composer.  Header editing
 *	       functions added.
 *
 * 09 Sep 91 - weemacs() renamed pico() for the PIne COmposer.
 *
 */


/*
 * This program is in public domain; written by Dave G. Conroy.
 * This file contains the main driving routine, and some keyboard processing
 * code, for the MicroEMACS screen editor.
 *
 * REVISION HISTORY:
 *
 * 1.0  Steve Wilhite, 30-Nov-85
 *      - Removed the old LK201 and VT100 logic. Added code to support the
 *        DEC Rainbow keyboard (which is a LK201 layout) using the the Level
 *        1 Console In ROM INT. See "rainbow.h" for the function key defs
 *      Steve Wilhite, 1-Dec-85
 *      - massive cleanup on code in display.c and search.c
 *
 * 2.0  George Jones, 12-Dec-85
 *      - Ported to Amiga.
 *
 * 3.0  Daniel Lawrence, 29-Dec-85
 *	16-apr-86
 *	- updated documentation and froze development for 3.6 net release
 */

#include        <stdio.h>
#include	<setjmp.h>

/* make global definitions not external */
#define	maindef

#include	"osdep.h"	/* operating system dependent includes */
#include        "estruct.h"	/* global structures and defines */
#include	"efunc.h"	/* function declarations and name table	*/
#include	"pico.h"	/* PIne COmposer definitions */
#include	"edef.h"	/* global definitions */
#include	"ebind.h"	/* default key bindings */


/*
 * function key mappings
 */
static int pfkm[12][2] = {
    { F1,  (CTRL|'G')},
    { F2,  (CTRL|'X')},
    { F3,  (CTRL|'C')},
    { F4,  (CTRL|'J')},
    { F5,  (CTRL|'R')},
    { F6,  (CTRL|'W')},
    { F7,  (CTRL|'Y')},
    { F8,  (CTRL|'V')},
    { F9,  (CTRL|'K')},
    { F10, (CTRL|'U')},
    { F11, (CTRL|'O')},
#ifdef	SPELLER
    { F12, (CTRL|'T')}
#else
    { F12, (CTRL|'D')}
#endif
};


/*
 * common place for the various functions in pico() to jump to when ready
 * for pico() to return...
 */
jmp_buf	finstate;		/* stack environment to return codes */


/*
 * pico - the main routine for Pine's composer.
 *
 */
pico(pm)
PICO *pm;
{
    register int    c;
    register int    f;
    register int    n;
    register BUFFER *bp;
    int      i;
    int      viewflag = FALSE;		/* are we starting in view mode? */
    char     bname[NBUFN];		/* buffer name of file to read */
    extern   struct on_display ods;

    Pmaster = pm;
    gmode |= pm->pine_flags;/* high 4 bits rsv'd by pine composer */

    vtinit();				/* Init Displays.      */

    strcpy(bname, "main");		/* default buffer name */
    edinit(bname);			/* Buffers, windows.   */

    InitMailHeader(pm);			/* init mail header structure */

    /* setup to process commands */
    lastflag = 0;			/* Fake last flags.     */
    curbp->b_mode |= gmode;		/* and set default modes*/

    if(pm->messagebuf[0] != '\0'){	/* any text to start with? */
	readbuf(&pm->messagebuf);
	gotobob(FALSE, 1);
    }

    switch(setjmp(finstate)){		/* prepare for/handle final events */
      case COMP_EXIT :
	vttidy();			/* already confirmed */
	packheader();
	return((packbuf(&pm->messagebuf, &pm->messbuflen,
			pm->pine_flags&P_LOCALLF)| COMP_EXIT));
      case COMP_SUSPEND :
	vttidy();			/* suspend it! */
	packheader();
	return((packbuf(&pm->messagebuf, &pm->messbuflen, 
			pm->pine_flags&P_LOCALLF)| COMP_SUSPEND));
      case COMP_CANCEL :
	writeout(MAILGRAVE);		/* already confirmed */
	vttidy();
	zotedit();
	return(COMP_CANCEL);

      case COMP_GOTHUP:
	/* 
	 * pack up and let caller know that we've received a SIGHUP
	 */
	if(ComposerEditing)		/* expand addr if needed */
	  resolve_niks(ods.cur_e);
	vttidy();
	packheader();
	return((packbuf(&pm->messagebuf, &pm->messbuflen, 
			pm->pine_flags&P_LOCALLF)| COMP_GOTHUP));

      default:
	break;
    }

    if(pm->pine_flags&P_REPLY){		/* begin editing the header? */
	ArrangeHeader();		/* line up pointers */
    }
    else{
	update();			/* paint screen, */
	HeaderEditor(0, 0);		/* start editing... */
    }

    while(1){
	update();			/* Fix up the screen    */

	c = GetKey();	

	if(c == NODATA || time_to_check()){	/* new mail ? */
	    if((*Pmaster->newmail)(&i, 0, c == NODATA ? 0 : 2) >= 0){
		mlerase();
		(*Pmaster->showmsg)(c);
		mpresf = 1;
	    }

	    if(i || mpresf)		/* let em know cursor moved */
	      movecursor(0, 0);

	    if(c == NODATA)		/* no op, getkey timed out */
	      continue;
	}

	if (mpresf != FALSE) {		/* message stay around only  */
	    if (mpresf++ > MESSDELAY)	/* so long! */
	      mlerase();
	}

	f = FALSE;			/* vestigial */
	n = 1;
	
	execute(normal(c, pfkm, 2), f, n);	/* Do it.               */
    }
}

/*
 * Initialize all of the buffers and windows. The buffer name is passed down
 * as an argument, because the main routine may have been told to read in a
 * file by default, and we want the buffer name to be right.
 */

/*
 * For the pine composer, we don't want to take over the whole screen
 * for editing.  the first some odd lines are to be used for message 
 * header information editing.
 */
edinit(bname)
char    bname[];
{
    register BUFFER *bp;
    register WINDOW *wp;

    if(Pmaster)
      func_init();

    bp = bfind(bname, TRUE, 0);             /* First buffer         */

    if(Pmaster == NULL)
      blistp = bfind("[List]", TRUE, BFTEMP); /* Buffer list buffer   */

    wp = (WINDOW *) malloc(sizeof(WINDOW)); /* First window         */

    if(Pmaster){
	if (bp==NULL || wp==NULL)
	  return(0);
    }
    else{
        if (bp==NULL || wp==NULL || blistp==NULL)
	  exit(1);
    }

    curbp  = bp;                            /* Make this current    */
    wheadp = wp;
    curwp  = wp;
    wp->w_wndp  = NULL;                     /* Initialize window    */
    wp->w_bufp  = bp;
    bp->b_nwnd  = 1;                        /* Displayed.           */
    wp->w_linep = bp->b_linep;
    wp->w_dotp  = bp->b_linep;
    wp->w_doto  = 0;
    wp->w_markp = NULL;
    wp->w_marko = 0;

    if(Pmaster){
	wp->w_toprow = COMPOSER_TOP_LINE;
	wp->w_ntrows = term.t_nrow - COMPOSER_TOP_LINE - 2;
	fillcol = (term.t_ncol > 80) ? 80 : term.t_ncol - 6;
    }
    else{
        wp->w_toprow = 2;
        wp->w_ntrows = term.t_nrow-4;           /* "-1" for mode line.  */
	fillcol = term.t_ncol - 6;              /* set fill column */
    }

    wp->w_force = 0;
    wp->w_flag  = WFMODE|WFHARD;            /* Full.                */
}


/*
 * This is the general command execution routine. It handles the fake binding
 * of all the keys to "self-insert". It also clears out the "thisflag" word,
 * and arranges to move it to the "lastflag", so that the next command can
 * look at it. Return the status of command.
 */
execute(c, f, n)
{
    register KEYTAB *ktp;
    register int    status;

    ktp = (Pmaster) ? &keytab[0] : &pkeytab[0];

    while (ktp->k_fp != NULL) {
	if (ktp->k_code == c) {

	    if(lastflag&CFFILL){
		curwp->w_flag |= WFMODE;
		if(Pmaster == NULL)
		  sgarbk = TRUE;
	    }

	    thisflag = 0;
	    status   = (*ktp->k_fp)(f, n);
	    lastflag = thisflag;

	    return (status);
	}
	++ktp;
    }


    /*
     * Check to make sure that we're not going to go off of the screen
     * with our next character.  If so wrap the line...
     * note: in pico,  fillcol and wrap-mode are always set and 
     * 	 wrapword behaves somewhat differently
     */
    if(((llength(curwp->w_dotp)+2 >= term.t_ncol) 
	|| (c == ' ' && getccol(FALSE) > fillcol)) 
       && (curwp->w_bufp->b_mode & MDWRAP))
      wrapword();


    if ((c>=0x20 && c<=0x7E)                /* Self inserting.      */
        ||  (c>=0xA0 && c<=0xFE)) {

	if (n <= 0) {                   /* Fenceposts.          */
	    lastflag = 0;
	    return (n<0 ? FALSE : TRUE);
	}
	thisflag = 0;                   /* For the future.      */

	/* if we are in overwrite mode, not at eol,
	   and next char is not a tab or we are at a tab stop,
	   delete a char forword			*/
	if (curwp->w_bufp->b_mode & MDOVER &&
	    curwp->w_doto < curwp->w_dotp->l_used &&
	    (lgetc(curwp->w_dotp, curwp->w_doto) != '\t' ||
	     (curwp->w_doto) % 8 == 7))
	  ldelete(1, FALSE);

	/* do the appropriate insertion */
	/* pico never does C mode, this is simple */
	status = linsert(n, c);

	lastflag = thisflag;
	return (status);
    }
    
    if(c&CTRL)
      emlwrite("\007Unknown Command: ^%c", c&0xff);
    else
      emlwrite("\007Unknown Command", NULL);

    lastflag = 0;                           /* Fake last flags.     */
    return (FALSE);
}



/*
 * Fancy quit command, as implemented by Norm. If the any buffer has
 * changed do a write on that buffer and exit emacs, otherwise simply exit.
 */
quickexit(f, n)
{
    register BUFFER *bp;	/* scanning pointer to buffers */

    bp = bheadp;
    while (bp != NULL) {
	if ((bp->b_flag&BFCHG) != 0	/* Changed.             */
	    && (bp->b_flag&BFTEMP) == 0) {	/* Real.                */
	    curbp = bp;		/* make that buffer cur	*/
	    filesave(f, n);
	}
	bp = bp->b_bufp;			/* on to the next buffer */
    }
    wquit(f, n);                             /* conditionally quit   */
}



/* 
 * abort_composer - ask the question here, then go quit or 
 *                  return FALSE
 */
abort_composer(f,n)
{
    switch(mlyesno("Cancelling will abandon your mail message.  Cancel", 
		    FALSE)){
      case TRUE:
	longjmp(finstate, COMP_CANCEL);
	break;
      case ABORT:
	emlwrite("\007Cancel Aborted", NULL);
	break;
      default:
	mlerase();
	break;
    }
    return(FALSE);
}


/*
 * suspend_composer - return to pine with what's been edited so far
 */
suspend_composer(f, n)
{
    longjmp(finstate, COMP_SUSPEND);
}



/*
 * Quit command. If an argument, always quit. Otherwise confirm if a buffer
 * has been changed and not written out. Normally bound to "C-X C-C".
 */
wquit(f, n)
{
    register int    s;

    if(Pmaster){
	/*
	 * if we're not in header, show some of it as we verify sending...
	 */
	setmark(FALSE, 1);
	if(ComposerTopLine == COMPOSER_TOP_LINE){
	    gotobob(FALSE, 1);
	    update();
	}

	switch(mlyesno("Send message", TRUE)){
	  case TRUE:
	    longjmp(finstate, COMP_EXIT);
	    break;
	  case ABORT:
	    emlwrite("\007Send Message Aborted", NULL);
	    break;
	  default:
	    mlerase();
	    break;
	}

	swapmark(FALSE, 1);
    }
    else{
        if (f != FALSE                          /* Argument forces it.  */
        || anycb() == FALSE                     /* All buffers clean.   */
						/* User says it's OK.   */
        || (s=mlyesno("Modified buffer: Save before leaving", -1)) == FALSE) {
                vttidy();
                exit(0);
        }

	if(s == TRUE){
	    if(filewrite(0,1) == TRUE)
	      wquit(1, 0);
	}
	else if(s == ABORT){
	    emlwrite("\007Exit Aborted");
	}
        return(s);
    }
}


/*
 * Abort.
 * Beep the beeper. Kill off any keyboard macro, etc., that is in progress.
 * Sometimes called as a routine, to do general aborting of stuff.
 */
ctrlg(f, n)
{
    (*term.t_beep)();
    if (kbdmip != NULL) {
	kbdm[0] = (CTLX|')');
	kbdmip  = NULL;
    }
    emlwrite("Aborted");
    return (ABORT);
}


/* tell the user that this command is illegal while we are in
 *  VIEW (read-only) mode
 */
rdonly()
{
    (*term.t_beep)();
    emlwrite("Key illegal in VIEW mode");
    return(FALSE);
}



/*
 * reset all globals to their initial values
 */
func_init()
{
    extern int    vtrow;
    extern int    vtcol;
    extern int    lbound;
    extern int    ttrow;
    extern int    ttcol;

    /*
     * re-initialize global buffer type variables ....
     */
    fillcol = (term.t_ncol > 80) ? 80 : term.t_ncol - 6;
    eolexist = TRUE;
    revexist = FALSE;
    sgarbf = TRUE;
    mpresf = FALSE;
    mline_open = FALSE;
    ComposerEditing = FALSE;

    /*
     * re-initialize hardware display variables ....
     */
    vtrow = vtcol = lbound = 0;
    ttrow = ttcol = HUGE;

    pat[0] = rpat[0] = '\0';
}


/*
 * pico_help - help function for standalone composer
 */
pico_help(text, title, i)
char *text[], *title;
int i;
{
    register    int numline = 0;
    char        **p;
 
    p = text;
    while(*p++ != NULL) 
      numline++;
    return(wscrollw(COMPOSER_TOP_LINE, term.t_nrow-1, text, numline));

}



#if     TERMCAP
/*
 * zottree() - kills the key pad function key search tree
 *                and frees all lines associated with it!!!
 */
zottree(nodeptr)
struct	KBSTREE	*nodeptr;
{
    if(nodeptr != NULL){
	zottree(nodeptr->left);
	zottree(nodeptr->down);
	free((char *) nodeptr);
    }
}
#endif


/*
 * zotedit() - kills the buffer and frees all lines associated with it!!!
 */
zotedit()
{
    register BUFFER	*bp;
    register WINDOW	*wp;
    register LINE	*lp;
    register int	s;

    /*
     * clean up the lines and buffer ...
     */
    curbp->b_flag &= ~BFCHG;

    if ((s=bclear(curbp)) != TRUE)		/* Blow text away.      */
      return(s);

    free((char *) wheadp);			/* clean up window */
    wheadp = NULL;
    curwp  = NULL;

    free((char *) bheadp);			/* clean up buffers */
    bheadp = NULL;
    curbp  = NULL;

    zotheader();				/* blast header lines */

    zotdisplay();				/* blast display buffers */

#if     TERMCAP
    zottree(kpadseqs);
    kpadseqs = NULL;
#endif
}
