/***************************************************************************
 *                                                                         *
 *   TVB.CPP                                                               *
 *                                                                         *
 *   Copyright (c) 1996-1997 Galacticomm, Inc.    All rights reserved.     *
 *                                                                         *
 *   Text variables for C++.                                               *
 *                                                                         *
 *                                                - RNStein  8/16/96       *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"
#include <stdcomp.h>               // Avoids linker duplicate symbol warnings
#define RWSTD_NO_BOOL              // related to vector<bool> with galact.cpp
#include <vector>
using namespace std;
#include "tvb.h"

#pragma warn -par                  // supresses warning from memory.h line 208

#define FILREV "$Revision: 7 $"

typedef vector<tvbDefinition *> tvbVector;   // tvbDefinition vector type
typedef tvbVector::iterator tvbVectorIt;     // vector iterator

tvbVector * tvbDefStd=NULL;        // vector of standard tvbDefinitions
tvbVector * tvbDefCust=NULL;       // vector of custom tvbDefinitions

#define CLAIMTESTSTR "\3"          // string for testing standard claim
bool tvbStandardClaim=false;       // global flag for testing standard claim

extern "C"
INT                                /*   > 0 = target > test, etc.          */
tvbComp(                           /* binary search util comparison func   */
const VOID *target,                /*   target object (tvb name)           */
const VOID *array,                 /*   array object (standard def array)  */
ULONG index);                      /*   index of array element to test     */

const CHAR * EXPORT                //   it gives you a value (""=no such name)
textVar(                           // Text Variable conversion
const CHAR *name)                  //   give it a name
{
     tvbReference tvr(name);
     return(tvr.getValue());
}


// tvbTranslate

tvbTranslation::tvbTranslation(    // construct in-stream text var translation
istream& _tvin,                    //   input stream (with text variable names)
ostream& _tvout) :                 //   output stream (with text var values)
     tvin(_tvin),
     tvout(_tvout),
     prefix(TVBPREFIX),
     suffix(TVBSUFFIX),
     islimit(FALSE),
     ngot(0)
{
}

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

VOID
tvbTranslation::setFixes(          // set prefix and suffix for text var names
CHAR _prefix,                      // prefix (e.g. '[')
CHAR _suffix)                      // suffix (e.g. ']')
{
     prefix=_prefix;
     suffix=_suffix;
}

VOID
tvbTranslation::setLimit(          // limit number of bytes to read from tvin
LONG _limit)                       //   max number to read from input stream
{
     limit=_limit;
     islimit=TRUE;
}

VOID
tvbTranslation::proceed()          // proceed with translation
{
     CHAR ch;
     bool needachar=true;
     while (true) {
          if (needachar) {
               if (!nextChar(ch)) {
                    break;
               }
          }
          else {
               needachar=true;          // we're about to take another look
          }
          if (ch == prefix) {
               CHAR tvname[TVBNAMSIZ];
               bool gotachar;
               INT nlen=0;
               while ((gotachar=(bool)nextChar(ch)) != false
                   && istxvc(ch) && nlen < TVBNAMSIZ-1) {
                    tvname[nlen++]=ch;
               }
               tvname[nlen]='\0';
               bool defined=false;
               if (ch == suffix) {
                    tvbReference tvr(tvname);
                    defined=tvr.defined();
                    if (defined) {
                         tvout << tvr.getValue();
                    }
               }
               else {
                    needachar=false;     // take another look at last char
               }
               if (!defined) {
                    tvout.put(prefix);
                    tvout << tvname;
                    if (ch == suffix) {
                         tvout.put(suffix);
                    }
               }
               if (!gotachar) {
                    break;
               }
          }
          else {
               tvout.put(static_cast<CHAR>(ch));
          }
     }
}

GBOOL                              //   TRUE=was more, FALSE=EOF or limit
tvbTranslation::nextChar(          // get next charcter from stream
CHAR &ch)                          //   character got if TRUE
{
     GBOOL retval;
     int chgot;
     if (islimit && ngot >= limit) {
          retval=FALSE;
     }
     else if ((chgot=tvin.get()) == EOF) {
          retval=FALSE;
     }
     else {
          ngot++;
          ch=static_cast<CHAR>(chgot);
          retval=TRUE;
     }
     return(retval);
}


// definition search comparison function

INT                                /*   > 0 = target > test, etc.          */
tvbComp(                           /* binary search util comparison func   */
const VOID *target,                /*   target object (tvb name)           */
const VOID *array,                 /*   array object (standard def array)  */
ULONG index)                       /*   index of array element to test     */
{
     ASSERT(target != NULL);
     ASSERT(::tvbDefStd != NULL);
     ASSERT(array == ::tvbDefStd);
     ASSERT(index < ::tvbDefStd->size());
     (VOID)array;
     return(::stricmp(static_cast<CHAR const *>(target)
                     ,(*::tvbDefStd)[index]->getName()));
}


// tvbDefinition

tvbDefinition::tvbDefinition(      // construct a definition or definer
const CHAR *_name)                 //   text var name, NULL=many,indeterminate
{
     // allocate vectors on first use
     if (::tvbDefStd == NULL) {
          ASSERT(::tvbDefCust == NULL);
          ::tvbDefStd=new tvbVector;
          ::tvbDefCust=new tvbVector;
     }

     if (_name == NULL) {
          name=NULL;
     }
     else {
          name=strcpy(new CHAR[strlen(_name)+1],_name);
     }
}

tvbDefinition::~tvbDefinition()    // destructor
{
     if (name != NULL) {
          delete[] name;
          name=NULL;
     }
}

VOID
tvbDefinition::registerDef()       // register the text variable definition
{                                  //   (often called by derived constructor)
     ASSERT(::tvbDefStd != NULL);
     ASSERT(::tvbDefCust != NULL);
     bool isStandard;
     if (name == NULL) {
          isStandard=false;
     }
     else {
          ::tvbStandardClaim=false;
          claim(CLAIMTESTSTR);
          isStandard=::tvbStandardClaim;
     }
     if (isStandard) {
          INT cmp;
          ULONG i=::binFindNear(&cmp,name,static_cast<VOID *>(::tvbDefStd)
                               ,::tvbDefStd->size(),::tvbComp);
          ::tvbDefStd->insert(::tvbDefStd->begin()+i,this);
          // Note: if cmp == 0, results are undefined
     }
     else {
          ::tvbDefCust->push_back(this);
     }
}

GBOOL                              //   TRUE=this definition can resolve
tvbDefinition::claim(              // does this definition claim this name?
const CHAR *nam)                   //   name of text variable
{
     if (name == NULL) {
          return(FALSE);
     }
     else if (nam[0] == CLAIMTESTSTR[0]) {
          ::tvbStandardClaim=true;
          return(FALSE);
     }
     else {
          return(sameas(name,nam));     // useful case:  one-name text variable
     }
}


// tvbReference

tvbReference::tvbReference(        // construct a reference to a text variable
const CHAR *_name)                 //   name of alleged text variable
{
     name=strcpy(new CHAR[strlen(_name)+1],_name);
     findDef();
}

tvbReference::~tvbReference()      // destructor
{
     if (name != NULL) {
          delete[] name;
          name=NULL;
     }
}

GBOOL
tvbReference::defined()            // is this the name of a defined text var?
{
     return(tvd != NULL);
}

const CHAR *
tvbReference::getName()            // get name reference was contructed with
{
     return(name);
}

const CHAR *                       //   if !defined(), returns "", never NULL
tvbReference::getValue()           // get value of text variable
{
     if (defined()) {
          return(tvd->resolve(name));
     }
     else {
          return("");
     }
}

VOID
tvbReference::findDef()            // associate with a registered tvbDefinition
{
     tvd=NULL;

     // try custom definitions first
     for (tvbVectorIt it=::tvbDefCust->begin() ; it != ::tvbDefCust->end()
        ; it++) {
          if ((*it)->claim(name)) {
               tvd=*it;
               break;
          }
     }

     // if not found, try standard definitions
     if (tvd == NULL) {
          ULONG i=::binSearch(name,static_cast<VOID *>(::tvbDefStd)
                             ,::tvbDefStd->size(),::tvbComp);
          if (i < ::tvbDefStd->size()) {
               tvd=(*::tvbDefStd)[i];
          }
     }
}

