/* ------------------------------------------------------------------------ */
/*                      BUFFER ABSTRACTION OVERLAY                          */
/* ------------------------------------------------------------------------ */

#include "pcstdio.h"
#include "pcdefs.h"
#include "pcglobal.h"

/*      Function table here is compiled ONLY IF overlays are activated. */

#ifdef OVLON
int BSwap();
int BReDraw();
int KFind();
int KAssocBuf();
int KSwitchBuf();
int KStatBuf();
int KKillBuf();
int KWinOther();
int KDnPage();
int KRtPage();
int KLtPage();
int KUpPage();
int KDnOPage();
int KRtOPage();
int KLtOPage();
int KUpOPage();

extern  int KLabel();
extern  int KFormula();
extern  int KNumber();
extern  int KBackSpace();
extern  int KNewline();
extern  int KBottom();
extern  int KFarRight();
extern  int KFarLeft();
extern  int KTop();
extern  int KDown();
extern  int KUp();
extern  int KRight();
extern  int KLeft();
extern  int KExchange();
extern  int KSetMark();
extern  int KUnivArg();
extern  int KAbort;
extern  int KReDraw();
extern  int KReCalc();
extern  int KOther();

int (*Functions[])() =   {
	&KBackSpace,
	&KNewline,
	&KDown,
	&KUp,
	&KRight,
	&KLeft,

	&KLabel,                /* These functions are mainly in PCCOMD.C       */
	&KFormula,
	&KNumber,
	&KBottom,
	&KFarRight,
	&KFarLeft,
	&KTop,
	&KExchange,
	&KSetMark,
	&KUnivArg,
	&KAbort,
	&KAbort,
	&KAbort,
	&KReDraw,
	&KReCalc,

	0,0,0,0,0,0,0,0,0,0,
	/*                      these functions are overlay PCREPLI.C
	        &KErase,
	        &KRErase,
	        &KPick,
	        &KRPick,
	        &KPut,
	        &KRPut,
	        &KInsLine,
	        &KDelLine,
	        &KInsColumn,
	        &KDelColumn,
	*/
	1,1,1,1,1,1,1,1,1,1,1,1,
	/*                         these functions are in overlay PCIO.C
	        &KPosition,
	        &KNxtUnlkd,
	        &KWidth,
	        &KFormat,
	        &KFormat,
	        &KJust,
	        &KPrint,
	        &KRPrint,
	        &KRead,
	        &KWrite,
	        &KSave,
	        &KZap,
	*/
	2,2,2,2,2,2,2,2,2,2,2,2,2,2,
	/*                      these functions are in overlay PCWIN.C
	        &KLock,
	        &KRLock,
	        &KUnlock,
	        &KRUnlock,
	        &KEdit,
	        &KWin2,
	        &KWin1,
	        &KWhere,
	        &KUpdate,
	        &KTitles,
		   &KF1Hlp,				/* XXX */
	&KSF1Hlp,				/* XXX */
	&KAF1Hlp,				/* XXX */
	&KCF1Hlp,				/* XXX */
	&KHelp,
	&KMHelp,
	&KXHelp,
	&KExit,
	These functions are in this overlay */
	&BSwap,
	&BReDraw,
	&KSwitchBuf,
	&KKillBuf,
	&KStatBuf,
	&KFind,
	&KAssocBuf,
	&KWinOther,
	&KDnPage,
	&KUpPage,
	&KRtPage,
	&KLtPage,
	&KDnOPage,
	&KUpOPage,
	&KRtOPage,
	&KLtOPage,

	&KOther    };
#endif

/* Following are the ONLY two functions in the *Functions[]() table which
   are not called from a keystroke but directly from another routine.

   ReCalc() calls BSwap() using CallFunc(BSWAP) to swap Buff[RecBuf]
   into current memory for recalculation.

   getch() calls BReDraw() using CallFunc(BREDRAW) from PCMAIN.C to
   swap windows for a ReDraw(w2) operation when w2->curbuf != w->curbuf
*/

extern int RecBuf;      /* comm. from ReCalc() to here */
BSwap()
{   
	int OK;
	if (OK=BSaveBuf(1)) BReadBuf(RecBuf, w, 1);
	w->curbuf = RecBuf;  
	return(OK);
}

BReDraw()               /* ReDraw w2 when it contains a different buffer */
{   
	int MovPage();  
	OPage(MovPage);   
}

KSwitchBuf()    /* switch to a different buffer */
{   
	int b;
	CmdStart("Switch to Buffer <CR>: ");  
	if (!BGetName()) goto rt;
	if ((b=BNdxBuf(EntBuf)) == -1)
	{   
		if (!confirm("Create new buffer? ")) goto rt;
		if ((b=BMakeBuf(EntBuf,DFFILE)) == -1)
		{   
			CmdStart("\007All buffers in use!");  
			LvCmdLine();  
			return;   
		}
	}
	if (BSaveBuf(1)) BReadBuf(b, w, 1);
	w->prevbuf = w->curbuf;  
	w->curbuf = b;  
	ReDraw(w);
rt:
	ZapCmdLine();
}

KKillBuf()      /* delete a buffer */
{   
	int db, i;  
	struct buffer *Madr();
	CmdStart("Delete Buffer <CR>: ");  
	if (!BGetName()) goto rt;
	if ((db=BNdxBuf(EntBuf)) == -1)
	{   
		CmdStart("\007Nonexistent buffer!");  
		goto er;   
	}
	if (db == w->curbuf)
	{   
		CmdStart("\007Cannot delete current buffer!");  
		goto er;   
	}
	for (i=0; i<MAXBUF; i++)
		if (Buff[i] && Madr(Buff[i])->linked == db)
		{   
			CmdStart("\007Cannot delete linked-to buffer!");
er:
			LvCmdLine();  
			return;
		}
	BDelBuf(db);
rt:
	ZapCmdLine();
}

KStatBuf()      /* output status of buffers */
{   
	int i, bufcount, bline;  
	struct buffer *b;
	bufcount = 1;  
	Tnorm();
	for (i=MAXBUF-1; i >= 0; i--)
		if (b=Madr(Buff[i]))
		{   
			Tposn((bline=Tlines-bufcount++), 1); 
			TCLEOL(); 
			format(b->bname);
			if (i == w->curbuf)
			{   
				b->bmod = modified;  
				b->blx = lastx;  
				b->bly = lasty;   
			}
			if (b->bmod) {
				Tposn(bline,10);  
				putch('*');
			}
			if (b->blx) {
				Tposn(bline,13);  
				outloc(b->blx,b->bly);
			}
			Tposn(bline,19);  
			format(b->bfile);
			if (b->linked != -1) 
			{   
				Tposn(bline, 35);  
				format("-> ");
				format(Madr(Buff[b->linked])->bname);
			}
		}
	Tposn(Tlines-bufcount,1);  
	TCLEOL();  
	ZapCmdLine();
}

KFind()         /* straight 'find file' command */
{   
	CmdStart("File to find <CR>: ");  
	Find(0);   
}

KAssocBuf()     /* Find file & link to it */
{   
	CmdStart("Associate file <CR>: ");  
	Find(1);   
}

Find(link)      /* find file, read into another buffer, (and maybe link) */
int link;
{   
	int i, NonTerm();  
	struct buffer *b, *cb, *Mchg();
	cb = Mchg(Buff[w->curbuf]);
	if ((i=FillBuf(0, NonTerm, EntBuf, 15)) < 0) goto rt;
	if (!i)                               /* "unlink buffer" cmd */
	{   
		if (link) {
			cb->linked = -1;  
			modified = 1;
		}  
		goto rt;   
	}
	strupper(EntBuf);	/* make filename uppercase */
	for (i=0; i<MAXBUF; i++)    /* see if it's already there */
		if ((b=Madr(Buff[i])) && strcmp(EntBuf, b->bfile))
		{   
			RecBuf = i;  
			break;   
		}
	if (i >= MAXBUF && (RecBuf=BMakeBuf("", EntBuf)) == -1) goto rt;
	if (link) {
		cb->linked = RecBuf;  
		modified = 1;
	}
	w->prevbuf = w->curbuf;  
	if (finfp != NULL) modified = 0;
	if (BSwap())
	{   
		ReDraw(w);
		if (i >= MAXBUF)
		{   
			gotkey = keybuf = Keys[READCOMD];  
			gotfile = 1;
			if (finfp != NULL) {
				fclose(finfp);  
				finfp = NULL;
			}
		}
	}
rt:
	ZapCmdLine();
}

KWinOther()
{   
	struct window *twp;
	if (!w2->active)
	{   
		CmdStart("\007No other window");  
		LvCmdLine();  
		return(0);   
	}
	w->cx = curx;  
	w->cy = cury;  
	if (OnScreen(w,curx,cury)) NoCursor();
	if (w->curbuf != w2->curbuf && BSaveBuf(0)) BReadBuf(w2->curbuf, w2, 0);
	twp = w;  
	w = w2;  
	w2 = twp;  
	curx = w->cx;  
	cury = w->cy;
	movecur(curx, cury);  
	return(1);
}

KDnPage()
{   
	int nline, n;
	nline = w->lines*cmdarg;
	if (nline+(n=w->homex+w->lines) > MAXLINE) nline = MAXLINE-n+1;
	w->homex += nline;  
	curx += nline;  
	MovPage();
}

KUpPage()
{   
	int nline;
	nline = w->lines*cmdarg;
	if (w->homex-nline < w->minx) nline = w->homex - w->minx;
	w->homex -= nline;  
	curx -= nline;  
	MovPage();
}

KRtPage()
{   
	int ncol, n;
	ncol = w->cols*cmdarg;
	if (ncol+(n=w->homey+w->cols) > MAXCOL) ncol = MAXCOL-n+1;
	w->homey += ncol;  
	cury += ncol;  
	MovPage();
}

KLtPage()
{   
	int ncol;
	ncol = w->cols*cmdarg;
	if (w->homey-ncol < w->miny) ncol = w->homey - w->miny;
	w->homey -= ncol;  
	cury -= ncol;  
	MovPage();
}

KDnOPage()  {
	OPage(KDnPage);
}
KUpOPage()  {
	OPage(KUpPage);
}
KRtOPage()  {
	OPage(KRtPage);
}
KLtOPage()  {
	OPage(KLtPage);
}

OPage(pfunc)    /* other page determined by *pfunc */
int (*pfunc)();
{   
	NoCurUpd = 1; 
	if (KWinOther()) (*pfunc)();  
	NoCurUpd = 0;  
	KWinOther();   
}

/* -------------------- Buffer Utilities ---------------------------------- */

BGetName()      /* get a buffer name from user and leave in Entbuf */
{   
	int len;  
	struct buffer *bp;
	if ((len=FillBuf(0, NonTerm, EntBuf, 9)) < 0) return(0);
	if (!len)
	{   
		if (!(bp=Madr(Buff[w->prevbuf]))) return(0);
		copystr(bp->bname,EntBuf);
	}
	return(1);
}

BMakeBuf(newname, newfile)      /* make a new buffer */
char *newname, *newfile;
{   
	int i, bn;  
	struct buffer *b;  
	char name[9], *AskMem();
	if (*newname) copystr(newname, name); 
	else
	{   
		BDerivName(newfile, name);
		while ((bn=BNdxBuf(name)) != -1)
		{   
			CmdStart("\007Buffer exists!  Buffer to use: ");
			if ((i=FillBuf(0, NonTerm, name, 8)) < 0 ||
			    (i == 0 && BDelBuf(bn) == -1)) return(-1);
		}
	}
	for (i=0; i<MAXBUF; i++)
		if (!Buff[i] && (b=Mchg(Buff[i]=(struct buffer *)AskMem(BUFDEFSIZE))))
		{   
			copystr(name, b->bname);  
			copystr(newfile, b->bfile);
			b->blx = b->bly = b->blsy = 0;  
			b->bscp = 0;
			b->bdp = b->bcolw = b->bj = 0;  
			b->bmod = b->updx[0] = 0;
			b->bcx = b->bcy = b->bhomex = b->bhomey = 1;
			b->bmarkx = b->bmarky = 1;  
			b->bjust = b->linked = -1;
			b->bcolwidth = DFCOLW;  
			b->bdecpl = DFDECPL;  
			return(i);
		}
	return(-1);
}

BDelBuf(n)      /* delete buffer n  -- return -1 if did not */
int n;
{   
	struct buffer *b;  
	int x,y;
	char **vlp, **lp, *velt, **scrp;
	b = Madr(Buff[n]);
	if (!b->bmod || confirm("Delete modified buffer? "))
	{   
		if (b->blx)     /* contains something to free */
		{   
			scrp = (char **) Madr(b->bscp);
			for (x=1; x<=b->blx; x++)           /* free every line */
				if (vlp = (char **) *(scrp+x))  /* line has something */
				{   
					lp = (char **) Madr(vlp);
					for (y=1; y<=b->blsy; y++)  /* free this line */
						if (velt = *(lp+y)) Mfree(velt);
					Mfree(--vlp);               /* free line pointer */
				}
			Mfree(b->bscp);  
			Mfree(b->bdp);  
			Mfree(b->bcolw);  
			Mfree(b->bj);
		}
		Mfree(Buff[n]);  
		Buff[n] = 0;  
		return(0);
	} 
	return(-1);
}

BSaveBuf(upd_win)       /* save window information into buffer */
int upd_win;
{   
	struct buffer *cb;  
	int i;
	char **sp, *dp, *wp, *jp;
	cb = Mchg(Buff[w->curbuf]);         /* convert to mem addr */
	if (lastx)                  /* save screen information */
	{   
		if (!(cb->bscp = (char **) AskMem(lastx+lastx+2)) || 
		    !(cb->bdp = AskMem(lasty+2)) || !(cb->bcolw = AskMem(lasty+2)) || 
		    !(cb->bj = AskMem(lasty+2))
		    ) return(0);
		sp = (char **) Mchg(cb->bscp);
		for (i=1; i<=lastx; i++) 
		{   
			*((char ***)(sp+i)) = screen[i];  
			screen[i] = 0;   
		}
		dp = (char *) Mchg(cb->bdp);  
		wp = (char *) Mchg(cb->bcolw);
		jp = (char *) Mchg(cb->bj);
		for (i=1; i<=lasty; i++) 
		{   
			*(dp+2+i) = coldp[i];  
			*(wp+2+i) = indcolw[i];
			*(jp+2+i) = coljust[i];
			coldp[i] = coljust[i] = indcolw[i] = 0xFF;
		}
	} 
	else {
		cb->bscp = 0;  
		cb->bdp = cb->bj = cb->bcolw = 0;
	}
	copystr(file, cb->bfile);  
	cb->blx = lastx;  
	cb->bly = lasty;  
	cb->blsy = scrny;  
	cb->bmod = modified;

	if (upd_win)	/* also update window information */
	{	
		cb->bcx = curx;  
		cb->bcy = cury;
		cb->bhomex = w->homex;  
		cb->bhomey = w->homey;
		cb->bcolwidth = w->colwidth;  
		cb->bdecpl = w->decpl;
		cb->bjust = w->just;  
		cb->bmarkx = w->markx;  
		cb->bmarky = w->marky;
	}
	return(1);
}

BReadBuf(b, cw, upd_win)        /* read from buffer b into window cw */
int b;  
struct window *cw;  
int upd_win;
{   
	int i;  
	struct buffer *cb;
	char **sp, *dp, *wp, *jp;
	cb = Mchg(Buff[b]);  
	copystr(cb->bfile, file);
	lastx = cb->blx;  
	lasty = cb->bly;  
	scrny = cb->blsy;  
	modified = cb->bmod;
	if (lastx) 
	{   
		sp = (char **) Madr(cb->bscp);  
		dp = (char *) Madr(cb->bdp);
		wp = (char *) Madr(cb->bcolw);  
		jp = (char *) Madr(cb->bj);
		for (i=1; i<=lastx; i++) screen[i] = *((char ***)(sp+i));
		for (i=1; i<=lasty; i++) 
		{   
			coldp[i] = *(dp+2+i);  
			indcolw[i] = *(wp+2+i);
			coljust[i] = *(jp+2+i);
		}
		Mfree(cb->bscp);  
		Mfree(cb->bdp);  
		Mfree(cb->bcolw);  
		Mfree(cb->bj);
	}
	if (upd_win)	/* also update window information */
	{	
		curx = cb->bcx;  
		cury = cb->bcy;
		cw->homex = (cb->bhomex >= cw->minx)? cb->bhomex : cw->minx;
		cw->homey = (cb->bhomey >= cw->miny)? cb->bhomey : cw->miny;
		cw->colwidth = cb->bcolwidth;  
		cw->decpl = cb->bdecpl;
		cw->just = cb->bjust;  
		cw->markx = cb->bmarkx;  
		cw->marky = cb->bmarky;
		cw->cols = NumCols(cw);
	}
}
