/***************************************************************************
 *                                                                         *
 *   DNFMGR.CPP                                                            *
 *                                                                         *
 *   Copyright (c) 1998 Galacticomm, Inc.         All Rights Reserved.     *
 *                                                                         *
 *   Custom DynaFile template manager.                                     *
 *                                                                         *
 *                                           - J. Alvrus    06/24/1998     *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"
#include "tvb.h"
#include "dnfmgr.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;

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

#define FILREV "$Revision: 5 $"

typedef vector<dnfMap *> MapVector;// dnfMap vector type
MapVector MapArray;                // vector of saved maps

class tvbDnfTemplate : public tvbDefinition { // DNF_TEMPLATE text variable
public:
     tvbDnfTemplate();

     VOID
     set(                          // set text variable
     dnfHandler const * dnf);      //   based on this handler

     VOID
     clr();                        // clear text variable

     CHAR const *
     resolve(                      // override base class resolve
     CHAR const *);

private:
     CHAR m_val[GCMAXPTH];         // what this text variable will return
} tvbDnf;

// local function declarations

static
bool                               //   returns true if URL ends in "/"
urlIsDir(                          // does URL refer to a directory?
CHAR const * url);                 //   full URL (with or w/out host)

static __cdecl
INT                                //   > 0 = target > test, etc.
mapComp(                           // map comparison function for lookups
VOID const * target,               //   target object
VOID const * array,                //   array object
ULONG index);                      //   index of array element to test

static
dnfMap *                           //   returns pointer to new map
dnfCopyMap(                        // copy a DNF map
dnfMap const * pMapSrc,            //   source to copy
CHAR const * filePath);            //   file associated with new map

static
dnfStep *                          //   returns pointer to new array
dnfCopySteps(                      // create a copy of a map's step array
dnfMap const * pMapSrc);           //   map from which to copy

// function definitions

dnfHandler *                       //   returns allocated handler
dnfCreateHandlerURL(               // create a new DNF handler from URL
acthSession * pSes,                //   for this session
dnfMap * pMapBase,                 //   map on which to base handler
CHAR const * BaseDir)              //   base template directory
{
     // get template to use
     CHAR tempFile[GCMAXPTH];
     if (urlIsDir(pSes->url())) {
          stlcpy(tempFile,pMapBase->getFile(),GCMAXPTH);
     }
     else {
          dnfTemplateFromURL(BaseDir,pSes->urlsfx(),tempFile,GCMAXPTH);
     }

     // create handler
     return(dnfCreateHandler(pSes->boutp,pMapBase,tempFile));
}

dnfHandler *                       //   returns allocated handler
dnfCreateHandler(                  // create a new DNF handler for a file
ostream * pbout,                   //   stream to the browser
dnfMap * pMapBase,                 //   map on which to base handler
CHAR const * tempFile)             //   DynaFile template to handle
{
     // get map for template
     dnfMap * pMap=dnfGetMap(pMapBase,tempFile);

     // create handler
     return(new dnfHandler(*pMap,*pbout));
}

dnfMap *                           //   returns map for template
dnfGetMap(                         // get a DNF map for a template
dnfMap * pMapBase,                 //   base map used for template
CHAR const * tempFile)             //   DynaFile template to handle
{
     // see if template refers to the base map's template
     CHAR FullPathBase[GCMAXPTH];  // full path of template in base map
     normspec(FullPathBase,pMapBase->getFile());
     CHAR FullPathTemp[GCMAXPTH];  // full path of template to handle
     normspec(FullPathTemp,tempFile);
     if (sameas(FullPathBase,FullPathTemp)) {
          return(pMapBase);
     }

     // find and return any already-saved map
     INT cmp;
     ULONG i=binFindNear(&cmp,FullPathTemp,static_cast<VOID *>(&::MapArray)
                        ,::MapArray.size(),mapComp);
     if (cmp == 0) {
          return(::MapArray[i]);
     }

     // create new map for FullPathTemp
     dnfMap * pMap=dnfCopyMap(pMapBase,FullPathTemp);

     // add new map to array
     ::MapArray.insert(::MapArray.begin()+i,pMap);
     return(pMap);
}

CHAR *                             //   returns destination buffer
dnfTemplateFromURL(                // create template path from URL
CHAR const * BaseDir,              //   base template directory
CHAR const * url,                  //   URL w/relative path & file name
CHAR * buf,                        //   buffer to receive complete path
size_t bufSiz)                     //   size of buffer
{
     strrpl(stlcpy(buf,BaseDir,bufSiz),'/',SL);
     return(strrpl(makePath(buf,NULL,url,bufSiz),'/',SL));
}

static
bool                               //   returns true if URL ends in "/"
urlIsDir(                          // does URL refer to a directory?
CHAR const * url)                  //   full URL (with or w/out host)
{
     // handle parameters (if any)
     CHAR const * urlEnd=strchr(url,'?');
     if (urlEnd == NULL) {
          urlEnd=url+strlen(url);
     }

     // return whether URL ends in "/"
     return(urlEnd > url && urlEnd[-1] == '/');
}

static __cdecl
INT                                //   > 0 = target > test, etc.
mapComp(                           // map comparison function for lookups
VOID const * target,               //   target object
VOID const * array,                //   array object
ULONG index)                       //   index of array element to test
{
     ASSERT(target != NULL);
     ASSERT(array == &::MapArray);
     ASSERT(index < ::MapArray.size());
     (VOID)array;
     return(stricmp(static_cast<CHAR const *>(target)
                   ,::MapArray[index]->getFile()));
}

static
dnfMap *                           //   returns pointer to new map
dnfCopyMap(                        // copy a DNF map
dnfMap const * pMapSrc,            //   source to copy
CHAR const * filePath)             //   file associated with new map
{
     // make a copy of step aray
     dnfStep * pSteps=dnfCopySteps(pMapSrc);

     // construct new map based on old
     return(new dnfMap(filePath,pMapSrc->getDesc(),pSteps));
}

static
dnfStep *                          //   returns pointer to new array
dnfCopySteps(                      // create a copy of a map's step array
dnfMap const * pMapSrc)            //   map from which to copy
{
     dnfStep * pStepOld=pMapSrc->step;
     dnfStep * pStepNew=new dnfStep[pMapSrc->nsteps+1];
     for (INT i=0 ; i < pMapSrc->nsteps+1 ; ++i) {
          pStepNew[i].init(pStepOld[i].type,pStepOld[i].id
                          ,pStepOld[i].sym[0],pStepOld[i].sym[0]);
     }
     return(pStepNew);
}

// DNF_TEMPLATE text variable handling stuff

VOID
dnfSetTemplateTvb(                 // set DNF_TEMPLATE text variable
dnfHandler const * dnf)            //   based on this dnfHandler
{
     tvbDnf.set(dnf);
}

VOID
dnfClearTemplateTvb()              // clear DNF_TEMPLATE text variable
{
     tvbDnf.clr();
}

tvbDnfTemplate::tvbDnfTemplate() : // Constructo the magnificent
     tvbDefinition("DNF_TEMPLATE")
{
     clr();
     registerDef();
}

VOID
tvbDnfTemplate::set(               // set text variable
dnfHandler const * dnf)            //   based on this handler
{
     if (dnf == NULL) {
          clr();
     }
     else {
          CHAR const * fullName=(dnf->getMap()).getFile();
          ASSERT(fullName != NULL);
          ::fileparts(GCPART_FNAM,fullName,m_val,GCMAXPTH);
     }
}

VOID
tvbDnfTemplate::clr()              // clear the value
{
     *m_val='\0';
}

CHAR const *
tvbDnfTemplate::resolve(           // override base class resolve
CHAR const *)
{
     return(m_val);
}
