// Change History
// 12/03/03 AV Development: Code Cleanup. const/none const conversion
// 11/20/03 AV Development: BC6 conversion. Move IsValidPeriod from h to cpp

// **************************************************************************
// DRS_Formula
// **************************************************************************
#include "drs/drs_formula.h"
#include "drs/drs_type.h"
#include "drs/drs_vect.h"

//Create Formula from formula
//DRS_Formula::DRS_Formula( DRS_FormulaNode *pFormulaNodeNew)
//{
//  pFormulaNode = pFormulaNodeNew
//}

//Create "default" formula (VectorVal formula root node)
DRS_Formula::DRS_Formula( DRS_Vector const &rVector)
  : lCurRecNum(UNDEFINED_UINT32), ID(0),
    VectorRawData( rVector),
    iDatabaseID( rVector.ParentDatabase().ID()),
    isMutable( false),isQuantile( false),
    isSumable(true), isGroupable(false), MaxValue(0)
{
  uint iNode = 0;
  pFormulaRoot.reset( new DRS_FormulaNodeObjectVectorVal( this, &iNode));
}

DRS_Formula::DRS_Formula( DRS_Vector const &rVector, uint iID, byte *sSection, byte *sFile)
              : lCurRecNum(UNDEFINED_UINT32), ID(iID),
                VectorRawData( rVector),
                iDatabaseID( rVector.ParentDatabase().ID()), isQuantile( false),
                isSumable(true), isGroupable(false), MaxValue(0)
{
  char sKey[32], buf[128];
  wsprintfA( sKey, "Formula%uDescr", iID);
  GetPrivateProfileStringA( (LPCSTR)sSection, sKey, "", buf, 128, (LPCSTR) sFile);
  Descr.Extract( (byte*) buf);

  /*
  wsprintf( sKey, "Formula%uIsReportStates", iID);
  isReportStates = (bool) GetPrivateProfileInt( sSection, sKey, 0, sFile);
  */
  wsprintfA( sKey, "Formula%uIsReport", iID);
  isReport = !!GetPrivateProfileIntA( (LPCSTR)sSection, sKey, 0, (LPCSTR)sFile);

  wsprintfA( sKey, "Formula%uIsMutable", iID);
  isMutable = !!GetPrivateProfileIntA( (LPCSTR)sSection, sKey, 0, (LPCSTR)sFile);

  wsprintfA( sKey, "Formula%uMaxValue", iID);
  byte snum[38];
  GetPrivateProfileStringA( (LPCSTR) sSection, sKey, "0", (LPSTR) snum, sizeof(snum), (LPCSTR)sFile);
  MaxValue = atoAT_Num((const char*)snum);
  
  wsprintfA( sKey, "Formula%uIllegalPeriodCount", iID);

  uint iIllegalPeriodCount = GetPrivateProfileIntA( (LPCSTR)sSection, sKey, 0, (LPCSTR)sFile);
  for( uint i = 0; i < iIllegalPeriodCount; ++i)
    {
    wsprintfA( sKey, "Formula%uIllegalPeriod%d", iID, i);
    vIllegalPeriods.push_back( GetPrivateProfileIntA( (LPCSTR)sSection, sKey, MAX_UINT, (LPCSTR)sFile));
    if( vIllegalPeriods[vIllegalPeriods.size()-1] == MAX_UINT)
      throw AT_Bounds_Err( *this, (AT_String) sKey, i);
    }

  wsprintfA( sKey, "Formula%uIsSumable", iID);
  isSumable = !!GetPrivateProfileIntA( (LPCSTR)sSection, sKey, !iID, (LPCSTR)sFile);

  wsprintfA( sKey, "Formula%uIsGroupable", iID);
  isGroupable = !!GetPrivateProfileIntA( (LPCSTR)sSection, sKey, iID > 1 && !iIllegalPeriodCount, (LPCSTR)sFile);

  wsprintfA( sKey, "Formula%uNode0Type", iID);
  uint iNode0Type = GetPrivateProfileIntA( (LPCSTR) sSection, sKey, -1, (LPCSTR)sFile);

  wsprintfA( sKey, "Formula%uNode0Class", iID);
  uint iNode0TypeClass = GetPrivateProfileIntA( (LPCSTR)sSection, sKey, -1, (LPCSTR)sFile);

  uint iNode = 0; //Yields node count after node creation
  switch( iNode0Type)
    {
    case DRS_FORMULANODE_OBJECT:
      switch(iNode0TypeClass)
        {
        case DRS_FORMULANODE_OBJECT_VECTORQUANTILE:
          if (!rVector.QunatileCount())
            break;
          isQuantile = true;
          pFormulaRoot.reset(new DRS_FormulaNodeObjectVectorQuantile( this, &iNode, sSection, sFile));
          break;
        case DRS_FORMULANODE_OBJECT_VECTORVAL:
          pFormulaRoot.reset( new DRS_FormulaNodeObjectVectorVal( this, &iNode));
          break;
        case DRS_FORMULANODE_OBJECT_VECTORVALREL:
          pFormulaRoot.reset( new DRS_FormulaNodeObjectVectorValRel( this, &iNode, sSection, sFile));
          break;
        case DRS_FORMULANODE_OBJECT_VECTORVALABS:
          pFormulaRoot.reset( new DRS_FormulaNodeObjectVectorValAbs( this, &iNode, sSection, sFile));
          break;
        case DRS_FORMULANODE_OBJECT_INTEGER:
          pFormulaRoot.reset( new DRS_FormulaNodeObjectInteger( this, &iNode, sSection, sFile));
          break;
        case DRS_FORMULANODE_OBJECT_RELATIVENODE:
          pFormulaRoot.reset( new DRS_FormulaNodeObjectRelativeNode( this, &iNode, sSection, sFile));
          break;
        case DRS_FORMULANODE_OBJECT_DIMVECTORCOUNT:
          pFormulaRoot.reset( new DRS_FormulaNodeObjectDimVectorCount( this, &iNode, sSection, sFile));
          break;
        default:
          throw AT_Bounds_Err( uint, sKey, iNode0TypeClass);
        }
      break;
    case DRS_FORMULANODE_ACTION:
      switch(iNode0TypeClass)
        {
        case DRS_FORMULANODE_ACTION_ADD:
          pFormulaRoot.reset( new DRS_FormulaNodeActionAdd( this, &iNode, sSection, sFile));
          break;
        case DRS_FORMULANODE_ACTION_SUBTRACT:
          pFormulaRoot.reset( new DRS_FormulaNodeActionSubtract( this, &iNode, sSection, sFile));
          break;
        case DRS_FORMULANODE_ACTION_MULTIPLY:
          pFormulaRoot.reset( new DRS_FormulaNodeActionMultiply( this, &iNode, sSection, sFile));
          break;
        case DRS_FORMULANODE_ACTION_DIVIDE:
          pFormulaRoot.reset( new DRS_FormulaNodeActionDivide( this, &iNode, sSection, sFile));
          break;
        default:
          throw AT_Bounds_Err( uint, sKey, iNode0TypeClass);
        }
      break;
    default:
      throw AT_Bounds_Err( uint, "Formula node type", iNode0Type);
    }
}

DRS_Formula::~DRS_Formula()
{
}

void DRS_Formula::Serialise( uint iFormulaID, byte *sSection, byte *sFile)
{
  byte sKey[32];
  wsprintfA( (LPSTR) sKey, "Formula%uDescr", iFormulaID);
  Descr.Serialise( sSection, sKey, sFile);

  uint iNodeID = 0;
  pFormulaRoot->Serialise( iFormulaID, &iNodeID, sSection, sFile);
}

//TO_DO: Get single vector data by vector occurrence
//TO_DO: Get all vector data in block. NOTE: will return ALL markets. Overkill?
//Get vector data by address
void DRS_Formula::GetVector( DRS_RecordSet &rRecordSet, uint32 pVectorAddress[], DRS_VectorDataElem* pResults[], bool *pIsAvail)
{
  //if the rec has changed, clear stateformula nodes.
  //Each node dependant on an address will reload rec iff address changes.
  //This allows, e.g., for not recalculating market totals for vector elements of the
  //same market

  //IF the record has changed, need to clear storage
  if( rRecordSet.RecordNumberAbs() != lCurRecNum)
    {
    pFormulaRoot->LoadRecord();
    lCurRecNum = rRecordSet.RecordNumberAbs();
    }

  //If the vector address has changed, node calcs may need to update storage

  //On create, get rec idx from Vector to be taken from DRS_Record
  //Better than AT_Record passing from DB since we may allow user to retrieve
  //formula for direct access later.

  pFormulaRoot->LoadVectorAddress( &rRecordSet, pVectorAddress);

  //TO_DO: Each node computes entire vector data, not single element. Consider math exceptions!
  uint iPeriodCount = VectorRawData.GetVector()->PeriodCount();

  *pIsAvail = VectorRawData.LoadFromDRSVector( rRecordSet, pVectorAddress);
  /*
  if( pIsAvail && ! *pIsAvail)
    {
    delete [] pVectorDataRaw;
    memset( pVectorData, 0, iPeriodCount * sizeof( int32));
    return;
    }
  */
  for( uint i = 0; i < iPeriodCount; i++)
    {
    // Catch math exceptions
    try
      {
      if( IsValidPeriod(i)) //Invalid periods return 0
        pResults[i]->SetFormulaNode( pFormulaRoot.get(), &VectorRawData, i, lCurRecNum);
      else *pResults[i] = DRS_VectorDataElem( 0, VectorRawData.GetVector()->DataPrecisionMult());
      }
    catch( AT_Error &exception)
      {
      throw;
      }
    }
}

inline bool DRS_Formula::IsValidPeriod( uint iPeriod) const
{
    return ( std::find( vIllegalPeriods.begin(), vIllegalPeriods.end(), iPeriod) == vIllegalPeriods.end() );
}

const DRS_Vector * DRS_Formula::Vector() const
{
return VectorRawData.GetVector();
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// DRS_FormulaNode
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
const DRS_Vector * DRS_FormulaNode::GetVector() const
{
return Formula ? Formula->Vector() : NULL;
}

uint DRS_FormulaNode::GetFormulaID() const
{
return Formula ? Formula->GetID() : 0;
}

void DRS_FormulaNode::TypeClassStamp( uint iType, uint iTypeClass, uint iFormulaID, uint *pNodeID, byte *sSection, byte *sFile)
{

  byte sKey[32];
  wsprintfA( (LPSTR) sKey, "Formula%uNode%uType", iFormulaID, *pNodeID);
  UTL_WritePrivateProfileInt( sSection, sKey, (uint32) iType, sFile);

  wsprintfA( (LPSTR) sKey, "Formula%uNode%uClass", iFormulaID, *pNodeID);
  UTL_WritePrivateProfileInt( sSection, sKey, (uint32) iTypeClass, sFile);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// DRS_FormulaNodeObject
////////////////////////////////////////////////////////////////////////////////////////////////////////////////

//VectorValue
inline DRS_VectorDataElem DRS_FormulaNodeObjectVectorVal::Compute( const DRS_VectorData &rDataVector, uint iItem, uint32 lCurRecNum) const
{
  return DRS_VectorDataElem( rDataVector.GetDataElement( iItem));
}

//VectorQuantile
DRS_FormulaNodeObjectVectorQuantile::DRS_FormulaNodeObjectVectorQuantile( const DRS_Formula * f, uint *pNodeID, byte *sSection, byte *sFile)
           : DRS_FormulaNodeObject( f), pQuantile(0), iBaseIndexID(0),
           normal(0), mult(1)
{
  //Read
  byte sKey[32];
  wsprintfA( (LPSTR) sKey, "Formula%uNode%uQuantileID", GetFormulaID(), *pNodeID);
  iQuantileIdx = GetPrivateProfileIntA( (LPCSTR) sSection, (LPCSTR) sKey, -1, (LPCSTR) sFile);
  if( iQuantileIdx == -1) throw AT_Bounds_Err( *this, "QuantileID", iQuantileIdx);

//  if (rVector.VectorQuantile(iQuantileIdx).Precision() < rVector.IndexPrecision())
//    normal = 0;
  const DRS_Vector * vector = GetVector();
  normal = vector->Normal(vector->VectorQuantile(iQuantileIdx).State());
  mult = pow10<uint>(vector->VectorQuantile(iQuantileIdx).Precision());

  (*pNodeID)++;
}

void DRS_FormulaNodeObjectVectorQuantile::Serialise( uint iFormulaID, uint *pNodeID, byte *sSection, byte *sFile)
{
  DRS_FormulaNode::TypeClassStamp( DRS_FORMULANODE_OBJECT, DRS_FORMULANODE_OBJECT_VECTORQUANTILE, iFormulaID, pNodeID, sSection, sFile);

  DRS_FileKey sKey;
  wsprintfA( (LPSTR) sKey, "Formula%uNode%uQuantileID", iFormulaID, *pNodeID);
  UTL_WritePrivateProfileInt( sSection, sKey, (uint32)iQuantileIdx,sFile);

  (*pNodeID)++;
}
void DRS_FormulaNodeObjectVectorQuantile::LoadVectorAddress( DRS_RecordSet *pRecord, uint32 pVectorAddress[])
{
  //Get the quantile for vector address

  //Quantile is located from Database by IndexFileID and QuantileId
  //Store base IndexID within IndexFile/Quantile ( w/out period)
  const DRS_Vector * vector = GetVector();
  const uint32 * addr = vector->IndexAddress( pVectorAddress);
  iBaseIndexID = vector->IndexID( addr, vector->VectorQuantile(iQuantileIdx).State(), 0);
  DRS_Database * db = (DRS_Database *)&vector->ParentDatabase();
  pQuantile = &db->Quantile( vector->IndexFileID( addr), iQuantileIdx);
}

inline DRS_VectorDataElem DRS_FormulaNodeObjectVectorQuantile::Compute( const DRS_VectorData &rDataVector, uint iItem, uint32 lCurRecNum) const
{
  AT_Assert( pQuantile, *this, "VectorQuantile::Compute");

  //Convert iItem ( i.e. data period) to index period
  //Return 0 on non-indexed periods.
  const DRS_Vector * vector = GetVector();
  uint iIndexPeriod = vector->DataPeriodToIndexPeriod( iItem);
  if( iIndexPeriod == MAX_UINT) return DRS_VectorDataElem( 0, 1);
  RealValue vect_val = rDataVector.GetDataElement(iItem).GetRealValueAtPrecision(vector->VectorQuantile(iQuantileIdx).Precision()) * mult;

  return DRS_VectorDataElem( ( !rDataVector.GetDataElement( iItem) ?
                               0:
                               pQuantile->valueToQuantile( iBaseIndexID + iIndexPeriod,
                                                           vect_val ? vect_val - normal : 0,
                                                           lCurRecNum)), 1);
}

inline void DRS_FormulaNodeObjectVectorQuantile::LoadRecord( )
{
  pQuantile = 0;
}

#pragma args_used
void DRS_FormulaNodeObjectVectorVal::Serialise( uint iFormulaID, uint *pNodeID, byte *sSection, byte *sFile)
{
  DRS_FormulaNode::TypeClassStamp( DRS_FORMULANODE_OBJECT, DRS_FORMULANODE_OBJECT_VECTORVAL, iFormulaID, pNodeID, sSection, sFile);

  (*pNodeID)++;
}

//Relative vector value
DRS_FormulaNodeObjectVectorValRel::DRS_FormulaNodeObjectVectorValRel( const DRS_Formula * f, int iNewOffset)
    : DRS_FormulaNodeObject(f), iOffset(iNewOffset), iPeriodCount(0)
{
  //Keep vector length
  const DRS_Vector * vector = GetVector();
  iPeriodCount = vector->PeriodCount();
}

DRS_FormulaNodeObjectVectorValRel::DRS_FormulaNodeObjectVectorValRel( const DRS_Formula * f, uint *pNodeID, byte *sSection, byte *sFile) :
    DRS_FormulaNodeObject( f)
{
  //Keep vector length
  const DRS_Vector * vector = GetVector();
  iPeriodCount = vector->PeriodCount();
  //Read iOffset
  byte sKey[32];
  wsprintfA( (LPSTR)sKey, "Formula%uNode%uOffset", GetFormulaID(), *pNodeID);
  if( (iOffset = GetPrivateProfileIntA( (LPCSTR) sSection, (LPCSTR) sKey, 0, (LPCSTR) sFile)) == MAX_UINT)
    throw AT_Bounds_Err( *this, (AT_String) sKey, -1);

  (*pNodeID)++;
}
inline DRS_VectorDataElem DRS_FormulaNodeObjectVectorValRel::Compute( const DRS_VectorData &rDataVector, uint iItem, uint32 lCurRecNum) const
{
  if( ((int)iItem + iOffset) < 0 || ((int)iItem + iOffset) >= (int32)iPeriodCount)
    throw AT_Bounds_Err( *this, (AT_String)"Period", iItem + iOffset);

  return rDataVector.GetDataElement( iItem + iOffset);
}
void DRS_FormulaNodeObjectVectorValRel::Serialise( uint iFormulaID, uint *pNodeID, byte *sSection, byte *sFile)
{
  DRS_FormulaNode::TypeClassStamp( DRS_FORMULANODE_OBJECT, DRS_FORMULANODE_OBJECT_VECTORVALREL, iFormulaID, pNodeID, sSection, sFile);

  byte sKey[32];
  wsprintfA( (LPSTR) sKey, "Formula%uNode%uValRel", iFormulaID, *pNodeID);
  UTL_WritePrivateProfileInt( sSection, sKey, (uint32)iOffset,sFile);
  (*pNodeID)++;
}

//Absolute vector value
DRS_FormulaNodeObjectVectorValAbs::DRS_FormulaNodeObjectVectorValAbs( const DRS_Formula * f, uint *pNodeID, byte *sSection, byte *sFile) :
    DRS_FormulaNodeObject(f)
{
  const DRS_Vector * vector = GetVector();

  byte sKey[32];
  wsprintfA( (LPSTR) sKey, "Formula%uNode%uValAbs", GetFormulaID(), *pNodeID);
  iPeriod = GetPrivateProfileIntA( (LPCSTR) sSection, (LPCSTR) sKey, -1, (LPCSTR) sFile);
  if( iPeriod >= vector->PeriodCount()) throw AT_Bounds_Err( *this, sKey, iPeriod);
  (*pNodeID)++;
}
inline DRS_VectorDataElem DRS_FormulaNodeObjectVectorValAbs::Compute( const DRS_VectorData &rDataVector, uint iItem, uint32 lCurRecNum) const
{
  return rDataVector.GetDataElement( iPeriod);
}
void DRS_FormulaNodeObjectVectorValAbs::Serialise( uint iFormulaID, uint *pNodeID, byte *sSection, byte *sFile)
{
  DRS_FormulaNode::TypeClassStamp( DRS_FORMULANODE_OBJECT, DRS_FORMULANODE_OBJECT_VECTORVALABS, iFormulaID, pNodeID, sSection, sFile);

  byte sKey[32];
  wsprintfA( (LPSTR) sKey, "Formula%uNode%uValAbs", iFormulaID, *pNodeID);
  UTL_WritePrivateProfileInt( sSection, sKey, (uint32)iPeriod,sFile);
  (*pNodeID)++;
}

//Integer value
#pragma args_used
DRS_FormulaNodeObjectInteger::DRS_FormulaNodeObjectInteger( const DRS_Formula * f, uint *pNodeID, byte *sSection, byte *sFile) :
    DRS_FormulaNodeObject(f)
{
  byte sKey[32];
  wsprintfA((LPSTR)  sKey, "Formula%uNode%uInteger", GetFormulaID(), *pNodeID);
  lVal = UTL_GetPrivateProfileInt( sSection, sKey, 0L, sFile);
  (*pNodeID)++;
}
inline DRS_VectorDataElem DRS_FormulaNodeObjectInteger::Compute( const DRS_VectorData &rDataVector, uint iItem, uint32 lCurRecNum) const
{
  return DRS_VectorDataElem( lVal, 1);
}
void DRS_FormulaNodeObjectInteger::Serialise( uint iFormulaID, uint *pNodeID, byte *sSection, byte *sFile)
{
  DRS_FormulaNode::TypeClassStamp( DRS_FORMULANODE_OBJECT, DRS_FORMULANODE_OBJECT_INTEGER, iFormulaID, pNodeID, sSection, sFile);

  byte sKey[32];
  wsprintfA( (LPSTR) sKey, "Formula%uNode%uInteger", iFormulaID, *pNodeID);
  UTL_WritePrivateProfileInt( sSection, sKey, lVal,sFile);
  (*pNodeID)++;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////
//Dimesion Calculations
//////////////////////////////////////////////////////////////////////////////////////////////////////////
//Dim calcs compute dimension stats on the dim of the desired vector. Ideally, they could store
//the stats for the last requested vector and update the stats only when
//	the record changes or
//	one of the dimensions above the dimension of the calc changes.
//
//But we currently have no way to determine if the record changes. So dim calcs are
//currently performed for each vector retrieval - somewhat wasteful.

//Relative node:
/*
DRS_FormulaNodeObjectRelativeNode::DRS_FormulaNodeObjectRelativeNode( DRS_Vector const &rParentVector, uint iNewDim, int iNewOffset)
          : rVector(rParentVector), iOffset(iNewOffset), addrLoaded(0)
{
  pSumVector = new int32[rVector.PeriodCount()];

  //VectorIds of currently loaded sum vector,
  //Sum vector must be refreshed when dim prior to reqested dim changes.
  //Must also refresh on new record.
}
*/

DRS_FormulaNodeObjectRelativeNode::DRS_FormulaNodeObjectRelativeNode( const DRS_Formula * f,
                                                                      uint *pNodeID,
                                                                      byte *sSection,
                                                                      byte *sFile)
  : DRS_FormulaNodeObject(f),
    iOffset(0),
    addrRelative( (uint*) new VectorAddress[f->Vector()->PeriodCount()]),
    addrLoaded(0),
    SumVector( *f->Vector())
{
  na_string base;
  na_string key;
  char num_buf[33];

  base = "Formula";
  base += itoa(GetFormulaID(), num_buf, 10);
  base += "Node";
  base += itoa(*pNodeID, num_buf, 10);

  const DRS_Vector * vector = GetVector();
  for( uint i = 1; i <= vector->Depth(); i++)
    {
    key = base;
    key += "Dim";
    key += itoa(i, num_buf, 10);
    addrRelative[i-1] = GetPrivateProfileIntA( (LPCSTR) sSection, key.c_str(), 0L, (LPCSTR) sFile);
    }


  //Relative offset
  key = base;
  key += "Shift";

  iOffset = GetPrivateProfileIntA( (LPCSTR) sSection, key.c_str(), 0, (LPCSTR) sFile);

  (*pNodeID)++;
}

DRS_FormulaNodeObjectRelativeNode::~DRS_FormulaNodeObjectRelativeNode()
{
  delete [] addrLoaded;
}

inline void DRS_FormulaNodeObjectRelativeNode::LoadRecord( )
{
  //Clear stored vector address.
  delete [] addrLoaded;
  addrLoaded = 0;
}
void DRS_FormulaNodeObjectRelativeNode::LoadVectorAddress( DRS_RecordSet *pRecord, uint32 pVectorIDs[])
{

  //Load relative node data

  const DRS_Vector * vector = GetVector();
  if( ! addrLoaded)
    {
    addrLoaded = (uint32*) new VectorAddress[vector->Depth()];
    }

  for( uint i = 1 ; i <= vector->Depth(); i++)
    {
      addrLoaded[i-1] = (addrRelative[i-1] == NODE_SUM) ?

                                    NODE_SUM : pVectorIDs[i-1] + addrRelative[i-1];
    }

  SumVector.Clear();
  vector->GetData( pRecord, addrLoaded, &SumVector);
}

inline DRS_VectorDataElem DRS_FormulaNodeObjectRelativeNode::Compute( const DRS_VectorData &rDataVector, uint iItem, uint32 lCurRecNum) const
{
  const DRS_Vector * vector = GetVector();
  if( ((int)iItem + iOffset) < 0 || ((int)iItem + iOffset) >= (int32)vector->PeriodCount())
    throw AT_Bounds_Err( *this, "Period", iItem + iOffset);
  return SumVector.GetDataElement( iItem + iOffset);
}
void DRS_FormulaNodeObjectRelativeNode::Serialise( uint iFormulaID, uint *pNodeID, byte *sSection, byte *sFile)
{
  DRS_FormulaNode::TypeClassStamp( DRS_FORMULANODE_OBJECT, DRS_FORMULANODE_OBJECT_RELATIVENODE, iFormulaID, pNodeID, sSection, sFile);

  byte sKey[32];
  //na_string key;
  const DRS_Vector * vector = GetVector();

  for( uint i = 1; i < vector->Depth(); i++)
    {
    wsprintfA( (LPSTR) sKey, "Formula%uNode%uDim%u", iFormulaID, *pNodeID, i);
    UTL_WritePrivateProfileInt( sSection, sKey, (uint32) addrRelative[i-1],sFile);
    }

  wsprintfA( (LPSTR) sKey, "Formula%uNode%uShift", iFormulaID, *pNodeID);
  UTL_WritePrivateProfileInt( sSection, sKey, iOffset,sFile);
}

//Dimension vector count
DRS_FormulaNodeObjectDimVectorCount::DRS_FormulaNodeObjectDimVectorCount( const DRS_Formula * f, uint iNewDim) :
    DRS_FormulaNodeObject(f), iDim(iNewDim)
{
  const DRS_Vector * vector = GetVector();
  if( iDim >= vector->DimCount()) throw AT_Bounds_Err( *this, "Vector dimension", iDim);

  addrLoaded = 0;
  iVectorCount = 0;
}
DRS_FormulaNodeObjectDimVectorCount::DRS_FormulaNodeObjectDimVectorCount( const DRS_Formula * f, uint *pNodeID, byte *sSection, byte *sFile) :
    DRS_FormulaNodeObject(f)
{
  byte sKey[32];

  const DRS_Vector * vector = GetVector();
  wsprintfA( (LPSTR) sKey, "Formula%uNode%uDim", GetFormulaID(), *pNodeID);
  iDim = GetPrivateProfileIntA( (LPCSTR) sSection, (LPCSTR) sKey, -1, (LPCSTR) sFile);
  if( iDim >= vector->DimCount()) throw AT_Bounds_Err( *this, "Vector dimension", iDim);


  addrLoaded = 0;
  iVectorCount = 0;
  (*pNodeID)++;
}

#pragma args_used
void DRS_FormulaNodeObjectDimVectorCount::LoadVectorAddress( DRS_RecordSet *pRecord, uint32 pVectorIDs[])
{
  //If no vectorIDs loaded

  const DRS_Vector * vector = GetVector();
  if( ! addrLoaded)
    {
    uint iDimCount = vector->DimCount();
    addrLoaded = new uint32[iDimCount];
    memcpy( addrLoaded, pVectorIDs, iDimCount * sizeof(uint32));
    iVectorCount = vector->Node(iDim, addrLoaded).GetVectorCount( &vector->Dim(iDim), addrLoaded);
    }
  //If vector address changes in dim range, reload sum for new dim range
  //Must also check for change in disparate items.
  else
    {
    if( memcmp( pVectorIDs, addrLoaded, (iDim + 1) * sizeof(uint32)))
      {
      memcpy( addrLoaded, pVectorIDs, vector->DimCount() * sizeof(uint32));
      iVectorCount = vector->Node(iDim, addrLoaded).GetVectorCount( &vector->Dim(iDim), addrLoaded);
      }
    else
      { //Check dispatate items
      uint iDimCount = vector->DimCount();
      for( uint i = iDim + 1; i < iDimCount; i++)
        if( !vector->Dim(i).IsCollective() && ( pVectorIDs[i] != addrLoaded[i]))
          {
          memcpy( addrLoaded, pVectorIDs, iDimCount * sizeof(uint32));
          iVectorCount = vector->Node(iDim, addrLoaded).GetVectorCount( &vector->Dim(iDim), addrLoaded);
          break;
          }
      }
    }
}

inline DRS_VectorDataElem DRS_FormulaNodeObjectDimVectorCount::Compute( const DRS_VectorData &rDataVector, uint iItem, uint32 lCurRecNum) const
{
  return DRS_VectorDataElem( iVectorCount, 1);
}
void DRS_FormulaNodeObjectDimVectorCount::Serialise( uint iFormulaID, uint *pNodeID, byte *sSection, byte *sFile)
{
  DRS_FormulaNode::TypeClassStamp( DRS_FORMULANODE_OBJECT, DRS_FORMULANODE_OBJECT_DIMVECTORCOUNT, iFormulaID, pNodeID, sSection, sFile);

  byte sKey[32];
  wsprintfA( (LPSTR) sKey, "Formula%uNode%uDim", iFormulaID, *pNodeID);
  UTL_WritePrivateProfileInt( sSection, sKey, (uint32)iDim,sFile);
  (*pNodeID)++;
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// DRS_FormulaNodeAction
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Base class
DRS_FormulaNodeAction::DRS_FormulaNodeAction(const DRS_Formula * f, uint *pNodeID, byte *sSection, byte *sFile)
  : DRS_FormulaNode(f), pLeftNode(0), pRightNode(0)
{
  (*pNodeID)++;

  //An action creates it's left then right nodes.
  pLeftNode = CreateNode( f, pNodeID, sSection, sFile);
  pRightNode = CreateNode( f, pNodeID, sSection, sFile);

}
DRS_FormulaNodeAction::~DRS_FormulaNodeAction()
{
  delete pLeftNode;
  delete pRightNode;
}
void DRS_FormulaNodeAction::AttachNodeLeft( DRS_FormulaNode *pNewNode)
{
  delete pLeftNode;
  pLeftNode = pNewNode;
}
void DRS_FormulaNodeAction::AttachNodeRight( DRS_FormulaNode *pNewNode)
{
  delete pRightNode;
  pRightNode = pNewNode;
}

DRS_FormulaNode * DRS_FormulaNodeAction::CreateNode(const DRS_Formula * f, uint *pNodeID, byte *sSection, byte *sFile)
{

  byte sKey[38];

  wsprintfA( (LPSTR) sKey, "Formula%uNode%uType", GetFormulaID(), *pNodeID);
  uint iNodeType = GetPrivateProfileIntA( (LPCSTR) sSection, (LPCSTR) sKey, -1, (LPCSTR) sFile);

  wsprintfA( (LPSTR) sKey, "Formula%uNode%uClass", GetFormulaID(), *pNodeID);
  uint iNodeTypeClass = GetPrivateProfileIntA( (LPCSTR) sSection, (LPCSTR) sKey, -1, (LPCSTR) sFile);

  switch( iNodeType)
    {
    case DRS_FORMULANODE_OBJECT:
      switch(iNodeTypeClass)
        {
        case DRS_FORMULANODE_OBJECT_VECTORQUANTILE:
          return( new DRS_FormulaNodeObjectVectorQuantile( f, pNodeID, sSection, sFile));
        case DRS_FORMULANODE_OBJECT_VECTORVAL:
          return( new DRS_FormulaNodeObjectVectorVal( f, pNodeID));
        case DRS_FORMULANODE_OBJECT_VECTORVALREL:
          return( new DRS_FormulaNodeObjectVectorValRel( f, pNodeID, sSection, sFile));
        case DRS_FORMULANODE_OBJECT_VECTORVALABS:
          return( new DRS_FormulaNodeObjectVectorValAbs( f, pNodeID, sSection, sFile));
        case DRS_FORMULANODE_OBJECT_INTEGER:
          return( new DRS_FormulaNodeObjectInteger( f, pNodeID, sSection, sFile));
        case DRS_FORMULANODE_OBJECT_RELATIVENODE:
          return( new DRS_FormulaNodeObjectRelativeNode( f, pNodeID, sSection, sFile));
        case DRS_FORMULANODE_OBJECT_DIMVECTORCOUNT:
          return( new DRS_FormulaNodeObjectDimVectorCount( f, pNodeID, sSection, sFile));
        default:
          throw AT_Bounds_Err( *this, sKey, iNodeTypeClass);
        }
    case DRS_FORMULANODE_ACTION:
      switch(iNodeTypeClass)
        {
        case DRS_FORMULANODE_ACTION_ADD:
          return( new DRS_FormulaNodeActionAdd( f, pNodeID, sSection, sFile));
        case DRS_FORMULANODE_ACTION_SUBTRACT:
          return( new DRS_FormulaNodeActionSubtract( f, pNodeID, sSection, sFile));
        case DRS_FORMULANODE_ACTION_MULTIPLY:
          return( new DRS_FormulaNodeActionMultiply( f, pNodeID, sSection, sFile));
        case DRS_FORMULANODE_ACTION_DIVIDE:
          return( new DRS_FormulaNodeActionDivide( f, pNodeID, sSection, sFile));
        default:
          throw AT_Bounds_Err( *this, sKey, iNodeTypeClass);
        }
    default:
      throw AT_Bounds_Err( uint, "Formula node type", iNodeType);
    }

}

inline void DRS_FormulaNodeAction::LoadRecord( )
{
  pLeftNode->LoadRecord();
  pRightNode->LoadRecord();
}

void DRS_FormulaNodeAction::LoadVectorAddress( DRS_RecordSet *pRecord, uint32 pVectorIDs[])
{
  pLeftNode->LoadVectorAddress(pRecord, pVectorIDs);
  pRightNode->LoadVectorAddress(pRecord, pVectorIDs);
}

//Action Add
inline DRS_VectorDataElem DRS_FormulaNodeActionAdd::Compute( const DRS_VectorData &rDataVector, uint iItem, uint32 lCurRecNum) const
{
  return( pLeftNode->Compute(rDataVector, iItem, lCurRecNum)
          + pRightNode->Compute( rDataVector, iItem, lCurRecNum));
}
void DRS_FormulaNodeActionAdd::Serialise( uint iFormulaID, uint *pNodeID, byte *sSection, byte *sFile)
{
  DRS_FormulaNode::TypeClassStamp( DRS_FORMULANODE_ACTION, DRS_FORMULANODE_ACTION_ADD, iFormulaID, pNodeID, sSection, sFile);
  (*pNodeID)++; //Any calc's BEFORE this!!
}

//Action subtract
DRS_FormulaNodeActionSubtract::DRS_FormulaNodeActionSubtract(const DRS_Formula * f) : DRS_FormulaNodeAction(f)
{
    UpdateMaxValue();
}

DRS_FormulaNodeActionSubtract::DRS_FormulaNodeActionSubtract( const DRS_Formula * f, uint *pNodeID, byte *sSection, byte *sFile)
    : DRS_FormulaNodeAction( f, pNodeID, sSection, sFile)
{
    UpdateMaxValue();
}

void DRS_FormulaNodeActionSubtract::UpdateMaxValue()
{
    DRS_Formula * ff = (DRS_Formula *)GetFormula();
    AT_Num v = ff->GetMaxValue();
    if(!v)
        {
        const DRS_Vector * vector = GetVector();
        uint m = vector->DataPrecisionMult();
        ff->SetMaxValue(MAX_VALUE * m);
        }
}

inline DRS_VectorDataElem DRS_FormulaNodeActionSubtract::Compute( const DRS_VectorData &rDataVector, uint iItem, uint32 lCurRecNum) const
{
  return(  pLeftNode->Compute(rDataVector, iItem, lCurRecNum)
            - pRightNode->Compute(rDataVector, iItem, lCurRecNum));
}
void DRS_FormulaNodeActionSubtract::Serialise( uint iFormulaID, uint *pNodeID, byte *sSection, byte *sFile)
{
  DRS_FormulaNode::TypeClassStamp( DRS_FORMULANODE_ACTION, DRS_FORMULANODE_ACTION_SUBTRACT, iFormulaID, pNodeID, sSection, sFile);
  (*pNodeID)++; //Any calc's BEFORE this!!
}
//Action Divide
inline DRS_VectorDataElem DRS_FormulaNodeActionDivide::Compute( const DRS_VectorData &rDataVector, uint iItem, uint32 lCurRecNum) const
{
  //Catch divide by 0
  DRS_VectorDataElem lDenom( pRightNode->Compute(rDataVector, iItem, lCurRecNum));
  return( lDenom.GetRawValue() == 0 ? lDenom :
                                      ( pLeftNode->Compute( rDataVector, iItem, lCurRecNum)/ lDenom));
}
void DRS_FormulaNodeActionDivide::Serialise( uint iFormulaID, uint *pNodeID, byte *sSection, byte *sFile)
{
  DRS_FormulaNode::TypeClassStamp( DRS_FORMULANODE_ACTION, DRS_FORMULANODE_ACTION_DIVIDE, iFormulaID, pNodeID, sSection, sFile);
  (*pNodeID)++; //Any calc's BEFORE this!!
}
//Action Multiply
inline DRS_VectorDataElem DRS_FormulaNodeActionMultiply::Compute( const DRS_VectorData &rDataVector, uint iItem, uint32 lCurRecNum) const
{
  return DRS_VectorDataElem( pLeftNode->Compute( rDataVector, iItem, lCurRecNum) *
                             pRightNode->Compute( rDataVector, iItem, lCurRecNum));
}
void DRS_FormulaNodeActionMultiply::Serialise( uint iFormulaID, uint *pNodeID, byte *sSection, byte *sFile)
{
  DRS_FormulaNode::TypeClassStamp( DRS_FORMULANODE_ACTION, DRS_FORMULANODE_ACTION_MULTIPLY, iFormulaID, pNodeID, sSection, sFile);
  (*pNodeID)++; //Any calc's BEFORE this!!
}


