/***************************************************************************
 *                                                                         *
 *   QUESTION.CPP                                                          *
 *                                                                         *
 *   Copyright (c) 1997 Galacticomm, Inc.                                  *
 *                                                                         *
 *                                                                         *
 *   question Class.                                                       *
 *                                                  - N.C. Osterc  9/08/98 *
 *                                                                         *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"
#include "poll.h"
#include "question.h"
#include "galpnq.h"
#include "ahutil.h"
#include "majorbbs.h"

#define FILREV "$Revision: 18 $"

#define BASEA 65
#define ANSWER1    0
#define MINNADSIZ  5               // minumum address and name size
#define MINPHOSIZ  7               // minimum size of phone number
#define MINPSWSIZ  3               // minimum size of password

MARKSOURCE(question);

question::question()
{
     initQues();
}

question::question(                // constructor
const CHAR *pollname)              // name of poll question is in
{
     initQues();
     stlcpy(m_qdata.pollname,pollname,PNAMELEN);
}

question::question(                // constructor
const CHAR *pollname,              // the name of the poll
SHORT qnumber)                     // the question number
{
     initQues();
     dfaSetBlk(qbb);
     setupQCompKey(pollname,qnumber);
     dfaAcqEQ(&m_qdata,&m_qcomp,0);
     dfaRstBlk();
}

question::~question()              // destructor
{
}

VOID
question::initQues()
{
     setmem(&m_qdata,sizeof(struct quesdata),0);
}

VOID
question::setupQCompKey(
const CHAR *pollname,
SHORT num)
{
     stlcpy(m_qcomp.pollname,pollname,PNAMELEN);
     m_qcomp.qnum=num;
}

bool
question::create()                 // create the question
{
     bool created=false;
     dfaSetBlk(qbb);
     setupQCompKey(m_qdata.pollname,m_qdata.qnum);
     if (!dfaAcqEQ(NULL,&m_qcomp,0)) {
          dfaInsertV(&m_qdata,fldoff(quesdata,txtmsg)+
           strlen(m_qdata.txtmsg)+1);
          created=true;
     }
     dfaRstBlk();
     return(created);
}

bool
question::load()                   // load question data from disk
{
     bool loaded=false;

     dfaSetBlk(qbb);
     setupQCompKey(m_qdata.pollname,m_qdata.qnum);
     if (dfaAcqEQ(&m_qdata,&m_qcomp,0)) {
          loaded=true;
     }
     dfaRstBlk();
     return(loaded);
}

bool
question::remove()
{
     bool deleted=false;

     dfaSetBlk(qbb);
     setupQCompKey(m_qdata.pollname,m_qdata.qnum);
     if (dfaAcqEQ(NULL,&m_qcomp,0)) {
          dfaDelete();
          deleted=true;
     }
     dfaRstBlk();
     return(deleted);
}

bool
question::update()                 // update question data to disk
{
     bool updated=false;

     dfaSetBlk(qbb);
     setupQCompKey(m_qdata.pollname,m_qdata.qnum);
     if (dfaAcqEQ(NULL,&m_qcomp,0)) {
          dfaUpdateV(&m_qdata,fldoff(quesdata,txtmsg)+
           strlen(m_qdata.txtmsg)+1);
          updated=true;
     }
     dfaRstBlk();
     return(updated);
}

SHORT
question::getAccountField()        // get the accoutning field
{
     return(m_qdata.field);
}

const CHAR *
question::getAnswerString(         // get answer string
SHORT idx)                         // for this index in array
{
     ASSERT(idx >= ANSWER1 && idx < MAXASTGS);

     return(m_qdata.ansstg[idx]);
}

SHORT
question::getBranch(               // get branch
SHORT idx)                         // for this index in array
{
     ASSERT(idx >= ANSWER1 && idx < MAXASTGS);

     return(m_qdata.branch[idx]);
}

SHORT
question::getFlags()               // get flags
{
     return(m_qdata.flags);
}

LONG
question::getMaxLen()              // get maximum length of answer
{
     return(m_qdata.max);
}

LONG
question::getMinLen()              // get minimum length of answer
{
     return(m_qdata.min);
}

SHORT
question::getNext()                // get next question number
{
     return(m_qdata.next);
}

SHORT
question::getNumAnswers()          // number of answers that can be submitted
{
     return(m_qdata.ansparam);
}

SHORT
question::getNumber()              // get the question number
{
     return(m_qdata.qnum);
}

const CHAR *
question::getPollName()            // get the poll name
{
     return(m_qdata.pollname);
}

SHORT
question::getPrevious()            // get previous question number
{
     return(m_qdata.prev);
}

SHORT
question::getQType()               // get the type of question
{
     return(m_qdata.qtype);
}

const CHAR *
question::getTextMsg()             // get question text
{
     return(m_qdata.txtmsg);
}

VOID
question::setAccountField(         // set accounting field
SHORT accfield)                    // the accounting field
{
     m_qdata.field=accfield;
}

VOID
question::setAnswerString(         // set answer string
SHORT idx,                         // the index
const CHAR *answerString)          // answer string
{
     ASSERT(idx >= ANSWER1 && idx < MAXASTGS);

     stlcpy(m_qdata.ansstg[idx],answerString,ANSSTGLEN);
}

VOID
question::setBranch(               // is this poll closed?
SHORT idx,                         // index
SHORT branch)                      // question to branch to
{
     m_qdata.branch[idx]=branch;
}

VOID
question::setFlags(                // set flags
SHORT flag)                        // variable containing flag values
{
     m_qdata.flags=flag;
}

VOID
question::setMaxLen(               // set max length
LONG max)                          // length
{
     m_qdata.max=max;
}

VOID
question::setMinLen(               // set minimum length
LONG min)                          // minimum length
{
     m_qdata.min=min;
}

VOID
question::setNext(                 // set next question number
SHORT next)                        // next
{
     m_qdata.next=next;
}

VOID
question::setNumAnswers(           // number of answers per question
SHORT numAnswers)                  // the number
{
     m_qdata.ansparam=numAnswers;
}

VOID
question::setNumber(               // set this question number
SHORT number)                      // this question number
{
     m_qdata.qnum=number;
}

VOID
question::setPoll(                 // set pollname for which this question resides
const CHAR *pollname)              // name of the poll
{
     stlcpy(m_qdata.pollname,pollname,PNAMELEN);
}

VOID
question::setPrevious(             // set the previous question number
SHORT prev)                        // previous question
{
     m_qdata.prev=prev;
}

VOID
question::setQType(                // Set the questin type
SHORT type)                        // the type
{
     m_qdata.qtype=type;
}

VOID
question::setTextMsg(              // set text message
const CHAR *text)                  // the text
{
    stlcpy(m_qdata.txtmsg,text,TXTSIZ);
}

const CHAR *
question::getAnswer(               // Get a user's answer for this question
const CHAR *userid)                // the user-ID
{
     ASSERT(userid != NULL);

     stlcpy(m_acomp.pollname,m_qdata.pollname,PNAMELEN);
     stlcpy(m_acomp.uid,userid,UIDSIZ);
     m_acomp.qnum=m_qdata.qnum;
     dfaSetBlk(abb);
     if (dfaAcqEQ(&m_adata,&m_acomp,0)) {
          dfaRstBlk();
          return(m_adata.answer);
     }
     dfaRstBlk();
     return(NULL);
}

bool
question::isValMultiAnswer(         // determine if this is a valid muiltianswer
SHORT idx)                          // the answer number
{
     return (idx >= ANSWER1 && idx < MAXASTGS);
}

bool
question::isAnswerSelected(         // determine if single-answer selected
SHORT idx,
const CHAR *userid)
{
     ASSERT(idx >= ANSWER1 && idx < MAXASTGS);
     ASSERT(userid != NULL);
     ASSERT(m_qdata.qtype == MULTICHC);

     if (m_qdata.qtype != MULTICHC) {
          return(false);
     }

     const CHAR *answer=getAnswer(userid);

     if (answer != NULL && answer[0] == (BASEA+idx)) {
          return(true);
     }
     return(false);
}

SHORT
question::getSelectedBranch(          // get branch for single-answer m-choice
const CHAR *userid)
{
     ASSERT(m_qdata.qtype == MULTICHC);
     ASSERT(m_qdata.flags&BRANCH);
     ASSERT(getNumAnswers() == 1);

     const CHAR *ansr=getAnswer(userid);

     if (ansr != NULL) {
          for (INT i=ANSWER1; i < MAXASTGS; i++) {
               if (ansr[i] != '\0') {
                    return(getBranch(i));
               }
          }
     }
     return(-1);
}

bool
question::isMultiAnswerSelected(    // Determine if answer is selected.
SHORT idx,                          // the answer number (0-15)
const CHAR *userid)
{
     ASSERT(idx >= ANSWER1 && idx < MAXASTGS);
     ASSERT(userid != NULL);
     ASSERT(m_qdata.qtype == MULTICHC);

     if (m_qdata.qtype != MULTICHC) {
          return(false);
     }

     const CHAR *answer=getAnswer(userid);

     if (answer != NULL && answer[idx] == (BASEA+idx)) {
          return(true);
     }
     return(false);
}

bool
question::isBooleanSelected(       // Check a YESNO or TRUEFALSE question for selection
bool idx,                          // 1 ==  YES, 0 == NO
const CHAR *userid)                // current userid
{
     ASSERT(userid != NULL);
     ASSERT(m_qdata.qtype == YESNO || m_qdata.qtype == TRUEFALSE);

     if (m_qdata.qtype != YESNO && m_qdata.qtype != TRUEFALSE) {
          return(false);
     }
     const CHAR *answer=getAnswer(userid);

     if (answer != NULL) {
          if (answer[0] == '\0') {
               return(false);
          }
          else if (answer[0] == 'A' && idx == 1) {
               return(true);
          }
          else if (answer[0] == 'B' && idx == 0) {
               return(true);
          }
     }
     return(false);
}

VOID
question::setQuestionData(         // set all question data
struct quesdata qd)                // via passed struct
{
     movmem(&qd,&m_qdata,sizeof(struct quesdata));

}

const struct quesdata
question::getQuestionData()        // get question data structure
{
     return(m_qdata);
}

bool
question::saveAnswer(              // save answer
const CHAR *userid,
const CHAR *answerString)
{
     ASSERT(userid != NULL);
     ASSERT(answerString != NULL);

     poll *pobj=new poll(m_qdata.pollname);
     SHORT method, atype;
     dfaSetBlk(abb);
     stlcpy(m_acomp.pollname,m_qdata.pollname,PNAMELEN);
     stlcpy(m_acomp.uid,userid,UIDSIZ);
     m_acomp.qnum=m_qdata.qnum;
     if (!dfaAcqEQ(&m_adata,&m_acomp,0)
      || (atype=pobj->getAnswerType()) == ACCUMULATE) {
          if ((method=pobj->getStorageMethod()) != DISCARD
           && method != ASCII && !(m_qdata.flags&EXMPT)) {
               insertAnswer(userid,answerString);
          }
     }
     else {
          if (atype == REPLACE) {
               removeTally();
          }
          if (method != DISCARD && method != ASCII && !(m_qdata.flags&EXMPT)) {
               updateAnswer(userid,answerString);
          }
     }
     dfaRstBlk();
     if (m_qdata.qtype == USERACCT) {
          updateAccount(userid,m_qdata.field,answerString);
     }
     if (method == ASCII || method == BOTH) {
          writeASCII(userid,answerString);
     }
     if (pobj->getType() != QUEST && OK2TALLY(m_qdata.qtype)) {
          addTally(answerString);
     }
     delete pobj;
     return(true);
}

VOID
question::updateAnswer(            // update answer in databsae
const CHAR *userid,
const CHAR *answerString)
{
     ASSERT(userid != NULL);
     ASSERT(answerString != NULL);

     m_adata.qnum=m_qdata.qnum;
     stzcpy(m_adata.pollname,m_qdata.pollname,PNAMELEN);
     stzcpy(m_adata.uid,userid,UIDSIZ);
     stzcpy(m_adata.answer,answerString,ANSSIZ);
     dfaSetBlk(abb);
     dfaUpdateV(&m_adata,fldoff(ansrdata,answer)+
     strlen(m_adata.answer)+1);
     dfaRstBlk();
}

VOID
question::insertAnswer(            // insert answer into database
const CHAR *userid,
const CHAR *answerString)
{
     ASSERT(userid != NULL);
     ASSERT(answerString != NULL);

     m_adata.qnum=m_qdata.qnum;
     stzcpy(m_adata.pollname,m_qdata.pollname,PNAMELEN);
     stzcpy(m_adata.uid,userid,UIDSIZ);
     stzcpy(m_adata.answer,answerString,ANSSIZ);
     dfaSetBlk(abb);
     dfaInsertV(&m_adata,fldoff(ansrdata,answer)+
     strlen(m_adata.answer)+1);
     dfaRstBlk();
}

VOID
question::removeTally()            // remove answer from tally
{
     INT i=0;

     if (!OK2TALLY(m_qdata.qtype)) {
          return;
     }
     dfaSetBlk(tbb);
     if (!dfaAcqEQ(&m_tally,m_qdata.pollname,0)) {
          memset(&m_tally,0,sizeof(struct polltally));
          stzcpy(m_tally.pollname,m_qdata.pollname,PNAMELEN);
          dfaInsertV(&m_tally,sizeof(struct polltally));
          dfaRstBlk();
          return;
     }
     do {
          if (isalpha(m_adata.answer[i])) {
               m_tally.answer[m_qdata.qnum][m_adata.answer[i]-65]--;
          }
     } while (m_adata.answer[i++] != '\0');
     dfaAbsRec(NULL,0);
     dfaUpdateV(&m_tally,sizeof(struct polltally));
     dfaRstBlk();
}

VOID
question::addTally(                // add answer to tally
const CHAR *answerString)
{
     ASSERT(answerString != NULL);

     INT i=0;

     dfaSetBlk(tbb);
     if (!dfaAcqEQ(&m_tally,m_qdata.pollname,0)) {
          memset(&m_tally,0,sizeof(struct polltally));
          stzcpy(m_tally.pollname,m_qdata.pollname,PNAMELEN);
          dfaInsertV(&m_tally,sizeof(struct polltally));
          dfaAbsRec(NULL,0);
     }
     do {
          if (isalpha(answerString[i])) {
               m_tally.answer[m_qdata.qnum][answerString[i]-65]++;
          }
     } while (answerString[i++] != '\0');
     dfaUpdateV(&m_tally,sizeof(struct polltally));
     dfaRstBlk();
}

VOID
question::updateAccount(           // update user account field as appropriate
const CHAR *userid,
INT accountField,
const CHAR *answerString)
{
     ASSERT(userid != NULL);
     ASSERT(accountField >= ACCFLD_NAME0 && accountField <= ACCFLD_PASSWORD);
     ASSERT(answerString != NULL);

     struct usracc *uptr=GetAccPtr(userid);

     switch (accountField) {
     case ACCFLD_NAME0:
     case ACCFLD_NAME1:
          if (checkMinMaxPfnAdr(userid,answerString)) {
               stzcpy(uptr->usrnam,answerString,NADSIZ);
          }
          break;
     case ACCFLD_ADR_LINE_1:
          if (checkMaxPfnAdr(userid,answerString)) {
               stzcpy(uptr->usrad1,answerString,NADSIZ);
          }
          break;
     case ACCFLD_ADR_LINE_2:
          if (checkMinMaxPfnAdr(userid,answerString)) {
               stzcpy(uptr->usrad2,answerString,NADSIZ);
          }
          break;
     case ACCFLD_ADR_LINE_3:
          if (checkMinMaxPfnAdr(userid,answerString)) {
               stzcpy(uptr->usrad3,answerString,NADSIZ);
          }
          break;
     case ACCFLD_ADR_LINE_4:
          if (checkMaxPfnAdr(userid,answerString)) {
               stzcpy(uptr->usrad4,answerString,NADSIZ);
          }
          break;
     case ACCFLD_PHONE:
          if (strlen(answerString) > MINPHOSIZ) {
               stzcpy(uptr->usrpho,answerString,PHOSIZ);
          }
          break;
     case ACCFLD_BDAY:
          if (okbday((CHAR *)answerString)) {
               stzcpy(uptr->birthd,answerString,DATSIZ);
          }
          break;
     case ACCFLD_SEX:
          if (sameas(answerString,"m") || sameas(answerString,"f")) {
               uptr->sex=toupper(answerString[0]);
          }
          break;
     case ACCFLD_ANSI:
          if (sameto(answerString,"yes") || sameto(answerString,"on")) {
              uptr->ansifl|=ANSON;
          }
          else if (sameto(answerString,"no")
                || sameto(answerString,"off")) {
              uptr->ansifl&=~ANSON;
              uptr->ansifl|=ANSMAN;
          }
          else if (sameto(answerString,"auto")) {
              uptr->ansifl=0;
          }
          break;
     case ACCFLD_EDITOR:
          if (sameto(answerString,"yes")
          || sameto(answerString,"fse")) {
              uptr->usrprf&=~PRFLIN;
          }
          else if (sameto(answerString,"no")
                || sameto(answerString,"line")) {
              uptr->usrprf|=PRFLIN;
          }
          break;
     case ACCFLD_PASSWORD:
          if (checkMinMaxPw(answerString)) {
               stzcpy(uptr->psword,answerString,PSWSIZ);
          }
          break;
     }

     if (!onsysn(userid,1)) {
          dfaSetBlk(accbb);
          if (dfaAcqEQ(NULL,userid,0)) {
               dfaUpdate(uptr);
          }
          dfaRstBlk();
     }
}

bool
question::checkMinMaxPw(           // check min/max size on pw answer
const CHAR *answer)
{
     if (strlen(answer) >= PSWSIZ) {
          return(false);
     }
     else if (strlen(answer) < MINPSWSIZ) {
          return(false);
     }
     return(true);
}

bool
question::checkMaxPfnAdr(          // check max and profan on answer
const CHAR *userid,
const CHAR *answer)
{
     if (strlen(answer) >= NADSIZ) {
          return(false);
     }
     else if (!uhskey(userid,syskey)
      && (pfnlvl=profan((CHAR *)answer)) != 0) {
          if (pfnlvl > pfceil) {
               return(false);
          }
     }
     return(true);
}

bool
question::checkMinMaxPfnAdr(       // check min/max sizes and profanity on answer
const CHAR *userid,
const CHAR *answer)
{
     if (strlen(answer) >= NADSIZ) {
          return(false);
     }
     else if (strlen(answer) < MINNADSIZ) {
          return(false);
     }
     else if (!uhskey(userid,syskey)
      && (pfnlvl=profan((CHAR *)answer)) != 0) {
          if (pfnlvl > pfceil) {
               return(false);
          }
     }
     return(true);
}

VOID
question::writeASCII(              // write answer to ASCII file
const CHAR *userid,
const CHAR *answerString)
{
     ASSERT(userid != NULL);
     ASSERT(answerString != NULL);

     CHAR tstr[FILESIZ];
     FILE *fp;

     setmbk(polmb);
     stzcpy(tstr,rawmsg(ASCIIFIL),FILESIZ);
     if ((fp=fopen(tstr,FOPAA)) != NULL) {
          fprintf(fp,"\n%s %s %s: %s #%d\nAnswer: %s\n",
                  ncdatel(today()),nctime(now()),
                  userid,m_qdata.pollname,m_qdata.qnum+1,
                  *answerString == '\r' ? answerString+1 : answerString);
          fclose(fp);
     }
     else {
          shocst("PNQ FILE OPEN ERROR!","Error opening file %s for append!",
                 tstr);
     }
     rstmbk();
}

VOID
question::setMinMaxDefaults()
{
     switch (m_qdata.qtype) {
     case 0:
     case CREDIT:                       /* credit card question type       */
          m_qdata.max=(LONG)MAXLENCC;   /* allow dashes or spaces          */
          m_qdata.min=(LONG)MINLENCC;   /* smallest valid credit card #    */
          break;
     case DATE:                         /* date question type */
          m_qdata.max=10L;              /* allow for date mm/dd/yyyy format*/
          m_qdata.min=8L;               /* allow for date mm/dd/yy format  */
          break;
     case DISONLY:                      /* display only question type      */
          m_qdata.max=m_qdata.min=0L;   /* no input expected          */
          break;
     case MULTICHC:                     /* multiple choice                 */
          m_qdata.max=1L;               /* Alpha letter only               */
          m_qdata.min=1L;               /* Must be minimum of 1 letter     */
          break;
     case NUMERIC:                      /* numeric question type           */
          m_qdata.max=99999L;
          m_qdata.min=0L;
          break;
     case OPEN_ENDED:                   /* open ended response ques. type  */
          m_qdata.max=1999L;
          m_qdata.min=0L;
          break;
     case TRUEFALSE:                    /* true / false                    */
          m_qdata.min=4L;
          m_qdata.max=5L;
          setmem(m_qdata.ansstg,ANSSTGLEN*MAXASTGS,0);
          setmem(m_qdata.branch,2*MAXASTGS,0);
          stzcpy(m_qdata.ansstg[0],truefalse[1],ANSSTGLEN);
          stzcpy(m_qdata.ansstg[1],truefalse[2],ANSSTGLEN);
          break;
     case YESNO:                        /* yes / no                        */
          m_qdata.min=2L;
          m_qdata.max=3L;
          setmem(m_qdata.ansstg[0],ANSSTGLEN*MAXASTGS,0);
          setmem(m_qdata.branch,2*MAXASTGS,0);
          stzcpy(m_qdata.ansstg[0],yesno[1],ANSSTGLEN);
          stzcpy(m_qdata.ansstg[1],yesno[2],ANSSTGLEN);
          break;
     case USERACCT:                     /* user account question type      */
          m_qdata.max=60L;
          m_qdata.min=0L;
     default:
          ASSERTM(false,"Invalid Question Type on MinMaxDefault");
     }
}


