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

#include "gcomm.h"
#include "majorbbs.h"
#include "poll.h"
#include "string.h"
#include "enctvb.h"
#include "ahutil.h"

#define FILREV "$Revision: 20 $"

MARKSOURCE(poll);

poll::poll()                       // default constructor
{
     initPoll();
}

poll::poll(                        // constructor
const CHAR *pollname)              // given poll to objectify
{
     ASSERT(pollname != NULL);
     initPoll();
     dfaSetBlk(pbb);
     if (!dfaAcqEQ(&m_pdata,pollname,0)) {
          stlcpy(m_pdata.pname,pollname,PNAMELEN);
     }
     dfaRstBlk();
     dfaSetBlk(timebb);
     if (dfaAcqEQ(&m_td,pollname,0)) {
          m_updTime=m_td.now;
          m_updDate=m_td.cofdat;
     }
     dfaRstBlk();
}

VOID
poll::initPoll()                   // initialize necessaries for poll
{
     setmem(&m_pdata,sizeof(struct polldata),0);
     m_tallyTotal=0;
     m_tallyAnswers=0;
     m_updTime=0;
     m_updDate=0;
     m_percentVal=NULL;
     m_percentBuf[0]='\0';
     m_pQuesMap=new QUESMAP;
}

poll::~poll()                      // destructor
{
     if (m_pQuesMap != NULL) {
          emptyMap();
          delete m_pQuesMap;
     }
}

bool
poll::create()                     // create the poll
{
     ASSERT(strlen(m_pdata.pname) > 0);
     ASSERT(dfaQueryEQ(m_pdata.pname,0) == 0);

     dfaSetBlk(pbb);
     if (!dfaQueryEQ(m_pdata.pname,0)) {
          if (dfaInsertDup(&m_pdata)) {
               shocst("PNQ NEW POLL ADDED","Poll %s has been added from web",
                    m_pdata.pname);
               dfaRstBlk();
               return(true);
          }
     }
     dfaRstBlk();
     return(false);
}

VOID
poll::tag4delete()
{
     m_pdata.flags|=DELETEP;
}

bool
poll::load()                       // load poll data from disk
{
     ASSERT(strlen(m_pdata.pname) > 0);

     dfaSetBlk(pbb);
     if (dfaAcqEQ(&m_pdata,m_pdata.pname,0)) {
          dfaRstBlk();
          return(true);
     }
     dfaRstBlk();
     return(false);
}

bool
poll::update()                     // update poll data
{
     dfaSetBlk(pbb);
     if (dfaAcqEQ(NULL,m_pdata.pname,0)) {
          dfaUpdate(&m_pdata);
          shocst("PNQ POLL UPDATED","Poll %s has been updated from web",
           m_pdata.pname);
          dfaRstBlk();
          return(true);
     }
     ASSERTM(false,"Failed to update poll - doesn't exist.");
     return(false);
}

SHORT
poll::getAccessAttrib()            // get access attribute (SHOW, DIM etc.)
{
     return(m_pdata.acc_attr);
}

SHORT
poll::getAnswerType()              // get answer type (ACCUMULATEetc.)
{
     return(m_pdata.answer);
}

USHORT
poll::getCloseDate()               // get closing date of poll
{
     return(m_pdata.closedate);
}

USHORT
poll::getCreateDate()              // get creation date of poll
{
     return(m_pdata.createdate);
}

const CHAR *
poll::getDescription()             // get description of poll
{
     return(m_pdata.pdesc);
}

const CHAR *
poll::getGrantClass()              // get class to grant
{
     return(m_pdata.grant_class);
}

LONG
poll::getGrantCredits()            // get credits to grant
{
     return(m_pdata.grant_crd);
}

const CHAR *
poll::getGrantFile()               // get file to grant
{
     return(m_pdata.grant_file);
}

const CHAR *
poll::getGrantFileName()           // get only the file name
{
     CHAR *ptr=strrchr(m_pdata.grant_file,'\\');
     return(ptr == NULL ? m_pdata.grant_file : ptr+1);
}

string
poll::getFileLink()                // get file link for output in .MSG option
{
     if (strlen(m_pdata.grant_file) == 0) {
          return("");
     }

     string fstr="/polls/download/";
     fstr+=urlEncode(m_pdata.pname);
     fstr+="/";
     CHAR *ptr=strrchr(m_pdata.grant_file,'\\');
     if (ptr == NULL) {
          fstr+=m_pdata.grant_file;
     }
     else {
          fstr+=(ptr+1);
     }
     return(fstr);
}

const CHAR *
poll::getGrantKey()                // get key to grant
{
     return(m_pdata.grant_key);
}

const CHAR *
poll::getLookupKey()               // get key required for lookup
{
     return(m_pdata.lookup_key);
}

const CHAR *
poll::getLogonKey()               // get key required for logon info
{
     return(m_pdata.logon_key);
}

const CHAR *
poll::getName()                    // get the name of the poll
{
     return(m_pdata.pname);
}

SHORT
poll::getPriority()                // get the priority (ASK, INSIST etc.)
{
     return(m_pdata.priority);
}

bool
poll::isQuestion(                  // is this a valid question?
SHORT number)                      // given question number
{
     struct quescomp qcomp;
     stlcpy(qcomp.pollname,m_pdata.pname,PNAMELEN);
     qcomp.qnum=number;
     dfaSetBlk(qbb);
     if (dfaQueryEQ(&qcomp,0)) {
          dfaRstBlk();
          return(true);
     }
     dfaRstBlk();
     return(false);
}

bool
poll::firstOpenQues(                // get first "unused" question
SHORT *qnum)
{
     for (SHORT i=0; i < MAXQUES; i++) {
          if (!isQuestion(i)) {
               *qnum=i;
               return(true);
          }
     }
     return(false);
}

bool
poll::isClosed()                   // is poll closed?
{
     return(cofdat(m_pdata.closedate) < cofdat(today()));
}

question *                         // NULL if not found
poll::getQuestion(                 // get question object
SHORT number)                      // given question number
{
     ASSERT(number >= 0 && number < MAXQUES);
     ASSERT(strlen(m_pdata.pname) > 0);
     QUESMAP::iterator it;

     if ((it=m_pQuesMap->find(number)) != m_pQuesMap->end()) {
          if (pollUpdated()) {
               emptyMap();
          }
          else {
               return((*it).second);
          }
     }
     if (isQuestion(number)) {
          question *ques=new question(m_pdata.pname,number);
          pair<SHORT,question *> qspair(number,ques);
          m_pQuesMap->insert(qspair);
          return(ques);
     }
     return(NULL);
}

VOID
poll::emptyMap()                   // clear out map
{
     if (!m_pQuesMap->empty()) {
          m_pQuesMap->erase(m_pQuesMap->begin(),m_pQuesMap->end());
     }
}

const CHAR *
poll::getResultKey()               // get results key
{
     return(m_pdata.res_key);
}

SHORT
poll::getStorageMethod()           // get storage method (BTRIEVE, DISCARD etc.)
{
     return(m_pdata.data);
}

const CHAR *
poll::getTakeKey()                 // get key required to take
{
     return(m_pdata.acc_key);
}

SHORT
poll::getFlags()                   // get flags
{
     return(m_pdata.flags);
}

SHORT
poll::getType()                    // get type (POLL or QUESTIONNAIRE)
{
     return(m_pdata.polltype);
}

bool
poll::getSaveBranch()              // get whether to save branches
{
     return(m_pdata.flags&DISBRANCH);
}

VOID
poll::setAccessAttrib(             // set access attribute
SHORT attrib)                      // the attribute
{
     m_pdata.acc_attr=attrib;
}

VOID
poll::setAnswerType(               // set the answer type
SHORT type)                        // the answer type
{
     ASSERT(type >= ONCE && type <= ACCUMULATE);

     m_pdata.answer=type;
}

VOID
poll::setCloseDate(                // set the closing date
USHORT date)                       // the closing date
{
     m_pdata.closedate=date;
}

VOID
poll::setCreateDate(
USHORT date)
{
     m_pdata.createdate=date;
}

VOID
poll::setDescription(              // set the description
const CHAR *description)           // the description
{
     ASSERT(description != NULL);

     stlcpy(m_pdata.pdesc,description,PDESCLEN);
}

VOID
poll::setGrantClass(               // set class to grant
const CHAR *cls)                   // the class
{
     ASSERT(cls != NULL);

     stlcpy(m_pdata.grant_class,cls,KEYLEN);
}

VOID
poll::setGrantCredits(             // set credits to grant
LONG credits)                      // credits
{
     m_pdata.grant_crd=credits;
}

VOID
poll::setGrantFile(                // set file to grant
const CHAR *filename)              // path and filename
{
     ASSERT(filename != NULL);

     stlcpy(m_pdata.grant_file,filename,GCSTRPTH);
}

VOID
poll::setLookupKey(                // set lookup key
const CHAR *key)                   // the key
{
     ASSERT(key != NULL);

     stlcpy(m_pdata.lookup_key,key,KEYLEN);
}

VOID
poll::setLogonKey(                // set lookup key
const CHAR *key)                  // the key
{
     ASSERT(key != NULL);

     stlcpy(m_pdata.logon_key,key,KEYLEN);
}

VOID
poll::setTakeKey(                  // set take key
const CHAR *key)                   // the key
{
     ASSERT(key != NULL);

     stlcpy(m_pdata.acc_key,key,KEYLEN);
}

VOID
poll::setResultsKey(               // set results key
const CHAR *key)                   // the key
{
     ASSERT(key != NULL);

     stlcpy(m_pdata.res_key,key,KEYLEN);
}

VOID
poll::setGrantKey(                 // set grant key
const CHAR *key)                   // the key
{
     ASSERT(key != NULL);

     stlcpy(m_pdata.grant_key,key,KEYLEN);
}

VOID
poll::setName(                     // set name
const CHAR *pollname)              // the poll name
{
     ASSERT(pollname != NULL);

     stlcpy(m_pdata.pname,pollname,PNAMELEN);
}

VOID
poll::setPriority(                 // set priority
SHORT priority)                    // the priority
{
     ASSERT(priority >= SKIP && priority <= INSIST);

     m_pdata.priority=priority;
}

VOID
poll::setStorageMethod(            // set storage method
SHORT method)                      // the method
{
     ASSERT(method >= BTRIEVE && method <= DISCARD);

     m_pdata.data=method;
}

VOID
poll::setFlags(                    // set flags
SHORT flags)                       // the flags
{
     m_pdata.flags=flags;
}

VOID
poll::setSaveBranch(               // set whether to save branches
bool save)                         // save?
{
     if (save) {
          m_pdata.flags&=~DISBRANCH;
     }
     else {
          m_pdata.flags|=DISBRANCH;
     }
}

VOID
poll::setType(                     // set the type of poll
SHORT type)                        // type
{
     ASSERT(type == POLL || type == QUEST);

     m_pdata.polltype=type;
}

bool
poll::isComplete(                  // has poll been completed?
const CHAR *userid)                // userid to check
{
     ASSERT(userid != NULL);
     ASSERT(strlen(m_pdata.pname) > 0);

     stlcpy(m_scomp.uid,userid,UIDSIZ);
     stlcpy(m_scomp.pname,m_pdata.pname,PNAMELEN);
     dfaSetBlk(sbb);
     if (dfaQueryEQ(&m_scomp,0)) {
          dfaRstBlk();
          return(true);
     }
     dfaRstBlk();
     return(false);
}

VOID
poll::finishedPoll(                // write to database - user completed poll
const CHAR *userid)
{
     pollstat stat;

     dfaSetBlk(sbb);
     stlcpy(stat.uid,userid,UIDSIZ);
     stlcpy(stat.pname,m_pdata.pname,PNAMELEN);
     stlcpy(m_scomp.uid,userid,UIDSIZ);
     stlcpy(m_scomp.pname,m_pdata.pname,PNAMELEN);
     if (!dfaQueryEQ(&m_scomp,0)) {
          dfaInsert(&stat);
     }
     else {
          dfaAbsRec(NULL,0);
          if (m_pdata.data != DISCARD) {
               dfaUpdate(&stat);
          }
     }
     dfaRstBlk();
}

SHORT
poll::getTalliedAnswers(          // get tallied answers for question
SHORT qnumber,                    // the question
SHORT answer)                     // the answer
{
     ASSERT(isQuestion(qnumber) == true);

     m_tallyAnswers=0;

     dfaSetBlk(tbb);
     if (dfaAcqEQ(&m_tally,m_pdata.pname,0)) {
          m_tallyAnswers=m_tally.answer[qnumber][answer];
     }
     dfaRstBlk();
     return(m_tallyAnswers);
}

const CHAR *
poll::getTalliedPercent(          // get tallied percentage for question
SHORT qnumber,                    // the question
SHORT answer)                     // the answer
{
     ASSERT(isQuestion(qnumber) == true);

     m_percentVal=NULL;

     dfaSetBlk(tbb);
     if (dfaAcqEQ(&m_tally,m_pdata.pname,0)) {
          m_percentVal=getPercentage(m_tally.answer[qnumber][answer],
           getTalliedTotal(qnumber));
     }
     dfaRstBlk();
     return(m_percentVal);
}

bool
poll::prevTallied(                 // is there another tallied question prev?
SHORT start,                       // where to start looking
SHORT *qtally)                     // where to put value
{
     m_qnum=start;

     while (m_qnum >= 0 && isQuestion(m_qnum)) {
          if (isTallied(m_qnum)) {
               *qtally=m_qnum;
               return(true);
          }
          --m_qnum;
     }
     return(false);
}

bool
poll::nextTallied(                 // is there another tallied question?
SHORT start,                       // where to start looking
SHORT *qtally)                     // where to put value
{
     m_qnum=start;

     while (isQuestion(m_qnum)) {
          if (isTallied(m_qnum)) {
               *qtally=m_qnum;
               return(true);
          }
          ++m_qnum;
     }
     return(false);
}

bool
poll::allAnswered(                 // determine if all questions have been answered
const CHAR *userid)
{
     bool rc=true;

     for (SHORT it=0 ; it < MAXQUES ; it++) {
          if (isQuestion(it)) {
               question *q=getQuestion(it);
               if (q->getAnswer(userid) == NULL && q->getQType() != DISONLY
                && !(q->getFlags()&EXMPT)) {
                    rc=false;
               }
               else if (q->getFlags()&BRANCH) {
                    it=q->getSelectedBranch(userid);
                    if (it == -1) { // shouldn't occur
                         rc=false;
                         break;
                    }
               }
          }
     }
     return(rc);
}

bool
poll::isTallied(                    // is passed questoin number tallied?
SHORT qnumber)
{
     ASSERT(isQuestion(qnumber) == true);

     question *q=getQuestion(qnumber);
     if (q != NULL) {
          if (q->getQType() == MULTICHC || q->getQType() == TRUEFALSE
           || q->getQType() == YESNO) {
               return(true);
          }
     }
     return(false);
}

bool
poll::prevLookup(                  // is there another previous lookup question?
const CHAR *userid,                // userid to lookup
SHORT start,                       // where to start looking
SHORT *qlook)                      // where to put value
{
     m_qnum=start;

     while (m_qnum >= 0 && isQuestion(m_qnum)) {
          if (canLookup(userid,m_qnum)) {
               *qlook=m_qnum;
               return(true);
          }
          --m_qnum;
     }
     return(false);
}

bool
poll::nextLookup(                  // is there another question to lookup
const CHAR *userid,                // user to lookup
SHORT start,                       // where to start looking
SHORT *qlook)                      // where to put value
{
     m_qnum=start;

     while (isQuestion(m_qnum)) {
          if (canLookup(userid,m_qnum)) {
               *qlook=m_qnum;
               return(true);
          }
          ++m_qnum;
     }
     return(false);
}

bool
poll::canLookup(                    // is passed question number ok to lookup?
const CHAR *userid,                 // user to lookup
SHORT qnumber)
{
     ASSERT(isQuestion(qnumber) == true);
     ASSERT(getStorageMethod() != DISCARD && getStorageMethod() != ASCII);

     question *q=getQuestion(qnumber);
     if (q == NULL || q->getFlags()&EXMPT || q->getQType() == DISONLY
      || q->getAnswer(userid) == NULL) {
          return(false);
     }
     return(true);
}

SHORT
poll::getTalliedTotal(            // get total answers for question
SHORT qnumber)                    // the question
{
     ASSERT(isQuestion(qnumber) == true);

     question *q=getQuestion(qnumber);

     ASSERT(q != NULL);

     m_tallyTotal=0;
     dfaSetBlk(tbb);
     if (dfaAcqEQ(&m_tally,m_pdata.pname,0)) {
          for (INT astg=0; astg < MAXASTGS; astg++) {
               if (strlen(q->getAnswerString(astg)) > 0) {
                    m_tallyTotal+=m_tally.answer[qnumber][astg];
               }
          }
     }
     dfaRstBlk();
     return(m_tallyTotal);
}

const struct polldata
poll::getPollData()               // get poll data struct
{
     return(m_pdata);
}

VOID
poll::setPollData(                // set poll data - with struct
struct polldata pd)                  // passed struct
{
     movmem(&pd,&m_pdata,sizeof(struct polldata));
}

bool
poll::pollUpdated()               // has poll been updated?
{
     GBOOL updated=false;

     dfaSetBlk(timebb);
     if (dfaAcqEQ(&m_td,m_pdata.pname,0)) {
          if (m_updTime != m_td.now || m_updDate != m_td.cofdat) {
               m_updTime=m_td.now;
               m_updDate=m_td.cofdat;
               updated=true;
          }
     }
     dfaRstBlk();
     return(updated);
}

CHAR *
poll::getPercentage(              // calculate percentage
INT num,                          // number of times this answers selected
INT tot)                          // total answers
{
     LONG comp;

     if (tot == 0) {
          comp=0L;
     }
     else {
          comp=(LONG)num*1000L/(LONG)tot;
          comp-=comp/10L*10L;
     }
     if (tot != 0) {
          setmem(&m_percentBuf,PERCSIZ,0);
          sprintf(m_percentBuf,"%3ld.%1.1ld",num*100L/tot,comp);
          return(m_percentBuf);
     }
     return("  0.0");
}

SHORT
poll::getStatus(                   // Get poll status
const CHAR *userid)                // user in question
{
     if (m_pdata.flags&(EDITP|ADDP)) {
          return(STAT_MAINT);
     }
     else if (m_pdata.flags&DELETEP) {
          return(STAT_DEL);
     }
     else if (userid != NULL && isComplete(userid)) {
          return(STAT_DONE);
     }
     return(STAT_NEW);
}

VOID
poll::grantKey(                    // grant a key
const CHAR *userid)                // user
{
     ASSERT(userid != NULL);

     if (strlen(m_pdata.grant_key) > 0) {
          if (!uhskey(userid,m_pdata.grant_key)) {
               givkey(userid,m_pdata.grant_key);
          }
     }
}

VOID
poll::grantClass(                  // grant a class
const CHAR *userid)                // userid
{
     ASSERT(userid != NULL);

     if (strlen(m_pdata.grant_class) > 0) {
          struct usracc *uptr;
          if (fndcls(m_pdata.grant_class) != NULL
           && (uptr=GetAccPtr(userid)) != NULL
           && !sameas(uptr->curcls,m_pdata.grant_class)) {
               swtcls(uptr,1,m_pdata.grant_class,3,0);
          }
     }
}

VOID
poll::grantCreds(                  // grant credits
const CHAR *userid)                // user
{
     ASSERT(userid != NULL);

     if (m_pdata.grant_crd > 0L) {
          addcrd((CHAR *)userid,l2as(m_pdata.grant_crd),0);
     }
     else if (m_pdata.grant_crd < 0L) {
          LONG creds=m_pdata.grant_crd*-1L;
          gdedcrd(userid,creds,0,1);
     }
}
