/***************************************************************************
 *                                                                         *
 *   TAKESYN.CPP                                                           *
 *                                                                         *
 *   Copyright (c) 1997 Galacticomm, Inc.                                  *
 *                                                                         *
 *                                                                         *
 *   Take Synthesis Class.                                                 *
 *                                                  - N.C. Osterc  9/06/98 *
 *                                                                         *
 ***************************************************************************/

#include "takesyn.h"
#include "galpnqah.h"
#include "dnfmgr.h"
#include "poll.h"
#include "question.h"
#include "galpnqh.h"
#include "majorbbs.h"

#define FILREV "$Revision: 17 $"

#define TAKEDIR     "take/"
#define LOOKPFX     "lookup_"
#define MULTANSPFX  "multans_"
#define HASTMPLPFX  (m_tmplPfx.length() > 0)
#define ISLOOKUP    (ISPARAM("lookup"))
#define ISSUBMIT    (ISPARAM("submit"))
#define ALLOWANON   (m_pPoll->getAnswerType() == ACCUMULATE && alwanon)
#define ISCLOSED    (m_pPoll->isClosed())
#define CANTTAKE    (m_pPoll->getAnswerType() <= ONCE && m_pPoll->isComplete(m_usrPtr->userid()))
#define ISDONE      (m_pPoll->getStatus(m_usrPtr->userid()) == STAT_DONE)
#define ISMAINT     (m_pPoll->getStatus(NULL) == STAT_MAINT)
#define ISDEL       (m_pPoll->getStatus(NULL) == STAT_DEL)
#define ISANON      (m_usrPtr == NULL)
#define HASTAKEKEY  (m_usrPtr->hasKey(m_pPoll->getTakeKey()))
#define HASLOOKKEY  (m_usrPtr->hasKey(m_pPoll->getLookupKey()))
#define STORDISCARD (m_pPoll->getStorageMethod() == DISCARD)
#define STORASCII   (m_pPoll->getStorageMethod() == ASCII)
#define CURUSER     (m_usrPtr->userid())
#define ANSWERA     "A"             // expected "true" or "yes" value
#define ANSWERB     "B"             // expected "false" or "no" value

#define MAXNUMSIZ   10              // max numeric-answer length

#define MC          100             // regular m-choice
#define MCMANS      101             // multi-answer mchoice

enum {MCITEM,MCMANSITEM};

template <class T>
string stringFormat(T val)         // format any value to a string
{
     string str;
     ostrstream ost;
     ost << val << ends;
     str=ost.str();
     ost.rdbuf()->freeze(0);
     return(str);
}


// dynafile stuff

dnfStep dnfTakeErrSteps[]={
     dnfStep(DNFMAPEND)
};

// dnfstep instances for questions

dnfStep CCSteps[]={
     dnfStep(DNFMAPEND)
};

dnfStep DateSteps[]={
     dnfStep(DNFMAPEND)
};

dnfStep DisplaySteps[]={
     dnfStep(DNFMAPEND)
};

dnfStep NumericSteps[]={
     dnfStep(DNFMAPEND)
};

dnfStep OpenSteps[]={
     dnfStep(DNFMAPEND)
};

dnfStep TFSteps[]={
     dnfStep(DNFMAPEND)
};

dnfStep Address1Steps[]={
     dnfStep(DNFMAPEND)
};

dnfStep Address2Steps[]={
     dnfStep(DNFMAPEND)
};

dnfStep Address3Steps[]={
     dnfStep(DNFMAPEND)
};

dnfStep Address4Steps[]={
     dnfStep(DNFMAPEND)
};

dnfStep ANSISteps[]={
     dnfStep(DNFMAPEND)
};

dnfStep BdaySteps[]={
     dnfStep(DNFMAPEND)
};

dnfStep EditorSteps[]={
     dnfStep(DNFMAPEND)
};

dnfStep NameSteps[]={
     dnfStep(DNFMAPEND)
};

dnfStep PasswordSteps[]={
     dnfStep(DNFMAPEND)
};

dnfStep PhoneSteps[]={
     dnfStep(DNFMAPEND)
};

dnfStep SexSteps[]={
     dnfStep(DNFMAPEND)
};

dnfStep YNSteps[]={
     dnfStep(DNFMAPEND)
};

dnfStep MCSteps[]={
     dnfStep(DNFTABLE),
          dnfStep(DNFCOLUMN,MCITEM,"LISTITEM"),
     dnfStep(DNFTABLEEND),
     dnfStep(DNFMAPEND)
};

dnfStep MCMansSteps[]={
     dnfStep(DNFTABLE),
          dnfStep(DNFCOLUMN,MCMANSITEM,"LISTITEM"),
     dnfStep(DNFTABLEEND),
     dnfStep(DNFMAPEND)
};

dnfStep DoneSteps[]={
     dnfStep(DNFMAPEND)
};

// dnfMap instances

dnfMap dnfTakeErrMap(PPFIX TAKEDIR "error.htm","take error",dnfTakeErrSteps);
dnfMap CCMap(PPFIX TAKEDIR "ccnumber.htm","credit card",CCSteps);
dnfMap DateMap(PPFIX TAKEDIR "date.htm","date",DateSteps);
dnfMap DisplayMap(PPFIX TAKEDIR "disponly.htm","Display",DisplaySteps);
dnfMap NumericMap(PPFIX TAKEDIR "numeric.htm","numeric",NumericSteps);
dnfMap OpenMap(PPFIX TAKEDIR "openend.htm","Open-ended",OpenSteps);
dnfMap TFMap(PPFIX TAKEDIR "truefalse.htm","True/False",TFSteps);
dnfMap Adr1Map(PPFIX TAKEDIR "address1.htm","Address1",Address1Steps);
dnfMap Adr2Map(PPFIX TAKEDIR "address2.htm","Address2",Address2Steps);
dnfMap Adr3Map(PPFIX TAKEDIR "address3.htm","Address3",Address3Steps);
dnfMap Adr4Map(PPFIX TAKEDIR "address4.htm","Address4",Address4Steps);
dnfMap ANSIMap(PPFIX TAKEDIR "ansi.htm","ANSI",ANSISteps);
dnfMap BdayMap(PPFIX TAKEDIR "bday.htm","Birthday",BdaySteps);
dnfMap EditorMap(PPFIX TAKEDIR "editor.htm","Editor",EditorSteps);
dnfMap NameMap(PPFIX TAKEDIR "name.htm","Name",NameSteps);
dnfMap PasswordMap(PPFIX TAKEDIR "password.htm","Password",PasswordSteps);
dnfMap PhoneMap(PPFIX TAKEDIR "phone.htm","Phone",PhoneSteps);
dnfMap SexMap(PPFIX TAKEDIR "sex.htm","Sex",SexSteps);
dnfMap YNMap(PPFIX TAKEDIR "yesno.htm","Yes/No",YNSteps);
dnfMap MCMap(PPFIX TAKEDIR "mchoice.htm","MultiChoice",MCSteps);
dnfMap MCMansMap(PPFIX TAKEDIR "multans_mchoice.htm","MultiAnswerChoice",
MCMansSteps);
dnfMap DoneMap(PPFIX TAKEDIR "done.htm","Done",DoneSteps);

struct flagMap {                   // struct of maps and flags
     dnfMap *theMap;
     SHORT qflag;
};

struct mcVal {                     // struct of mc-values to codes
     const CHAR *value;
     CHAR code;
};

struct mcVal mcArray[]= {            // mc-value to code array
     {NULL,'A'},
     {NULL,'B'},
     {NULL,'C'},
     {NULL,'D'},
     {NULL,'E'},
     {NULL,'F'},
     {NULL,'G'},
     {NULL,'H'},
     {NULL,'I'},
     {NULL,'J'},
     {NULL,'K'},
     {NULL,'L'},
     {NULL,'M'},
     {NULL,'N'},
     {NULL,'O'},
     {NULL,'P'},
     {NULL,0}
};

struct flagMap qTypeArray[]={      // flag to map array
     {&CCMap,CREDIT},
     {&DateMap,DATE},
     {&DisplayMap,DISONLY},
     {&MCMap,MULTICHC+MC},
     {&MCMansMap,MULTICHC+MCMANS},
     {&NumericMap,NUMERIC},
     {&OpenMap,OPEN_ENDED},
     {&TFMap,TRUEFALSE},
     {&YNMap,YESNO},
     {&NameMap,USERACCT+ACCFLD_NAME0},
     {&NameMap,USERACCT+ACCFLD_NAME1},
     {&Adr1Map,USERACCT+ACCFLD_ADR_LINE_1},
     {&Adr2Map,USERACCT+ACCFLD_ADR_LINE_2},
     {&Adr3Map,USERACCT+ACCFLD_ADR_LINE_3},
     {&Adr4Map,USERACCT+ACCFLD_ADR_LINE_4},
     {&PhoneMap,USERACCT+ACCFLD_PHONE},
     {&BdayMap,USERACCT+ACCFLD_BDAY},
     {&SexMap,USERACCT+ACCFLD_SEX},
     {&ANSIMap,USERACCT+ACCFLD_ANSI},
     {&EditorMap,USERACCT+ACCFLD_EDITOR},
     {&PasswordMap,USERACCT+ACCFLD_PASSWORD},
     {NULL,0}
};

// class method defs

MARKSOURCE(takesyn);

takeSynthesis::takeSynthesis(   // default constructor
                   acthSession *_ses):  // session object
                   pollSynthesis(_ses),
                   m_currentQ(0),
                   m_iter(0),
                   m_didOne(FALSE),
                   m_tmplPfx(""),
                   m_pollName(""),
                   m_lookUser(""),
                   m_bChosen(FALSE),
                   m_pAnsChoice(NULL),
                   m_doingTable(FALSE),
                   m_pValue(NULL),
                   m_rowCount(0)
{
     m_ahState=AH_POLLSTAT;
     initMCArray();
     m_pPoll=NULL;
}

takeSynthesis::~takeSynthesis()    // destructor
{
     if (m_pPoll != NULL) {
          delete m_pPoll;
          m_pPoll=NULL;
     }
}

VOID
takeSynthesis::initMCArray()       // init multiple choice array (from msg options)
{
     mcArray[0].value=valmc1;
     mcArray[1].value=valmc2;
     mcArray[2].value=valmc3;
     mcArray[3].value=valmc4;
     mcArray[4].value=valmc5;
     mcArray[5].value=valmc6;
     mcArray[6].value=valmc7;
     mcArray[7].value=valmc8;
     mcArray[8].value=valmc9;
     mcArray[9].value=valmc10;
     mcArray[10].value=valmc11;
     mcArray[11].value=valmc12;
     mcArray[12].value=valmc13;
     mcArray[13].value=valmc14;
     mcArray[14].value=valmc15;
     mcArray[15].value=valmc16;
}

ACTHCODE
takeSynthesis::proceed()           // proceed entry point
{
     ACTHCODE retval=ACTHMORE;
     ACTHCODE dynaval=ACTHMORE;

     if (!m_errorStatus) {
          if (m_pPoll == NULL) {
               if (getPollNameParam()) {
                    m_pPoll=new poll(m_pollName.c_str());
               }
               else {
                    m_ahState=AH_QERROR;
               }
          }
          if (m_pPoll != NULL) {
               if (ALLOWANON) {
                    if ((retval=initRequest()) != ACTHMORE) {
                         return(retval);
                    }
               }
               else if ((retval=isUserOK()) != ACTHMORE) {
                    return(retval);
               }
          }
          else if ((retval=initRequest()) != ACTHMORE) {
               return(retval);
          }
     }

     if (!m_cyc.start()) {
          return(retval);
     }

     do {
          switch (m_ahState) {
          case AH_POLLSTAT:
               m_ahState=AH_QPARAMS;
               if (m_usrPtr != NULL) {
                    if (ISLOOKUP) {
                         if (!HASLOOKKEY || STORDISCARD || STORASCII) {
                              return(ACTHFORBID);
                         }
                    }
                    else if (CANTTAKE) {
                         setError(ERRCOMP);
                         m_ahState=AH_QERROR;
                    }
                    else if (!HASTAKEKEY) {
                         return(ACTHFORBID);
                    }
               }
               if (!ISLOOKUP) {
                    if (ISCLOSED) {
                         setError(ERRCLOSE);
                         m_ahState=AH_QERROR;
                    }
                    else if (ISMAINT) {
                         setError(ERRMAINT);
                         m_ahState=AH_QERROR;
                    }
                    else if (ISDEL) {
                         setError(ERRDEL);
                         m_ahState=AH_QERROR;
                    }
               }
               break;
          case AH_QPARAMS:
               getTemplatePrefix();
               if (!getLookup()) {
                    m_ahState=AH_QERROR;
                    break;
               }
               if (ISSUBMIT) {
                    getQNumberParam();
                    m_ahState=AH_QSAVE;
               }
               else if (getQNumberParam()) {
                    m_ahState=AH_QSEND;
                    if (ISLOOKUP) {
                         if (!m_pPoll->nextLookup(m_lookUser.c_str(),m_currentQ,&m_currentQ)) {
                              m_ahState=AH_QDONE;
                         }
                    }
               }
               else {
                    m_ahState=AH_QERROR;
               }
               break;
          case AH_QSAVE:
               m_ahState=saveAnswers();
               break;
          case AH_QSEND:
               if (m_pQues == NULL) {
                    m_pQues=m_pPoll->getQuestion(m_currentQ);
               }
               else {
                    m_pQues=m_pPoll->getQuestion(m_pQues->getNext());
               }
               if (ISLOOKUP) {
                    if (!m_pPoll->isQuestion(m_currentQ)) {
                         dynaval=handleDynaResp(FALSE,NULL,lookupMap(&DoneMap));
                    }
                    else {
                         dynaval=handleDynaResp(FALSE,NULL,lookupMap(thisMap()));
                    }
               }
               else if (HASTMPLPFX) {
                    dynaval=handleDynaResp(FALSE,NULL,prefixMap(thisMap()));
               }
               else {
                    dynaval=handleDynaResp(FALSE,NULL,thisMap());
               }
               m_ahState=AH_QDYNA;
               break;
          case AH_QDONE:
               if (HASTMPLPFX) {
                    dynaval=handleDynaResp(FALSE,NULL,prefixMap(&DoneMap));
               }
               else {
                    dynaval=handleDynaResp(FALSE,NULL,&DoneMap);
               }
               m_ahState=AH_QDYNA;
               break;
          case AH_QERROR:
               dynaval=handleDynaResp(TRUE,&dnfTakeErrMap,NULL);
               m_ahState=AH_QDYNA;
               break;
          case AH_QDYNA:
               if (dynaval == ACTHDONE) {  // we got re-directed
                    return(ACTHDONE);
               }
               if (HASTMPLPFX) {
                    m_pTvb->setTemplatePrefix(m_tmplPfx.c_str());
               }
               if (m_pPoll != NULL && m_pQues != NULL) {
                    if (ISLOOKUP) {
                         m_pTvb->setTakeVbs(m_pQues,m_lookUser.c_str(),TRUE);
                         m_pTvb->setListVariables(m_pPoll,m_lookUser.c_str());
                    }
                    else if (ISANON) {
                         m_pTvb->setTakeVbs(m_pQues,NULL,FALSE);
                         m_pTvb->setListVariables(m_pPoll,NULL);
                    }
                    else {
                         m_pTvb->setTakeVbs(m_pQues,CURUSER,FALSE);
                         m_pTvb->setListVariables(m_pPoll,CURUSER);
                    }
               }
               dnfSetTemplateTvb(m_dnfPtr);
               if (m_pQues != NULL && m_pQues->getQType() == MULTICHC) {
                    retval=dynafile();
               }
               else {
                    retval=pollSynthesis::dynafile();
               }
               break;
          }
     }
     while(m_cyc.haveTime() && retval != ACTHDONE);

     return(retval);
}

const CHAR *
takeSynthesis::getSynthDir()       // synth directory
{
     return(PPFIX TAKEDIR);
}

VOID
takeSynthesis::getTemplatePrefix() // template prefix
{
     GETPARAMVAL("prefix");
     if (m_pValBuf[0] != '\0') {
          m_tmplPfx=m_pValBuf;
     }
}

dnfMap *
takeSynthesis::lookupMap(          // map for doing lookups
dnfMap *map)
{
     ASSERT(map != NULL);

     string mapFile=getSynthDir();
     mapFile+=LOOKPFX;
     CHAR mapFileBuf[GCMAXPTH];

     stlcpy(mapFileBuf,map->getFile(),GCMAXPTH);
     CHAR *ptr=strrchr(mapFileBuf,'/');
     if (ptr != NULL) {
          ++ptr;
          mapFile+=ptr;
     }
     else {
          mapFile+=&mapFileBuf[0];
     }
     return(dnfGetMap(map,mapFile.c_str()));
}

dnfMap *
takeSynthesis::prefixMap(         // map based on prefix
dnfMap *map)
{
     ASSERT(m_tmplPfx.length() > 0);
     ASSERT(map != NULL);

     string mapFile=getSynthDir();
     mapFile+=m_tmplPfx;

     CHAR mapFileBuf[GCMAXPTH];
     stlcpy(mapFileBuf,map->getFile(),GCMAXPTH);
     CHAR *ptr=strrchr(mapFileBuf,'/');
     if (ptr != NULL) {
          ++ptr;
          mapFile+=ptr;
     }
     else {
          mapFile+=&mapFileBuf[0];
     }
     return(dnfGetMap(map,mapFile.c_str()));
}

bool
takeSynthesis::getLookup()         // get look-up paramter
{
     GETPARAMVAL("lookup");
     if (m_pValBuf[0] != '\0') {
          if (!m_pPoll->isComplete(m_pValBuf)) {
               setError(ERRNCOMP);
               return(FALSE);
          }
          m_lookUser=m_pValBuf;
     }
     return(TRUE);
}

bool
takeSynthesis::getPollNameParam()  // get pollname parameter
{
     GETPARAMVAL("pollname");
     strupr(m_pValBuf);
     if (m_pValBuf[0] == '\0') {
          setError(ERRNOPN);
          return(FALSE);
     }
     else {
          if (m_pAPI->isPoll(m_pValBuf)) {
               m_pollName=m_pValBuf;
          }
          else {
               setError(ERRINVPN);
               return(FALSE);
          }
     }
     return(TRUE);
}

bool
takeSynthesis::getQNumberParam()   // get qnumber parameter
{
     GETPARAMVAL("qnumber");
     if (m_pValBuf[0] == '\0') {
          m_currentQ=0;
          if (!m_pPoll->isQuestion(m_currentQ)) {
               string curQ=stringFormat(m_currentQ);
               setError(ERRINVQN,curQ.c_str());
               return(FALSE);
          }
     }
     else {
          m_currentQ=atoi(m_pValBuf);
          if (!(ISLOOKUP) && !m_pPoll->isQuestion(m_currentQ)) {
               string curQ=stringFormat(m_currentQ);
               setError(ERRINVQN,curQ.c_str());
               return(FALSE);
          }
     }
     return(TRUE);
}

bool
takeSynthesis::moreQuestions()     // are there more questions after current?
{
     if (!m_pPoll->isQuestion(m_pQues->getNext())) {
          return(FALSE);
     }
     return(TRUE);
}

QUESSTATE
takeSynthesis::saveAnswers()       // save answers submitted to question(s)
{
     QUESSTATE qstate=AH_QSAVE;

     while (m_iter < MAXQUES) {
          bool didOne=FALSE;
          string sparam=qparam;
          sparam+=stringFormat(m_iter);
          GETPARAMVAL(sparam.c_str());
          if (m_pValBuf[0] != '\0') {
               if (m_pPoll->isQuestion(m_iter)) {
                    m_pQues=m_pPoll->getQuestion(m_iter);
               }
               else {
                    continue;
               }
               if (!answerValid(m_pValBuf)) {
                    qstate=AH_QERROR;
                    break;
               }
               if (!moreQuestions()) {
                    if (m_usrPtr != NULL) {
                         if (STORDISCARD || STORASCII
                          || m_pPoll->allAnswered(CURUSER)) {
                              m_pPoll->finishedPoll(CURUSER);
                              m_pPoll->grantKey(CURUSER);
                              m_pPoll->grantClass(CURUSER);
                              m_pPoll->grantCreds(CURUSER);
                         }
                         else {
                              setError(ERRNANSR);
                              qstate=AH_QERROR;
                         }
                    }
                    qstate=AH_QDONE;
                    break;
               }
               didOne=TRUE;
          }
          ++m_iter;
          if (didOne) {
               m_didOne=TRUE;
               break;
          }
     }
     if (qstate == AH_QSAVE && m_iter == MAXQUES) {
          if (m_didOne == FALSE) {  // found no questions
               setError(ERRNEVAL);
               qstate=AH_QERROR;
          }
          else {                    // done saving, send next question
               qstate=AH_QSEND;
          }
     }
     return(qstate);
}

bool
takeSynthesis::answerValid(
const CHAR *answer)
{
     bool validAns=FALSE;
     string sqnum=stringFormat(m_pQues->getNumber()+1);
     string lengthval;
     string theAnswer;
     bool isnumeric=FALSE;
     INT i=0;

     switch(m_pQues->getQType()) {
     case CREDIT:
          if (!alldgs(answer)) {
               setError(ERRCCNUM,sqnum.c_str(),NULL,m_pQues->getNumber());
          }
          else if (strlen(answer) < MINLENCC) {
               lengthval=stringFormat(MINLENCC);
               setError(ERRCCSHT,sqnum.c_str(),lengthval.c_str(),
                m_pQues->getNumber());
          }
          else if (strlen(answer) > MAXLENCC) {
               lengthval=stringFormat(MAXLENCC);
               setError(ERRCCLNG,sqnum.c_str(),lengthval.c_str(),
                m_pQues->getNumber());
          }
          else {
               theAnswer=answer;
               validAns=TRUE;
          }
          break;
     case DISONLY:
          return(TRUE);
     case DATE:
          INT y,m,d;
          dateDecode(answer,&y,&m,&d);
          if (!validDate(y,m,d)) {
               setError(ERRDINV,sqnum.c_str(),NULL,
                m_pQues->getNumber());
          }
          else {
               CHAR dateBuf[LDATSIZ];
               prnDate(y,m,d,dateBuf,LDATSIZ,PRND_MDYY,'/');
               theAnswer=dateBuf;
               validAns=TRUE;
          }
          break;
     case MULTICHC:
          // since we may be getting multiple answers here, two different
          // ways of handling input
          if (m_pQues->getNumAnswers() > 1) {
               CHAR nameBuf[PVALBUFSIZ];
               SHORT numAnswered=0;
               theAnswer.resize(16);
               for (INT it=0; it < ses->nparam(); it++) {
                    ses->param(it,nameBuf,PVALBUFSIZ,m_pValBuf,PVALBUFSIZ);
                    if (sameto(qparam,nameBuf)
                     || alldgs(nameBuf)) { // it's a question

                         i=0;
                         while (mcArray[i].value != NULL) {
                              if (m_pValBuf[0] != '\0'
                               && sameas(mcArray[i].value,m_pValBuf)) {
                                   if (m_pQues->isValMultiAnswer(i)) {
                                        theAnswer[i]=mcArray[i].code;
                                        ++numAnswered;
                                        validAns=TRUE;
                                   }
                                   else {
                                        setError(ERRMINV,sqnum.c_str(),
                                         NULL,m_pQues->getNumber());
                                        break;
                                   }
                                   break;
                              }
                              ++i;
                         }
                         if (numAnswered > m_pQues->getNumAnswers()) {
                              string snumans=stringFormat(m_pQues->getNumAnswers());
                              setError(ERR2MNY,sqnum.c_str(),snumans.c_str(),
                               m_pQues->getNumber());
                              validAns=FALSE;
                              break;
                         }
                    }
               }
          }
          else {
               i=0;
               while (mcArray[i].value != NULL) {
                    if (sameas(mcArray[i].value,answer)) {
                         theAnswer=mcArray[i].code;
                         if (m_pQues->getFlags()&BRANCH) {
                              saveOrTally(theAnswer);
                              m_currentQ=m_pQues->getBranch(i);
                              return(TRUE);
                         }
                         break;
                    }
                    ++i;
               }
               validAns=TRUE;
          }
          break;
     case NUMERIC:
          if (answer[0] == '-' && alldgs(answer+1)) {
               isnumeric=TRUE;
          }
          else if (alldgs(answer)) {
               isnumeric=TRUE;
          }
          if (!isnumeric) {
               setError(ERRNUMER,sqnum.c_str(),NULL,
                m_pQues->getNumber());
          }
          else if (strlen(answer) > MAXNUMSIZ) {
               lengthval=stringFormat(MAXNUMSIZ);
               setError(ERRNUMTL,sqnum.c_str(),lengthval.c_str(),
                m_pQues->getNumber());
          }
          else {
               validAns=TRUE;
               theAnswer=answer;
          }
          break;
     case OPEN_ENDED:
          if (strlen(answer) > ANSSIZ) {
               lengthval=stringFormat(ANSSIZ);
               setError(ERROPEN,sqnum.c_str(),lengthval.c_str(),
                m_pQues->getNumber());
          }
          else {
               validAns=TRUE;
               theAnswer=answer;
          }
          break;
     case TRUEFALSE:
          if (sameas(answer,valtrue)) {
               validAns=TRUE;
               theAnswer=ANSWERA;
          }
          else if (sameas(answer,valfalse)) {
               validAns=TRUE;
               theAnswer=ANSWERB;
          }
          else {
               setError(ERRTF,sqnum.c_str(),NULL,
                m_pQues->getNumber());
          }
          break;
     case USERACCT:
          validAns=TRUE;         // error checking is handled by question class
          theAnswer=answer;
          break;
     case YESNO:
          if (sameas(answer,valyes)) {
               validAns=TRUE;
               theAnswer=ANSWERA;
          }
          else if (sameas(answer,valno)) {
               validAns=TRUE;
               theAnswer=ANSWERB;
          }
          else {
               setError(ERRYN,sqnum.c_str(),NULL,m_pQues->getNumber());
          }
          break;
     default:
          ASSERTM(FALSE,"Invalid Question Type");
          break;
     }
     if (validAns) {
          saveOrTally(theAnswer);
     }
     return(validAns);
}

VOID
takeSynthesis::saveOrTally(        // save or tally answer, as appropriate
string &answer)
{
     if (ISANON) {
          m_pQues->addTally(answer.c_str());
     }
     else {
          m_pQues->saveAnswer(CURUSER,answer.c_str());
     }
}

dnfMap *
takeSynthesis::thisMap()           // use appropriate map
{
     ASSERT(m_pQues != NULL);
     SHORT searchVal=0;

     if ((searchVal=m_pQues->getQType()) == USERACCT) {
          searchVal+=m_pQues->getAccountField();
     }
     else if (searchVal == MULTICHC) {
          if (m_pQues->getNumAnswers() > 1) {
               searchVal+=MCMANS;
          }
          else {
               searchVal+=MC;
          }
     }
     INT it=0;
     while (qTypeArray[it].theMap != NULL) {
          if (qTypeArray[it].qflag == searchVal) {
               return(qTypeArray[it].theMap);
          }
          ++it;
     }
     return(NULL);
}

VOID
takeSynthesis::setTableVbs()
{
     if (m_doingTable && m_pAnsChoice != NULL && m_pValue != NULL) {
          m_pTvb->setTableMCVbs(m_bChosen,m_pAnsChoice,m_pValue);
     }
}

ACTHCODE
takeSynthesis::dynafile()
{
     ACTHCODE retval=ACTHMORE;

     setTableVbs();

     switch(m_dnfPtr->process()) {
     case DNFROWBEGIN:
          m_doingTable=TRUE;
          if (m_rowCount >= MAXASTGS) {
               m_dnfPtr->tableDone();
               m_doingTable=FALSE;
               break;
          }
          else if (strlen(m_pQues->getAnswerString(m_rowCount))
           == 0) {
               m_dnfPtr->sayAgain();
               m_pAnsChoice=NULL;
               m_pValue=NULL;
               ++m_rowCount;
               break;
          }
          m_pAnsChoice=m_pQues->getAnswerString(m_rowCount);
          if (!ISANON) {
               if (m_pQues->getNumAnswers() > 1) {
                    m_bChosen=m_pQues->isMultiAnswerSelected(m_rowCount,
                     ISLOOKUP ? m_lookUser.c_str() : m_usrPtr->userid());
               }
               else {
                    m_bChosen=m_pQues->isAnswerSelected(m_rowCount,
                     ISLOOKUP ? m_lookUser.c_str() : m_usrPtr->userid());
               }
          }
          m_pValue=mcArray[m_rowCount].value;
          ++m_rowCount;
          setTableVbs();
          break;
     case DNFEND:
          retval=ACTHDONE;
          break;
     }
     return(retval);
}
