// Change History:
// 01/12/04 AV Development: Add AddAppl/RemoveAppl and GetStatus() to test whether Appl is a valid object. See bug #57
// 01/12/04 AV Development: Call to DRS_Terminate will delete Application pointer. Make sure appl has valid status. See bug #57
// 12/16/03 AV Development: Add process control functions
// 12/09/03 AV Development: Remove RunningProcesses
// 12/09/03 AV Development: Remove call to SetLastEnvNum
// 11/20/03 AV Development: BC6 conversion. const/none const
// 8/19/03 AV Development: Merge: VectorDatabase
// 8/19/03 AV Development: Remove: RecordSetFree
// 8/19/03 AV Development: Add: DeleteRecord(const AT_Record * Record)
// 8/27/03 AV Development: Add _spawn func to replace C library spawnl
// 8/28/03 AV Development: Replace deleteRandFile with deleteFile
// 8/28/03 AV Development: Use createWriteSeqFile and replace file objects to file pointers !!!???
// 8/28/03 AV Development: Clear StaticEnv container. Bug reported by QC
// 9/16/03 AV Development: Remove process specific code to DRS_DDProcess class. Use process object for Report/Export
// 10/24/03 AV Development: Changes in DumpExportParams

#include  <process.h>
//#include <dir.h>
#include <time.h>

#include  "base/at_error.h"
#include  "base/at_filetypes.h"
#include  "retrieve/at_retrieve.h"

#include "drs/drs_appl.h"
#include "drs/drs_idx.h"
// #include "drs/drs_fieldmap.h"
#include "drs/drs_srch.h"

//using namespace std;

//extern DRS_Application * pCurAppl;

// **************************************************************************
// Class: IndexFile
// **************************************************************************
DRS_IndexFile::DRS_IndexFile( DRS_Database const &rParent, uint iNewIndexFileID)
      : rDatabase( rParent), iIndexFileID( iNewIndexFileID),
        p_drfile(0), p_dxfile(0), pAT_IndexList(0)
{
}

DRS_IndexFile::~DRS_IndexFile()
{
  //delete pAT_IndexList;
  ARL_DeleteIndexList(pAT_IndexList);
  //delete p_drfile;
  ARL_DeleteFile(p_drfile);
  //delete p_dxfile;
  ARL_DeleteFile(p_dxfile);
}

AT_Index DRS_IndexFile::IndexOpen( uint idx_id)
{
  if( !pAT_IndexList)
    {
    T_FileName fn;
    T_FilePath fp;

    //Open index files ( index list)
    //iIndexFileID -1 designates no multiple index lists

    if( iIndexFileID == (uint)-1) wsprintfA( (char *)fn, "%cX", rDatabase.Designator());
    else wsprintfA( (char *)fn, "%cX%.2u", rDatabase.Designator(), iIndexFileID);
    DRS_Application::DataFilePathGet( fn, fp, 256, rDatabase.ParentAppl().INIFile());
    //p_dxfile = new AT_Read_Rand_File( (byte *) fp, ATFT_Index);
    p_dxfile = ARL_CreateReadRandFile( (byte *) fp, ATFT_Index);

    if( !p_dxfile || !(*p_dxfile)) throw AT_Unexpected_Err;

    if( iIndexFileID == (uint)-1) wsprintfA( (char *)fn, "%cR", rDatabase.Designator());
    else wsprintfA( (char *)fn, "%cR%.2u", rDatabase.Designator(), iIndexFileID);
    DRS_Application::DataFilePathGet( fn, fp, 256, rDatabase.ParentAppl().INIFile() );
    //p_drfile = new AT_Read_Rand_File( (byte *) fp, ATFT_IndexRefs);
    p_drfile = ARL_CreateReadRandFile( (byte *) fp, ATFT_IndexRefs);

    if( !p_drfile || !(*p_drfile)) throw AT_Unexpected_Err;

    //pAT_IndexList = new AT_Index_List( p_drfile, p_dxfile);
    pAT_IndexList = ARL_CreateIndexList( p_drfile, p_dxfile);
    }

  return( pAT_IndexList->openIndex(idx_id));
}

AT_Quantile_List const & DRS_IndexFile::Quantile( int iQuantile)
{
  //Index list maintains a collection of AT_Quantiles.

  //Return if avail
  if( AT_Quantile_List const * p = collQuantiles.Find( iQuantile))
    return *p;

  //Else open, insert and return
  T_FileName fn;
  T_FilePath fp;

  //Open index files ( index list)
  //iIndexFileID -1 designates no multiple index lists

  if( iIndexFileID == (uint)-1) wsprintfA( (char *)fn, "%cQ", rDatabase.Designator());
  else wsprintfA( (char *)fn, "%cQ%.2u", rDatabase.Designator(), iIndexFileID);

  if( iQuantile != -1)
    {
    byte ext[10];
    wsprintfA( (char *)ext, ".%.3d", iQuantile);
    strcat_s((char*)fn, sizeof(fn), (const char *)ext);
    }

  DRS_Application::DataFilePathGet( fn, fp, 256, rDatabase.ParentAppl().INIFile());

  AT_Read_Rand_File oFile( (byte *) fp, ATFT_Quantiles);
  if( !oFile)
    throw AT_File_Open_Err( oFile, fp);

  return( *collQuantiles.Insert( ARL_CreateQuantileList( &oFile), iQuantile ));
}

inline bool DRS_IndexFile::operator!
  ()
  const
{
  return ( false);
}


// **************************************************************************
// DRS_Database
// **************************************************************************
DRS_Database::DRS_Database( DRS_Application const *pAppl, byte cDesig, uint id)
    : rAppl( *pAppl), cDBDesig(cDesig), iID(id),
      pDTFile(0), pDIFile(0), pDDFile(0), pDatabase(0),
      Vectors(0), iVectorCount(0),
      iFieldCount(0), iSearchFieldCount(0), iFieldMapCount(0)
{
  DRS_FileKey key;
  T_FilePath fp;
  T_FileName fn;

  ////////////////////////////////////////////////////////////////////////////////////////////
  //Open database files
  ////////////////////////////////////////////////////////////////////////////////////////////
/*
try //Don't raise on no low-level database.
  {
*/
  wsprintfA( (char *)fn, "%cT", cDBDesig);
  pDTFile = ARL_CreateReadRandFile(DRS_Application::DataFilePathGet( fn, fp, sizeof(fp), rAppl.INIFile() ), ATFT_DataDesc);

  if( !pDTFile || !*pDTFile)throw AT_File_Open_Err( *this, fp);

  wsprintfA( (char *)fn, "%cI", cDBDesig);
  pDIFile = ARL_CreateReadRandFile( DRS_Application::DataFilePathGet( fn, fp, sizeof(fp), rAppl.INIFile() ), ATFT_DataRefs);

  wsprintfA( (char *)fn, "%cD", cDBDesig);
  pDDFile = ARL_CreateReadRandFile( DRS_Application::DataFilePathGet( fn, fp, sizeof(fp), rAppl.INIFile() ), ATFT_Data);

  if( !pDDFile || !*pDDFile) throw AT_File_Open_Err( *this, fp);

  ////////////////////////////////////////////////////////////////////////////////////////////
  //Open database
  ////////////////////////////////////////////////////////////////////////////////////////////
  pDatabase = new AT_Database( pDTFile, pDIFile, pDDFile);
  // ???PS
  //pDatabase = ARL_CreateDatabase( pDTFile, pDIFile, pDDFile, false); 
  if( !pDatabase || !*pDatabase) throw AT_Unexpected_Err;

/*
  }

catch (AT_File_Open_Error &exception)
  {
  //Do nothing on no low-lewvel db files. i.e. allow app to open with no db but with config.
  }
*/
  ////////////////////////////////////////////////////////////////////////////////////////////
  //Field Maps ( MUST be defined b4 search fields)
  ////////////////////////////////////////////////////////////////////////////////////////////
  wsprintfA( (char *)fn, "%cF", cDBDesig);
  DRS_Application::DataFilePathGet( fn, fp, sizeof(T_FilePath), pAppl->INIFile() );
  DRS_Application::DataFileAssert( fp);
//FieldMaps = new auto_ptr<DRS_FieldMap> [iFieldMapCount];
/*
  if( iFieldMapCount = GetPrivateProfileIntA( "FieldMaps", "MapCount", 0, (const char *)fp))
    {
    FieldMaps = new auto_ptr<DRS_FieldMap> [iFieldMapCount];
    for( uint i = 0; i < iFieldMapCount; ++i)
      {
      DRS_Setting key( "Map");
      key += i;

      T_FileName sMapFile;
      T_FilePath sMapFilePath;
      wsprintfA( (char *)sMapFile, "%cM%2.2u", cDBDesig, i);

      auto_ptr<DRS_FieldMap> p(new DRS_FieldMap( fp, (byte *) "FieldMaps", key, DRS_Application::DataFilePathGet( sMapFile, sMapFilePath, sizeof(T_FilePath), pAppl->INIFile() )));
      FieldMaps[i] = p;
      }
    }
*/

  ////////////////////////////////////////////////////////////////////////////////////////////
  //Data fields
  ////////////////////////////////////////////////////////////////////////////////////////////
  iFieldCount = GetPrivateProfileIntA( "FieldDef", "FieldCount", 0, (const char *)fp);
  if( ! iFieldCount)
    iFieldCount = pDatabase->getFieldCount();

  if (iFieldCount)
    pFields = new auto_ptr<DRS_Field> [iFieldCount];
    
  for( uint i = 0; i < iFieldCount; i++)
    {
    wsprintfA( (char *)key, "FieldType%u", i);
    switch ( int iType = GetPrivateProfileIntA( "FieldDef", (const char *)key, 0, (const char *)fp))
      {
      case DATAFIELDTYPE_TEXT:
      {
        auto_ptr<DRS_Field> rhs(new DRS_FieldText( *this, fp, (byte *)"FieldDef", i));
        pFields[i] = rhs;
        break;
      }
      //case DF_Integer:
      case DATAFIELDTYPE_INTEGER:
      {
        auto_ptr<DRS_Field> rhs(new DRS_FieldInteger( *this, fp, (byte *)"FieldDef", i));
        pFields[i] = rhs;
        break;
      }
      default:
        //if( !( iType < 0))
          throw AT_Bounds_Err( *this, (AT_String)"Field type", iType);
        //iType is -1 based FieldMapID.
//        auto_ptr<DRS_Field> rhs(new DRS_FieldMapped( *this, fp, (byte *)"FieldDef", i, FieldMap( abs(iType) - 1)));
//		pFields[i] = rhs;
      }
    }

  ////////////////////////////////////////////////////////////////////////////////////////////
  //Search fields
  ////////////////////////////////////////////////////////////////////////////////////////////

  if( iFieldCount)
    {
    wsprintfA( (char *)fn, "%cS", cDBDesig);

    DRS_Application::DataFilePathGet( fn, fp, sizeof(T_FilePath), rAppl.INIFile() );
    rAppl.DataFileAssert( (byte *) fp);

    iSearchFieldCount = GetPrivateProfileIntA( "SearchFields", "FieldCount", 0, (const char *)fp);

    if( iSearchFieldCount)
      {
      aaSearchFields = new auto_ptr<DRS_SearchField>[iSearchFieldCount];

      for( uint16 i = 0; i < iSearchFieldCount; ++i)
        {
        DRS_Setting setting("Field");
        setting += i;

        auto_ptr<DRS_SearchField> p(new DRS_SearchField( *this, fp, (byte *) "SearchFields", setting));
        aaSearchFields[i] = p;
        }
      }
    }
  ////////////////////////////////////////////////////////////////////////////////////////////
  //Search trees
  ////////////////////////////////////////////////////////////////////////////////////////////

  ////////////////////////////////////////////////////////////////////////////////////////////
  //Vectors
  ////////////////////////////////////////////////////////////////////////////////////////////
  //Read Vector Definitions stored in Vectordef 'V'
  wsprintfA( (char *)fn, "%cV", cDBDesig);
  DRS_Application::DataFilePathGet( fn, fp, sizeof(T_FilePath), pAppl->INIFile() );
  DRS_Application::DataFileAssert(fp);

  iVectorCount = GetPrivateProfileIntA( "VectorDef", "VectorCount", 0, (const char *)fp);
  if( !iVectorCount) return;

  //Single or multiple index list?
  /*
  Vectors map its index list(s) to a level within the vector.
  It has as many index lists as it has potential nodes at that level
  in the vector (calculated from Size() values).
  If the index list is mapped to its parent level ( i.e. -1), then it is mapped
  to the database.
  Each vector returns counts of its expected index lists to its parent database so that
  the database can open the correct index list requested by any vector.
  Vectors request their index lists from the parent database.
  */
  Vectors = new DRS_Vector* [iVectorCount];
  for( uint i = 0; i < iVectorCount; i++)
    Vectors[i] = new DRS_Vector( i, *this, fp);
}

DRS_Database::~DRS_Database()
{
  //delete pDatabase;
  ARL_DeleteDatabase(pDatabase); //AV

  //delete pDTFile;
  ARL_DeleteFile(pDTFile);
  //delete pDIFile;
  ARL_DeleteFile(pDIFile);
  //delete pDDFile;
  ARL_DeleteFile(pDDFile);

  for( uint i = 0; i < iVectorCount; i++) delete Vectors[i];
  delete [] Vectors;

  //Delete index files.
  for( std::vector<DRS_IndexFile *>::iterator iter = vIndexFiles.begin(); iter != vIndexFiles.end(); iter++)
    delete *iter;
}

uint DRS_Database::DataFieldApplID( uint iFieldID) const
{
//Data field asks database for appl id of field id.
//Database asks appl for Field count of every database before it in id.
  uint iApplIDs = 0;

  for( uint i = 0; i < iID; i++)
    iApplIDs += rAppl.Database(i).FieldCount();

  iApplIDs += iFieldID;

  return iApplIDs;
}

bool DRS_Database::operator!
    ()
    const
{
  return( !pDTFile || !pDIFile || !pDDFile || !*pDTFile || !*pDIFile || !*pDDFile ||
          !pDatabase || !*pDatabase);
}

DRS_Field const & DRS_Database::Field( uint iField) const
{
  if( iField >= FieldCount()) throw AT_Bounds_Err( *this, (AT_String)"Field", iField);
  return( *pFields[iField]);
}

inline DRS_Vector const & DRS_Database::Vector( uint iVector) const
{
  AT_Assert( (iVector < iVectorCount), *this, "Vector");
  return( *Vectors[iVector]);
}

DRS_SearchField const & DRS_Database::SearchField( uint iIdx) const
{
  if( iIdx >= (uint)iSearchFieldCount)
    throw AT_Bounds_Err( iIdx, (AT_String)"Search field", iIdx);

  return( *aaSearchFields[iIdx].get());
}

DRS_IndexFile & DRS_Database::IndexFile( uint iIndexFileID)
{
  for( std::vector<DRS_IndexFile *>::iterator iter = vIndexFiles.begin(); iter != vIndexFiles.end(); iter++)
    if( (*iter)->ID() == iIndexFileID) return **iter;

  //IndexFile not avail, so open.
  DRS_IndexFile *pIndexFile = new DRS_IndexFile( *this, iIndexFileID);
  vIndexFiles.push_back( pIndexFile);

  return( *pIndexFile);
}

//Indexes
//Scalar
AT_Index DRS_Database::IndexOpen( int iIndexFileID, uint iIndexID)
{
  return IndexFile( iIndexFileID).IndexOpen( iIndexID);
}
//Vector
AT_Index DRS_Database::IndexOpen( uint iVectorID, int iIndexFileID, uint iIndexID)
{
  /* IndexFiles and IDs
  A vector has a single or multiple index files based on its IndexFileMapLevel.
  The vector stores its base IndexFileID, read from file.

  IFF iIndexFileID == -1, database must add IndexIDCounts() from all prior vectors
  that have iIndexFileIDBase == -1.

  Indexes for a Vector may be stored in a single or multiple enumerated index files
  ( requested IndexFileID >= 0)
  OR collectively stored with other vectors in an index file with NO enumeration.
  ( requested IndexFileID == -1)

  Note that for any UNenumerated index file, the order of the vectors in the
  database (those with base = -1) must correspond to the order of the index
  storage in the un-enumerated index file.

  The database should check on create
  that no IndexFileIDs overlap. e.g. Base 0, count 4. Base 0, count 1.
  Note that this requires that the ordering of those vectors sharting an index file
  corresponds to the index file layout for the vectors in the index file.
  */

  if( iIndexFileID < 0)
    {
    for( uint i = 0; i < iVectorID; i++)
      if( Vectors[i]->IndexFileIDBase() < 0)
        iIndexID += Vectors[i]->IndexCount();
    }

  return IndexFile( iIndexFileID).IndexOpen( iIndexID);
}

AT_Quantile_List const & DRS_Database::Quantile( int iIndexFileID, int iQuantileID)
{
  //Returns quantile file for IndexFileID and QuantileID
  return( IndexFile(iIndexFileID).Quantile(iQuantileID));
}

// **************************************************************************
// DRS_Application
// **************************************************************************

DRS_Application::DRS_Application( char const *appl_id) : iDatabaseCount(0) ,
    isLoaded(false), CloseSession(NULL)
//Blank appl. Records errors.
{
  SetApplId((const byte*)appl_id);
}

DRS_Application::DRS_Application( char const *appl_id, char const *ini_file) : iDatabaseCount(0) ,
    isLoaded(false), CloseSession(NULL)
{
  SetApplId((const byte*)appl_id);

  sIniFile = ini_file;

  byte buf[1000];
  GetPrivateProfileStringA( "Application", "Description", "", (LPSTR)buf, sizeof( buf), (LPCSTR)INIFile());
  sApplDescr = (char *)buf;

  GetPrivateProfileStringA( "Application", "DataPath", "", (LPSTR)buf, sizeof( buf), (LPCSTR)INIFile());
  if( !buf[0])
    throw AT_Bounds_Err(*this, (AT_String) "DataPath", 0);
  sDataPath = (char *)buf;

  //Read ini file and open relevant databases
  GetPrivateProfileStringA( "Application", "DatabaseDesignators", "", (LPSTR)buf, sizeof( buf), (LPCSTR)INIFile());
  _strupr_s((char*)buf, sizeof(buf));
  if( !buf[0])
    throw AT_Bounds_Err(*this, (AT_String) "DatabaseDesignators", 0);

  UTL_StringClean( buf);

  iDatabaseCount = strlen((const char*)buf);
  aaDatabases = new auto_ptr<DRS_Database> [iDatabaseCount];
  for( uint i = 0; i < iDatabaseCount; i++)
    {
    auto_ptr<DRS_Database> p( new DRS_Database( this, buf[i], i));
    aaDatabases[i] = p;
    if( !*aaDatabases[i].get())
      throw AT_Constructor_Err(0);
    }

  isLoaded = true;
}

void DRS_Application::SetApplId(const byte * id)
{
  byte buf[MAX_PATH];
  sApplID = (char *)FormatApplID(id, buf);
}

byte * DRS_Application::FormatApplID(const byte * in, byte * out)
{
    uint i = 0;
    while(in[i])
        out[i] = in[i++];

    while(i < 4)
        out[i++] = '_';
    out[i] = 0;
    
  strupr((char*)out);

return out;
}

DRS_Application::~DRS_Application()
{
    if(CloseSession)
        CloseSession();
}

bool DRS_Application::operator!
  ()
{
  return( !sDataPath.length() || !isLoaded);
}

string DRS_Application::ExceptionPop()
{
  if(!vErrorStrings.size())
    return string("");
  string s = vErrorStrings.back();
  vErrorStrings.pop_back();
  return s;
}

template <class Error, class Reporter>
bool Report(AT_Error * Err, std::vector<string> & vStrs)
{
  Error *err = dynamic_cast<Error *>(Err);
  if (err)
  {
    Reporter rpt;
    err->report(rpt);
    rpt.GetStrings( vStrs);
    return true;
  }

  return false;
}

void DRS_Application::RecordErrors( AT_Error &error)
{

  //Create proper reporter for type of error and pass to err.
  //Add all error strings to vErrorStrings

  for (AT_Error *e = &error; e; e = e->getCause())
    {
    if(Report<AT_Index_List::Error, AT_IdxErrorReporter>(e, vErrorStrings))
      continue;

    if(Report<AT_General_Error, AT_GenErrorReporter>(e, vErrorStrings))
      continue;

    if(Report<AT_System_Error, AT_GenErrorReporter>(e, vErrorStrings))
      continue;

    if(Report<DRS_Error, DRS_Error::Reporter>(e, vErrorStrings))
      continue;
    }
}
//Application vectors
uint DRS_Application::VectorCount() const
{
  uint iCount = 0;
  for( uint i = 0; i < iDatabaseCount; i++)
    iCount += aaDatabases[i].get()->VectorCount();
  return( iCount);
}
/*
DRS_Vector const * DRS_Application::GetVector( uint iVector)
{
  uint i;
  for( i = 0; (i < iDatabaseCount) && (iVector >= aaDatabases[i].get()->VectorCount());
       i++, iVector -= aaDatabases[i].get()->VectorCount());
  if( i >= iDatabaseCount) throw AT_Bounds_Err( *this, (AT_String)"Vector", iVector);

  return aaDatabases[i].get()->Vector( iVector);
}
*/
DRS_Vector const & DRS_Application::Vector( uint iVector) const
{
  uint i;
  uint iID = iVector;
  for( i = 0; (i < iDatabaseCount) && (iID >= aaDatabases[i].get()->VectorCount()); i++)
    iID -= aaDatabases[i].get()->VectorCount();
  if( i >= iDatabaseCount) throw AT_Bounds_Err( *this, (AT_String)"Vector", iVector);

  return aaDatabases[i].get()->Vector( iID);
}

uint DRS_Application::MaxVectorDepth() const
{
  uint res = 0;
  for(uint v = 0; v < VectorCount(); ++v)
    res = max(res, Vector(v).Depth());
  return res;    
}

//SearchFields
uint DRS_Application::SearchFieldCount() const
{
  uint iCount = 0;
  for( uint i = 0; i < iDatabaseCount; i++)
    iCount += aaDatabases[i].get()->SearchFieldCount();
  return( iCount);
}

DRS_SearchField const & DRS_Application::SearchField( uint iSearchField) const
{
  uint i;
  uint iID = iSearchField;
  for( i = 0; (i < iDatabaseCount) && (iID >= aaDatabases[i].get()->SearchFieldCount()); i++)
    iID -= aaDatabases[i].get()->SearchFieldCount();
  if( i >= iDatabaseCount) throw AT_Bounds_Err( *this, (AT_String)"SearchField", iSearchField);

  return aaDatabases[i].get()->SearchField( iID);
}


/*
int DRS_Application::SearchFieldFieldMapID( uint iSearchField) const
{
//Returns FieldMapID of SearchField in Application!
//Returns -1 on SearchFields IndexList not mapped.

  uint i;
  uint iID = iSearchField;
  for( i = 0; (i < iDatabaseCount) && (iID >= aaDatabases[i].get()->SearchFieldCount()); i++)
    iID -= aaDatabases[i].get()->SearchFieldCount();
  if( i >= iDatabaseCount) throw AT_Bounds_Err( *this, (AT_String)"SearchField", iSearchField);

  DRS_IndexList const * pIndexList = &aaDatabases[i].get()->SearchField( iID).IndexList();
  if( DRS_IndexListMapped const * pMapped = dynamic_cast<DRS_IndexListMapped const *>(pIndexList))
    {
    uint iFieldMapID =  pMapped->FieldMapID(); //FieldMapID in Database
    //Add FieldMapCounts for all prior databases
    for( uint j = 0; j < i; j++)
      iFieldMapID += aaDatabases[i].get()->FieldMapCount();
    return iFieldMapID;
    }
  else return -1;
}
*/

//Application fieldmaps
uint DRS_Application::FieldMapCount() const
{
/*
  uint iCount = 0;
  for( uint i = 0; i < iDatabaseCount; i++)
    iCount += aaDatabases[i].get()->FieldMapCount();
  return( iCount);
*/
return 0;
}

/*
DRS_FieldMap const & DRS_Application::FieldMap( uint iFieldMap) const
{
  uint i;
  uint iID = iFieldMap;
  for( i = 0; (i < iDatabaseCount) && (iID >= aaDatabases[i].get()->FieldMapCount()); i++)
    iID -= aaDatabases[i].get()->FieldMapCount();
  if( i >= iDatabaseCount) throw AT_Bounds_Err( *this, (AT_String)"Field map", iFieldMap);

  return aaDatabases[i].get()->FieldMap( iID);
}
*/

//Application Fields
uint DRS_Application::FieldCount() const
{
  uint iCount = 0;
  for( uint i = 0; i < iDatabaseCount; i++)
    iCount += aaDatabases[i].get()->FieldCount();
  return( iCount);
}

DRS_Field const & DRS_Application::Field( uint iField) const
{
  uint i;
  uint iID = iField;
  for( i = 0; (i < iDatabaseCount) && (iID >= aaDatabases[i].get()->FieldCount()); i++)
    iID -= aaDatabases[i].get()->FieldCount();
  if( i >= iDatabaseCount) throw AT_Bounds_Err( *this, (AT_String)"Field", iField);

  return aaDatabases[i].get()->Field( iID);
}

AT_Record_List * DRS_Application::RecList_New()
{
  AT_Assert( iDatabaseCount, *this, "RecList_New");
  //return (new AT_Record_List(aaDatabases[0].get()->pDatabase->getRecordCount()));
  return ARL_CreateRecordListR(RecordCount());
}

uint DRS_Application::VectorDatabase( uint iVector) const
{
//Return absolute vector count. Retrieval by occurrence.

  //Get the database and its vector id
  uint i;
  uint iID = iVector;
  for( i = 0; (i < iDatabaseCount) && (iID >= aaDatabases[i].get()->VectorCount()); i++)
    iID -= aaDatabases[i].get()->VectorCount();
  if( i >= iDatabaseCount) throw AT_Bounds_Err( *this, (AT_String)"Vector", iVector);

  return i; //iVectorID));
}

uint32 DRS_Application::RecordVectorCount( AT_Record const *AT_Records[], uint iVector) const
{
//Return absolute vector count. Retrieval by occurrence.

  //Get the database and its vector id
  uint i;
  uint iID = iVector;
  for( i = 0; (i < iDatabaseCount) && (iID >= aaDatabases[i].get()->VectorCount()); i++)
    iID -= aaDatabases[i].get()->VectorCount();
  if( i >= iDatabaseCount) throw AT_Bounds_Err( *this, (AT_String)"Vector", iVector);

  return( AT_Records[i]->getVectorCount()); //iVectorID));
}

void DRS_Application::DataFileAssert( byte *fp)
{
  int version = GetPrivateProfileIntA( "Version", "Version", -1, (LPCSTR)fp);
  //int release = GetPrivateProfileInt( "Version", "Release", -1, fp);

  if( version == -1) throw AT_File_Open_Err( fp, fp);
}

byte * DRS_Application::DataFilePathGet( const byte * fn, byte *fp, uint size, const byte * ini)
{ // fn = "VX00" e.g.

  // _TO_DO_ Verify fp buffer sizes

  // Search for file spec in ini file
  GetPrivateProfileStringA( "Data", ( char *)fn, "", (char *)fp, size, (LPCSTR)ini);
  if( fp[0] == 0)
    GetPrivateProfileStringA( "Application", "DataPath", "", (char *)fp, size, (LPCSTR)ini);

  if( fp[0] && (fp[std::strlen((const char*)fp)-1] != '\\'))
    std::strcat( (char*) fp, "\\");

  byte sAppl[MAXPATH];
  GetPrivateProfileStringA( "Application", "ApplID", "", (char *)sAppl, sizeof(sAppl), (LPCSTR)ini);
  FormatApplID(sAppl, sAppl);

  std::string fnName((char *)fn);
  fnName += "Name";
  byte sName[MAXPATH];
  if(GetPrivateProfileStringA( "Data", fnName.c_str(), "", (char *)sName, sizeof(sName), (LPCSTR)ini))
    std::strcat( (char*) fp, (const char*) sName);
  else
    {
    std::strcat( (char*) fp, (const char*) sAppl);
    std::strcat( (char*) fp, (const char*) fn);
    }
    
  return( fp);
}

AT_Record const * DRS_Application::RecordGet( uint32 lRecNo, uint iDatabase) const
{
  AT_Database * db = aaDatabases[iDatabase].get()->pDatabase;
  return db->getRecord(lRecNo);
}

void DRS_Application::DeleteRecord(const AT_Record * Record) const
{
    ARL_DeleteDatabaseRecord(Record);
}

AT_Record const ** DRS_Application::RecordSetGet( uint32 lRecNo) const
{
  //Get records for each db
  AT_Record const ** pRecords = new AT_Record const *[iDatabaseCount];

  for( uint i = 0; i < iDatabaseCount; i++)
    pRecords[i] = RecordGet(lRecNo, i);

  return pRecords;
}

/****************************************************************************
 NOTES
 ****************************************************************************
Template candidates:

IndexFileElem IndexGet
IndexElem IndexGet


*/
