// Change History
// 11/20/03 AV Development: BC6 conversion. const/none const
// 11/20/03 AV Development: BC6 conversion. Move IsValidPeriod from h to cpp

#ifndef _DRS_FORMULA_H_
#define _DRS_FORMULA_H_

#include <algorithm>
#include <vector>

#include "base/at_defs.h"
#include "xdart/x_at_data.h"

#include "drs/drs_data.h"

#include "drs/drs_type.h"

#define MAX_VALUE 10000

///////////////////////////////////////////////////////////////////////////////////////////////////////////
// Vector Formulas
///////////////////////////////////////////////////////////////////////////////////////////////////////////
//A vector formula computes a data vector based on a formula.
//A formula has the root node of its formula, which is a hierarchically linked collection of DRS_FormulaNodes.
//FormulaNodes are Objects or Actions of the types listed here:

typedef enum { DRS_FORMULANODE_OBJECT, DRS_FORMULANODE_ACTION} DRS_FORMULANODE;

typedef
enum { DRS_FORMULANODE_OBJECT_VECTORVAL,
       DRS_FORMULANODE_OBJECT_VECTORVALREL,
       DRS_FORMULANODE_OBJECT_VECTORVALABS,
       DRS_FORMULANODE_OBJECT_INTEGER,
       DRS_FORMULANODE_OBJECT_REAL,    //Not yet implemented
       DRS_FORMULANODE_OBJECT_RELATIVENODE,
       DRS_FORMULANODE_OBJECT_DIMVECTORCOUNT,
       DRS_FORMULANODE_OBJECT_VECTORQUANTILE,
       DRS_FORMULANODE_OBJECT_COUNT
     } DRS_FORMULANODE_OBJECTTYPE;
typedef
enum { DRS_FORMULANODE_ACTION_ADD,
       DRS_FORMULANODE_ACTION_SUBTRACT,
       DRS_FORMULANODE_ACTION_MULTIPLY,
       DRS_FORMULANODE_ACTION_DIVIDE,
       DRS_FORMULANODE_ACTION_COUNT
     } DRS_FORMULANODE_ACTIONTYPE;

class DRS_Vector;
class DRS_RecordSet;
class DRS_VectorData;
class DRS_VectorDataElem;
class DRS_Formula;

class DRS_FormulaNode  //Abstract base class
{
  protected:
    const DRS_Formula * Formula;

  protected:
    const DRS_Vector * GetVector() const;
    uint GetFormulaID() const;
    const DRS_Formula * GetFormula() const { return Formula; }
             
  public:
  DRS_FormulaNode(const DRS_Formula * f) : Formula(f) {}
  virtual ~DRS_FormulaNode() {};

  virtual void LoadRecord() {};
  virtual void LoadVectorAddress( DRS_RecordSet *pRecord, uint32 pVectorIDs[]) {};

  //void Compute( record parAms); // Compute for record
  //void Compute( report parAms); // Compute for report.

  virtual DRS_VectorDataElem Compute( const DRS_VectorData &rDataVector, uint iItem, uint32 lCurRecNum) const = 0;
  void          TypeClassStamp( uint iType, uint iTypeClass, uint iFormulaID, uint *pNodeID, byte *sSection, byte *sFile);
  virtual void  Serialise( uint iFormulaID, uint *pNodeID, byte *sSection, byte *sFile) = 0;

};

class DRS_FormulaNodeObject : public DRS_FormulaNode
{
  public:
  DRS_FormulaNodeObject(const DRS_Formula * f) : DRS_FormulaNode(f) {}
  virtual ~DRS_FormulaNodeObject() {};
  virtual DRS_VectorDataElem Compute( const DRS_VectorData &rDataVector, uint iItem, uint32 lCurRecNum) const = 0;
  virtual void  Serialise( uint iFormulaID, uint *pNodeID, byte *sSection, byte *sFile) = 0;
};

class DRS_FormulaNodeAction : public DRS_FormulaNode
{
  public:
  DRS_FormulaNodeAction(const DRS_Formula * f) : DRS_FormulaNode(f) ,
    pLeftNode(0), pRightNode(0) {};
  DRS_FormulaNodeAction(const DRS_Formula * f , uint *pNodeID, byte *sSection, byte *sFile);
  virtual ~DRS_FormulaNodeAction();

  void          AttachNodeLeft( DRS_FormulaNode *pNewNode);
  void          AttachNodeRight( DRS_FormulaNode *pNewNode);
  void          LoadRecord();
  void          LoadVectorAddress( DRS_RecordSet *pRecord, uint32 pVectorIDs[]);
  virtual DRS_VectorDataElem Compute( const DRS_VectorData &rDataVector, uint iItem, uint32 lCurRecNum) const = 0;
  virtual void  Serialise( uint iFormulaID, uint *pNodeID, byte *sSection, byte *sFile) = 0;

  //void AttachNodeLeft( DRS_FormulaNode *pNodeLeft);
  //void AttachNodeRight( DRS_FormulaNode *pNodeLeft);

  protected:
  DRS_FormulaNode * pLeftNode;
  DRS_FormulaNode * pRightNode;
  private:
  DRS_FormulaNode * CreateNode(const DRS_Formula * f, uint *pNodeID, byte *sSection, byte *sFile);
};


///////////////////////////////////////////////////////////////////////////////////////////////////////////
// FormulaNode Objects
///////////////////////////////////////////////////////////////////////////////////////////////////////////
class AT_Quantile_List;

class DRS_FormulaNodeObjectVectorQuantile : public DRS_FormulaNodeObject
{
  public:
  //From file
  DRS_FormulaNodeObjectVectorQuantile(const DRS_Formula * f) : DRS_FormulaNodeObject(f) {}
  DRS_FormulaNodeObjectVectorQuantile( const DRS_Formula * f,
                                          uint *pNodeID,
                                          byte *sSection,
                                          byte *sFile);
  virtual ~DRS_FormulaNodeObjectVectorQuantile() {};

  inline void LoadRecord();
  void LoadVectorAddress( DRS_RecordSet *pRecord, uint32 pVectorAddress[]);

  inline DRS_VectorDataElem Compute( const DRS_VectorData &rDataVector, uint iItem, uint32 lCurRecNum) const;
  void Serialise( uint iFormulaID, uint *pNodeID, byte *sSection, byte *sFile);

  private:
  
  uint iQuantileIdx;            //Vectors may have several quantile files with different quantile_counts
  uint32 iBaseIndexID;          //Base Index_id/quantile_fldnum of currently referenced VectorAddress ( no period)
  AT_Quantile_List const *pQuantile; //Current quantile

  uint mult;                    //convert to implied decimal
  uint normal;                  //normalize rx value
};

class DRS_FormulaNodeObjectVectorVal : public DRS_FormulaNodeObject
{
  public:
  DRS_FormulaNodeObjectVectorVal(const DRS_Formula * f); //New
  DRS_FormulaNodeObjectVectorVal( const DRS_Formula * f, uint *pNodeID) :
    DRS_FormulaNodeObject(f)
    { (*pNodeID)++;}; //From file
  virtual ~DRS_FormulaNodeObjectVectorVal() {};

  inline DRS_VectorDataElem Compute( const DRS_VectorData &rDataVector, uint iItem, uint32 lCurRecNum) const;
  void Serialise( uint iFormulaID, uint *pNodeID, byte *sSection, byte *sFile);
};
class DRS_FormulaNodeObjectVectorValRel : public DRS_FormulaNodeObject
{
  public:
  DRS_FormulaNodeObjectVectorValRel( const DRS_Formula * f, int iNewOffset);
  DRS_FormulaNodeObjectVectorValRel( const DRS_Formula * f, uint *pNodeID, byte *sSection, byte *sFile); //Deserialise & init
  virtual ~DRS_FormulaNodeObjectVectorValRel() {};

  inline DRS_VectorDataElem Compute( const DRS_VectorData &rDataVector, uint iItem, uint32 lCurRecNum) const;
  void Serialise( uint iFormulaID, uint *pNodeID, byte *sSection, byte *sFile);

  private:
  int iOffset;  //Relative Offset from current item
  uint iPeriodCount;
};
class DRS_FormulaNodeObjectVectorValAbs : public DRS_FormulaNodeObject
{
  public:
  DRS_FormulaNodeObjectVectorValAbs( const DRS_Formula * f, uint iNewPeriod) :
    DRS_FormulaNodeObject(f),
    iPeriod(iNewPeriod) {};
  DRS_FormulaNodeObjectVectorValAbs( const DRS_Formula * f, uint *pNodeID, byte *sSection, byte *sFile);
  virtual ~DRS_FormulaNodeObjectVectorValAbs() {};

  inline DRS_VectorDataElem Compute( const DRS_VectorData &rDataVector, uint iItem, uint32 lCurRecNum) const;
  void Serialise( uint iFormulaID, uint *pNodeID, byte *sSection, byte *sFile);

  private:

  uint iPeriod; //Absolute vector item
};
class DRS_FormulaNodeObjectInteger : public DRS_FormulaNodeObject
{
  public:
  DRS_FormulaNodeObjectInteger( const DRS_Formula * f, int32 lNewInt) :
    DRS_FormulaNodeObject(f), lVal(lNewInt) {};
  DRS_FormulaNodeObjectInteger( const DRS_Formula * f, uint *pNodeID, byte *sSection, byte *sFile);
  virtual ~DRS_FormulaNodeObjectInteger() {};

  inline DRS_VectorDataElem Compute( const DRS_VectorData &rDataVector, uint iItem, uint32 lCurRecNum) const;
  void Serialise( uint iFormulaID, uint *pNodeID, byte *sSection, byte *sFile);

  private:
  int32 lVal;
};


class DRS_FormulaNodeObjectRelativeNode : public DRS_FormulaNodeObject
{
  public:
  //DRS_FormulaNodeObjectRelativeNode( DRS_Vector const &rParentVector, uint iNewDim, int iNewOffset);
  DRS_FormulaNodeObjectRelativeNode( const DRS_Formula * f, uint *pNodeID, byte *sSection, byte *sFile);
  virtual ~DRS_FormulaNodeObjectRelativeNode();

  inline void LoadRecord();
  void LoadVectorAddress( DRS_RecordSet *pRecord, uint32 pVectorIDs[]);

  inline DRS_VectorDataElem Compute( const DRS_VectorData &rDataVector, uint iItem, uint32 lCurRecNum) const;
  void Serialise( uint iFormulaID, uint *pNodeID, byte *sSection, byte *sFile);

  private:

  auto_array<uint32> addrRelative;  //Relative address holds -1 for total, x for offset, default 0.
  uint32 * addrLoaded;  //Storage of current valid dim vector address
  DRS_VectorData SumVector;         //Storage of sum vector for current dim address
  int iOffset;                //Relative RelativeNode offset - def = 0
};

class DRS_FormulaNodeObjectDimVectorCount : public DRS_FormulaNodeObject
{
  public:
  DRS_FormulaNodeObjectDimVectorCount( const DRS_Formula * f, uint iNewDim);
  DRS_FormulaNodeObjectDimVectorCount( const DRS_Formula * f, uint *pNodeID, byte *sSection, byte *sFile);
  virtual ~DRS_FormulaNodeObjectDimVectorCount() {};

  void LoadVectorAddress( DRS_RecordSet *pRecord, uint32 pVectorIDs[]);

  inline DRS_VectorDataElem Compute( const DRS_VectorData &rDataVector, uint iItem, uint32 lCurRecNum) const;
  void Serialise( uint iFormulaID, uint *pNodeID, byte *sSection, byte *sFile);

  private:
  uint iDim;                  //Dimension for calc
  uint32 * addrLoaded;  //Storage of current valid dim vector address
  uint iVectorCount;
};

///////////////////////////////////////////////////////////////////////////////////////////////////////////
// FormulaNode Actions
///////////////////////////////////////////////////////////////////////////////////////////////////////////
class DRS_FormulaNodeActionAdd : public DRS_FormulaNodeAction
{
  public:
  DRS_FormulaNodeActionAdd(const DRS_Formula * f) : DRS_FormulaNodeAction(f) {};
  DRS_FormulaNodeActionAdd( const DRS_Formula * f, uint *pNodeID, byte *sSection, byte *sFile)
    : DRS_FormulaNodeAction( f, pNodeID, sSection, sFile)
    {};

  virtual ~DRS_FormulaNodeActionAdd() {};

  inline DRS_VectorDataElem Compute( const DRS_VectorData &rDataVector, uint iItem, uint32 lCurRecNum) const;
  void Serialise( uint iFormulaID, uint *pNodeID, byte *sSection, byte *sFile);
};
class DRS_FormulaNodeActionSubtract : public DRS_FormulaNodeAction
{
  protected:
  void UpdateMaxValue();

  public:
  DRS_FormulaNodeActionSubtract(const DRS_Formula * f);
  DRS_FormulaNodeActionSubtract( const DRS_Formula * f, uint *pNodeID, byte *sSection, byte *sFile);
  virtual ~DRS_FormulaNodeActionSubtract() {};

  inline DRS_VectorDataElem Compute( const DRS_VectorData &rDataVector, uint iItem, uint32 lCurRecNum) const;
  void Serialise( uint iFormulaID, uint *pNodeID, byte *sSection, byte *sFile);
};
class DRS_FormulaNodeActionMultiply : public DRS_FormulaNodeAction
{
  public:
  DRS_FormulaNodeActionMultiply(const DRS_Formula * f) : DRS_FormulaNodeAction(f) {} ;
  DRS_FormulaNodeActionMultiply( const DRS_Formula * f, uint *pNodeID, byte *sSection, byte *sFile)
    : DRS_FormulaNodeAction( f, pNodeID, sSection, sFile)
    {};
  virtual ~DRS_FormulaNodeActionMultiply() {};

  inline DRS_VectorDataElem Compute( const DRS_VectorData &rDataVector, uint iItem, uint32 lCurRecNum) const;
  void Serialise( uint iFormulaID, uint *pNodeID, byte *sSection, byte *sFile);
};
class DRS_FormulaNodeActionDivide : public DRS_FormulaNodeAction
{
  public:
  DRS_FormulaNodeActionDivide(const DRS_Formula * f) : DRS_FormulaNodeAction(f) {};
  DRS_FormulaNodeActionDivide( const DRS_Formula * f, uint *pNodeID, byte *sSection, byte *sFile)
    : DRS_FormulaNodeAction( f, pNodeID, sSection, sFile)
    {};
  virtual ~DRS_FormulaNodeActionDivide() {};
  
  inline DRS_VectorDataElem Compute( const DRS_VectorData &rDataVector, uint iItem, uint32 lCurRecNum) const;
  void Serialise( uint iFormulaID, uint *pNodeID, byte *sSection, byte *sFile);
};

class DRS_VectorDef;
class DRS_Formula
{
  friend DRS_VectorDef;
  
  public:
  DRS_Formula( DRS_Vector const &rVector); //Creates VectorVal root node
  DRS_Formula( DRS_Vector const &rVector, uint iID, byte *sSection, byte *sFile);
  ~DRS_Formula();

  inline DRS_Descriptor const & Descriptor() const {return Descr;};

  void Serialise( uint iFormulaID, byte *sSection, byte *sFile);
  void GetVector( DRS_RecordSet &rRecordSet, uint32 pVectorAddress[], DRS_VectorDataElem* pResults[], bool *pIsAvail);
  bool IsValidPeriod( uint iPeriod) const;

  inline bool IsReport() const { return isReport;};
  inline bool IsMutable() const { return isMutable;};
  inline bool IsGroupable() const { return isGroupable;};
  inline bool IsSumable() const { return isSumable;};  
  inline bool IsQuantile() const { return isQuantile;};
  inline bool isValid() const { return !!pFormulaRoot.get(); }

  uint GetDataSize() const { return IsQuantile() ? 1 : DRS_VectorData::GetDataSize(); }
  uint GetDataTextSize() const { return IsQuantile() ? 2 : TEXT_DATA_SIZE; }

  uint GetID() const { return ID; }

  AT_Num GetMaxValue() const { return MaxValue; }
  void SetMaxValue(AT_Num num) { MaxValue = num; }  

  const DRS_Vector * Vector() const;

  private:
  DRS_VectorData            VectorRawData;
  uint                      iDatabaseID; //For retrieval of correct rec from DRS_RecordSet
  DRS_DescriptorSerialize   Descr;
  uint32                    lCurRecNum;
  uint                      ID;
  auto_ptr<DRS_FormulaNode> pFormulaRoot;

  AT_Num                   MaxValue;
  std::vector<int>         vIllegalPeriods; //Illegal periods return 0

  bool isReport;        // Not all formulas are available for reports, e.g. Quantiles.
  bool isMutable;       // In reporting, can formula be applied across report tree?
  bool isQuantile;      // Is quantile
  bool isSumable;       // Allow aggregation
  bool isGroupable;     // Can be used as break field
  
  //NOTE: Reports create ReportFormulas from vector formulas used for calcs.
};


#endif
