/***************************************************************************
 *                                                                         *
 *   FSDBBS.C                                                              *
 *                                                                         *
 *   Copyright (c) 1992-1997 Galacticomm, Inc.      All Rights Reserved.   *
 *                                                                         *
 *   Full-Screen Data Entry interface for Worldgroup                       *
 *                                                                         *
 *   See FSDBBS.H and FSD.H for instructions on using FSD                  *
 *                                                                         *
 *                                             - R. Stein  2/12/92         *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"
#include "fsdbbs.h"
#include "wgsfsd.h"
#include "majorbbs.h"

static GBOOL fsdinp(VOID);
static VOID fsdsts(VOID);
static GBOOL fsdinj(VOID);
static VOID fsdhup(VOID);
static VOID fsdcls(VOID);

INT fsdstt;                        /* FSD module state number              */

struct module fsdmod={             /* module interface block               */
     "",                           /*    name used to refer to this module */
     fsdinp,                       /*    user logon supplemental routine   */
     fsdinp,                       /*    input routine if selected         */
     fsdsts,                       /*    status-input routine if selected  */
     fsdinj,                       /*    "injoth" routine for this module  */
     fsdinp,                       /*    user logoff supplemental routine  */
     fsdhup,                       /*    hangup (lost carrier) routine     */
     NULL,                         /*    midnight cleanup routine          */
     NULL,                         /*    delete-account routine            */
     fsdcls                        /*    finish-up (sys shutdown) routine  */
};

GBOOL infsdhup=FALSE;      /* are we in the middle of FSD hangup handling? */

struct fsdbbs *fsdusr,*fsdtbl=NULL;     /* array of stuff for current user */

CHAR *fsdbuf;                           /* temporary (non-retained) buffer */
INT fsdomt;                            /* exact output buffer size for FSD */
HMCVFILE fsdmb;                          /* FSD named-message file pointer */

                 /*--- variables supporting fsdous(), fsdouc(), fsdnfy() ---*/
CHAR qikout=0;           /* 0=called from mainline, 1=called from interrupt */
INT qikchn;                           /* channel when called from interrupt */

/* substt state codes */
#define ENTERING 1                                       /* data entry mode */
#define FINISHING 2                                     /* finishing output */

VOID
setfsd(INT chan)             /* set fsd context, acc to channel 0..nterms-1 */
{
     fsdusr=&fsdtbl[chan];
     fsdscb=&fsdusr->fsdscb;
}

static VOID
inifsdscb(VOID)          /* allocate fsd user array, if not done so already */
{
     if (fsdtbl == NULL) {
          fsdtbl=(struct fsdbbs *)alczer(nterms*sizeof(struct fsdbbs));
     }
}

VOID
inifsd(VOID)                      /* fsdbbs initializer under Worldgroup    */
{
     CHAR *cp;

     stzcpy(fsdmod.descrp,gmdnam("wgsfsd.mdf"),MNMSIZ);
     fsdmb=opnmsg("wgsfsd.mcv");
     almmmx=ynopt(ALMMMX);
     udwrap=ynopt(UDWRAP);
     cp=rawmsg(SECCHR);
     secchr=cp[strlen(cp)-1];      /* instead of chropt() so ' ' is valid  */
     fsdstt=register_module(&fsdmod);
     fsdini();
     fsdbuf=alcmem(fsdbln);
     inifsdscb();
     fsdomt=outbsz-1;
}

static VOID
fsdcon(VOID)                            /* turn on channel settings for FSD */
{
     btuche(usrnum,1);
     btulfd(usrnum,0);
     btuscr(usrnum,0);
     btuchi(usrnum,fsdchi);
     btuech(usrnum,0);
     btucli(usrnum);
     btuxnf(usrnum,0,19);
     btupbc(usrnum,0);
     btutru(usrnum,0);
}

static VOID
fsdcof(VOID)                           /* turn off channel settings for FSD */
{
     btuchi(usrnum,NULL);
     echon();
     btuche(usrnum,0);
     btulfd(usrnum,'\n');
     btuscr(usrnum,'\n');
     btutru(usrnum,15);
     rstrxf();
     btutsw(usrnum,usaptr->scnwid);
}

INT
fsdroom(
INT tmpmsg,                                      /* .MCV index for template */
CHAR *fldspc,                     /* permanent copy of field spec in memory */
INT amode)                /* 1=full-screen entry, 0=linear entry -1=display */
          /* returns number of bytes session will need, or -1=error in data */
                      /* WARNING:  corrects invalid usrnum, corrupts prfbuf */
{
     INT errors;
     INT savunm;

     savunm=usrnum;
     inifsdscb();
     if (usrnum < 0 || usrnum >= nterms) {
          usrnum=0;
     }
     setfsd(usrnum);
     fsdscb->fldspc=fldspc;
     fsdscb->mbpunc=prfbuf;
     fsdscb->flddat=(struct fsdfld *)(prfbuf+MBPMAX);
     maxfld=(outbsz-MBPMAX)/sizeof(struct fsdfld);
     fsdusr->curmbk=curmbk;
     fsdusr->tmpmsg=tmpmsg;
     fsdusr->amode=amode;
     if ((errors=fsdppc(amode == -1 ? getmsg(tmpmsg)
               : xlttxv(getasc(tmpmsg),mxmssz),
                        amode == 1)) > 0) {
          catastro("FSD Template/Field Spec %d has %d error(s)."
             "\nFirst error is: %s",
             tmpmsg,errors,fsdemg);
     }
     if (fsdscb->numfld >= maxfld) {      /* (waste 1 field for ovfl check) */
          catastro("FSD Field Spec has too many fields");
     }
     if (fsdscb->mbleng > MBPMAX) {
          catastro("FSD Template %d has too much punctuation, needs "
             "%d more bytes",tmpmsg,MBPMAX-fsdscb->mbleng);
     }
     usrnum=savunm;
     return(fsdscb->mbleng
         +fsdscb->numfld*sizeof(struct fsdfld)
         +fsdscb->maxans+1);
}

VOID
fsdapr(                           /* prepare answers (call after fsdroom()) */
CHAR *sesbuf,                /* region of bytes, size returned by fsdroom() */
INT sbleng,          /* allocated bytes at sesbuf (for error checking only) */
CHAR *answers)               /* default answers (ok if comes from getmsg()) */
             /* expects prfbuf contents exactly as left over from fsdroom() */
                                       /* clears prfbuf[] when done with it */
{
     INT nfld,n;

     setfsd(usrnum);
     if ((sbleng-=fsdscb->mbleng
       +fsdscb->numfld*sizeof(struct fsdfld)
       +fsdscb->maxans+1) < 0) {
          catastro("FSD invoked with a buffer %d bytes too small",-sbleng);
     }
     movmem(prfbuf,sesbuf,n=fsdscb->mbleng);
     fsdscb->mbpunc=sesbuf;
     nfld=fsdscb->numfld*sizeof(struct fsdfld);
     movmem(prfbuf+MBPMAX,sesbuf+n,nfld);
     fsdscb->flddat=(struct fsdfld *)(sesbuf+n);
     fsdscb->newans=sesbuf+n+nfld;
     fsdans(answers);
     fsdscb->crsatr=0x70;
     clrprf();
     prf("");
}

VOID
fsdbkg(                    /* display background for full-screen entry mode */
CHAR *templt)                                  /* template in raw text form */
{
     prf("\x1B[0m\x1B[2J\x1B[0m");
     btutsw(usrnum,0);
     btulok(usrnum,1);              /* Turn off keyboard till all displayed */
     btuoes(usrnum,1);           /* Let us know when screen display is done */
     fsddsp(templt);
}

VOID
fsdego(         /* begin FSD entry session (call after fsdroom(), fsdapr()) */
INT (*fldvfy)(                             /* field verify routine, or NULL */
     INT fldno,                                    /* field number 0 to N-1 */
     CHAR *answer),                                      /* proposed answer */
VOID (*whndun)(             /* when-done routine (must restore state, etc.) */
     SHORT save))                                          /* 1=save 0=quit */
                                      /* (expects caller to outprf(usrnum)) */
{
     fsdscb->fldvfy=fldvfy;
     if (fsdusr->amode == 1) {
          fsdent(0);
          fsdusr->flags|=FBFULL;
     }
     else {
          fsdlin();
          fsdusr->flags&=~FBFULL;
     }
     usrptr->state=fsdstt;
     usrptr->substt=ENTERING;
     fsdusr->whndun=(VOID (*)(INT))whndun;
     ainscb=&fsdusr->ainscb;
     ainbeg();
     fsdcon();
}

static VOID
goback(VOID)                                      /* go back to application */
{
     fsdcof();
     clrprf();
     if (fsdusr->flags&FBFULL) {
          prf("\x1B[%d;1f",min(ANSILN,fsdscb->maxy+1));
     }
     prf("\x1B[0;1;32m");
     outprf(usrnum);
     prf("");
     if (fsdusr->whndun != NULL) {
          fsdusr->whndun((fsdusr->flags&FBSAVE) != 0);
     }
     else {
          btuinj(usrnum,CRSTG);
     }
     outprf(usrnum);
}

VOID
fsdrhd (titl)                  /* send header (w/ title) for RIP FSD session */
CHAR *titl;                    /*      title to display                      */
{
     if (isripu()) {
          setmbk(fsdmb);
          prfmsg(FSDHDR,titl);
          rstmbk();
     }
}

/*--- Entry points for Worldgroup ---*/

static GBOOL
fsdinp(VOID)                   /* status-3 entry point, or suplemental link */
{
     return(usrptr->substt != 0);
}

static VOID
fsdsts(VOID)                                       /* FSDBBS status handler */
{
     if (status == OUTMT && usrptr->substt == ENTERING) {
          btuoes(usrnum,0);
          btulok(usrnum,0);
          return;
     }
     if (status != CYCLE) {
          dfsthn();
          return;
     }
     setfsd(usrnum);
     switch(usrptr->substt) {
     case ENTERING:
          clrprf();
          prf("");
          switch(fsdprc()) {
          case 0:
               outprf(usrnum);
               break;
          case 1:
               fsdusr->flags|=FBSAVE;
               usrptr->substt=FINISHING;
               fsdnfy();
               break;
          case -1:
               fsdusr->flags&=~FBSAVE;
               usrptr->substt=FINISHING;
               fsdnfy();
               break;
          }
          break;
     case FINISHING:
          if (btuoba(usrnum) == outbsz-1) {
               goback();
          }
          else {
               actdet=0;
               fsdnfy();
          }
          break;
     }
}

static VOID
fsdhup(VOID)
{
     setfsd(usrnum);
     if (usrptr->state == fsdstt) {
          infsdhup=TRUE;
          goback();
          infsdhup=FALSE;
     }
     setmem(fsdusr,sizeof(struct fsdbbs),0);
}

static VOID
fsdcls(VOID)
{
     free(fsdbuf);
     free(fsdtbl);
     clsmsg(fsdmb);
}

/*--- Exit points for Worldgroup ---*/

CHAR
fsdchi(                        /* btuchi() character input  handler for FSD */
INT chan,
INT c)
{
     struct fsdscb *scbsav;
     struct fsdbbs *fsusav;
     struct ainscb *ainsav;

     scbsav=fsdscb;
     fsusav=fsdusr;
     setfsd(chan);
     if (fsdscb->newans == NULL) {
          return(0);
     }
     qikchn=chan;
     qikout=1;
     if (c == -1) {
          fsdqoe();
     }
     else {
          ainsav=ainscb;
          ainscb=&fsdusr->ainscb;
          if ((c=ainchr(c)) != 0) {
               if (c < 256) {
                    c&=eurmsk;
               }
               fsdinc(c);
          }
          ainscb=ainsav;
     }
     qikout=0;
     fsdusr=fsusav;
     fsdscb=scbsav;
     return(0);
}

/*--- Exit points for FSD ---*/

VOID
fsdnfy(VOID)                  /* Notify system to call fsdprc() when it can */
{
     if (qikout) {
          chiinj(qikchn,CYCLE);
     }
     else {
          btuinj(usrnum,CYCLE);
     }
}

VOID
fsdous(CHAR *str)                                         /* display string */
{
     if (qikout) {
          chious(qikchn,str);
     }
     else {
          prf("%s",str);
     }
}

VOID
fsdouc(CHAR c)                                         /* display character */
{
     if (qikout) {
          chiout(qikchn,c);
     }
     else {
          prf("%c",c);
     }
}

INT
fsdoba(VOID)                    /* Output buffer bytes available, how many? */
{
     return(btuoba(usrnum));
}

VOID
fsdqdp(VOID)          /* low-level initiation of redisplay of entire screen */
{
     fsdusr->flags|=FBRDSP;
     fsdnfy();
}

CHAR *
fsdrft(VOID)                    /* refresh template (returns pointer to it) */
  /* returned copy of template only expected to last until fsdprc() returns */
{
     CHAR *cp;

     setmbk(fsdusr->curmbk);
     cp=(fsdusr->amode == -1 ? getmsg(fsdusr->tmpmsg)
      : xlttxv(getasc(fsdusr->tmpmsg),mxmssz));
     rstmbk();
     return(cp);
}

GBOOL
fsdinj(VOID)
{
    return(FALSE);
}

INT
infsd(
INT usn,
VOID (*exipnt)())    /* is user in FSD from here?                          */
{
     setfsd(usn);
     return(usroff(usn)->state == fsdstt && fsdusr->whndun == exipnt);
}
