#ifndef _DRS_TREE_H_
#define _DRS_TREE_H_

///////////////////////////////////////////////////////////////////////////////
//DRS_Tree
///////////////////////////////////////////////////////////////////////////////

/*         
A DRS_Tree defines a hierarchical nodal system. It is
used primarily for managing hierarchically addressed data vectors and reports.
It is also for D_Formulas (computations on data).

The nodes in the hierarchy must be derived from D_TreeNode.
The dimensions in the hierarchy must be derived from D_TreeDim.

*/

#include <vector>
#include "drs_setting.h"

template <class tDim>
class DRS_TreeDim
{
  public:
  //Constructors
  DRS_TreeDim() : iDepth(0), pChild(0), iMaxLength(1) {};
  DRS_TreeDim( DRS_Descriptor const &rDescr) : iDepth(0), pChild(0), iMaxLength(1), Descr(rDescr) {};

  //From file
  DRS_TreeDim( byte *sSection, uint iID, byte *sFile) : iDepth(0), pChild(0)
    {
    DRS_FileKey key;
    DRS_FileSetting setting;

    wsprintfA( (char *)key, "Dim%uDescr", iID);
    GetPrivateProfileStringA( (char *)sSection, (char *)key, "", (char *)setting, sizeof(DRS_FileSetting), (char *)sFile);
    Descr.Extract( setting);

    wsprintfA( (char *)key, "Dim%uLength", iID);
    iMaxLength = GetPrivateProfileIntA( (char *)sSection, (char *)key, -1L, (char *)sFile);
    if(iMaxLength == -1L) //Error
      throw DRSERR_MissingConfig( *this, sFile, sSection, key);
    };

  //Copy constructor
  DRS_TreeDim( DRS_TreeDim<tDim> const &rDim) : iDepth(rDim.Dim()), pChild(0)
    {
    //if( rDim.GetChild())
    //  pChild = new tDim( *rDim.GetChild());
    };

  DRS_TreeDim::~DRS_TreeDim( )
    {
    delete pChild;
    };

  /*
  tDim const & Child() const
                  { return *pChild;};
  */
  tDim const * GetChild() const
  {
    //if( !pChild) throw AT_Bounds_Err( *this, (AT_String)"Dimension Child", iDepth);
    return( pChild);
  };
  inline uint                   Dim() const {return iDepth;};
  inline uint                   Depth() const {return iDepth;};
  inline uint                   MaxLength() const { return iMaxLength;};
  inline DRS_Descriptor const & Descriptor() const { return Descr;};

  void AttachChild( tDim * pDim)
    {
    if( pChild) pChild->AttachChild( pDim);
    else
      {
      pChild = pDim;
      pChild->SetDim( iDepth + 1);
      }
    };


  protected:
  //Used by Parent dim ONLY!
  inline void SetDim( uint i) {iDepth = i;};

  private:
  tDim                   *pChild;
  uint                   iDepth;

  //REMOVE: Vectors only.
  DRS_DescriptorSerialize  Descr;
  uint                     iMaxLength; //Longest occurring sibling count.
};

template <class tNode>
class DRS_TreeNode
{
  public:
  DRS_TreeNode()
    {};
  //Copy Constructor
  DRS_TreeNode( DRS_TreeNode const &rNode);

  //Destructor
  virtual ~DRS_TreeNode()
            {
              for( uint i = vChildren.size(); i > 0; --i) delete vChildren[i-1];
            };

  //Family planning
  inline tNode * InheretChild( tNode *pChild)
            {
              vChildren.push_back( pChild);
              return pChild;
            };
  virtual uint32 AssertChild( uint32 lIndex) const
            {
                if( lIndex >= vChildren.size())
                  throw AT_Bounds_Err( *this, (AT_String)"Node child", lIndex);

                return lIndex;
            };
  inline uint ChildCount() const {return vChildren.size();}
  inline virtual tNode * GetChild( uint32 lID) const
            {
              lID = AssertChild(lID);
              return( vChildren[lID]);
            };

  protected:
  std::vector<tNode *> vChildren;
};

template <class tNode>
DRS_TreeNode<tNode>::DRS_TreeNode( DRS_TreeNode<tNode> const &rNode)
            {
            //Copy children
            uint iCount = rNode.ChildCount();
            for( uint i = 0; i < iCount; i++)
               InheretChild( new tNode(*rNode.GetChild(i)));
            }

//DRS_TreeNodeMapped
//Discontiguous nodes whereby the nodes ID is stored and referenced,
//i.e. does not relate to its position
//e.g. Vector nodes in reports.
template <class tNode>
class DRS_TreeNodeMapped : public DRS_TreeNode<tNode>
{
  public:
  DRS_TreeNodeMapped() : iID(0)
    {};
  DRS_TreeNodeMapped( uint32 id) : iID(id)
    {};

  //Copy constructor
  DRS_TreeNodeMapped( DRS_TreeNodeMapped<tNode> const &rNode) : iID(rNode.ID()), DRS_TreeNode<tNode>()
    {
    //Must copy children. DRS_TreeNode accesses children by location, not ID
            //Copy children
            /*
            uint iCount = rNode.GetChildCount();
            for( uint i = 0; i < iCount; i++)
               InheretChild( new tNode(*rNode.GetChildAbs(i)));
            */
    };

  inline uint32 ID() const { return iID;};
  tNode * GetChild( uint32 lID) const
            {
              for( std::vector<tNode *>::const_iterator iter = vChildren.begin();
                   iter != vChildren.end(); iter++)
                if( (*iter)->ID() == lID) return( *iter);

              throw AT_Bounds_Err( *this, "Child", lID);
            };

  tNode * GetChildNoRaise( uint32 lID) const
            {
              for( std::vector<tNode *>::const_iterator iter = vChildren.begin();
                   iter != vChildren.end(); iter ++)
                if( (*iter)->ID() == lID) return( *iter);

              return(0);
            };
  inline tNode * GetChildAbs( uint32 iIdx) const
                      { return DRS_TreeNode<tNode>::GetChild(iIdx);};

  private:
  uint32 iID;
};

//Note: Tree can't be public TreeNode and Public TreeDim because ...

template < class tNode, class tDim>
class DRS_Tree
{
  public:
  //Contructors
  DRS_Tree() : iDepth(0)
    {};

  virtual ~DRS_Tree()
    {};
    
  //DRS_Tree() : pRootNode(0), pRootDim(0) {};

  /*
  //Create from file
  //DRS_Tree( DRS_Setting thisfile, DRS_Setting thissection) : dimRoot( thisfile, thissection), nodeRoot( thisfile, thissection)
  DRS_Tree( DRS_Setting thisfile, DRS_Setting thissection) : pRootDim( new tDim( thisfile, thissection)), pRootNode( new tNode( thisfile, thissection))
    {};
    {


    wsprintf( key, "Dim%uDescr", iID);
    GetPrivateProfileString( sSection, key, "", setting, sizeof(DRS_FileSetting), sFile);
    Descr.Extract( setting);

    wsprintf( key, "Dim%uLength", iID);
    iMaxLength = GetPrivateProfileInt( sSection, key, -1, sFile);
    if(iMaxLength == (uint)-1) //Error
      throw AT_Corrupt_Err( (AT_String)"", (AT_String)key);


    }
  */
  //Copy contructor
  //DRS_Tree( DRS_Tree<tNode, tDim> const &rHierarchy) : pRootDim(0), pRootNode(0)
  /*
  DRS_Tree( DRS_Tree<tNode, tDim> const &rHierarchy)
            {
            if( rHierarchy.GetRootDim())
              pRootDim = new tDim( *rHierarchy.GetRootDim());

            if( rHierarchy.GetRootNode())
              pRootNode = new tNode( *rHierarchy.GetRootNode());
            };
  */

  tNode const & Node( uint iDim, uint32 *pAddress) const
            {
              tNode const *pNode = pRootNode.get();
              for( uint i = 0; i < iDim; pNode = pNode->GetChild( pAddress[i++]));
              return( *pNode);
            };
  tDim const & Dim( uint iDim) const
            {
              tDim const * pDim = pRootDim.get();
              for( uint i = 0; pDim && (i < iDim); i++, pDim = pDim->GetChild());
              if( !pDim) throw AT_Bounds_Err( *this, "Dim", iDim);
              return( *pDim);
            };

  inline tNode const * GetRootNode() const {return pRootNode.get();};
  inline tDim const * GetRootDim() const {return pRootDim.get();};

  inline tNode const & Root() const {return *pRootNode.get();};
  inline tNode& Root() {return *pRootNode.get();};

  inline tDim const &  RootDim() const {return *pRootDim.get();};

  inline uint DimCount() const {return iDepth + 1;};

  inline uint Depth() const    {return iDepth;};

  void Clear() //Clear tree. Reset root with new blank.
            {
            pRootNode.reset( new tNode );
            };

  protected:

  void AttachDim( tDim *pDim)
            {
              if( !pRootDim.get()) pRootDim.reset( pDim);
              else
                {
                pRootDim->AttachChild( pDim);
                iDepth++;
                }
            };

  void AttachNode( tNode *pNode, uint iDim = 0)
            {
              if( ! pRootNode.get()) //No root.
                {
                AT_Assert( ! iDim, *this, "Dim");
                pRootNode.reset( pNode);
                }
              else
                {
                //Only one root!
                AT_Assert( iDim, *this, "Dim");

                //Add node as sibling in last child of each dim.
                //Scan to lastparent dim, getting last sibling in each dim and attach node as child
                tNode *pCurNode = pRootNode.get();
                for( uint i = 1; i < iDim; i++)
                  pCurNode = pCurNode->GetChild( pCurNode->ChildCount() - 1);

                pCurNode->InheretChild( pNode);
                }
            };

  private:
  uint             iDepth;
  auto_ptr<tDim>   pRootDim;
  auto_ptr<tNode>  pRootNode;
};



#endif
