/*
 *
 *  Copyright (C) 2000-2014, OFFIS e.V.
 *  All rights reserved.  See COPYRIGHT file for details.
 *
 *  This software and supporting documentation were developed by
 *
 *    OFFIS e.V.
 *    R&D Division Health
 *    Escherweg 2
 *    D-26121 Oldenburg, Germany
 *
 *
 *  Module: dcmsr
 *
 *  Author: Joerg Riesmeier
 *
 *  Purpose:
 *    classes: DSRTreeNode, DSRTree
 *
 */


#ifndef DSRTREE_H
#define DSRTREE_H

#include "dcmtk/config/osconfig.h"   /* make sure OS specific configuration is included first */

#include "dcmtk/dcmsr/dsrtypes.h"
#include "dcmtk/dcmsr/dsrtncsr.h"


/*-----------------------*
 *  forward declaration  *
 *-----------------------*/

template <typename T> class DSRTree;


/*---------------------*
 *  class declaration  *
 *---------------------*/

/** Class for tree nodes
 */
class DCMTK_DCMSR_EXPORT DSRTreeNode
  : protected DSRTypes
{
    // allow direct access to member variables
    friend class DSRTree<DSRTreeNode>;
    friend class DSRTree<DSRDocumentTreeNode>;

  public:

    /** default constructor
     */
    DSRTreeNode()
      : Prev(NULL),
        Next(NULL),
        Down(NULL),
        Ident(IdentCounter++)   // tbc: is this MT-safe?
    {
    }

    /** destructor
     */
    virtual ~DSRTreeNode()
    {
    }

    /** clone this tree node.
     *  Actually, a new tree node is created since the pointer members are not needed.
     ** @return copy of this tree node
     */
    virtual DSRTreeNode *clone() const
    {
        return new DSRTreeNode;
    }

    /** get pointer to previous tree node
     ** @return pointer to previous tree node (might be NULL)
     */
    inline DSRTreeNode *getPrev() const
    {
        return Prev;
    }

    /** get pointer to next tree node
     ** @return pointer to next tree node (might be NULL)
     */
    inline DSRTreeNode *getNext() const
    {
        return Next;
    }

    /** get pointer to first child node
     ** @return pointer to first child node (might be NULL)
     */
    inline DSRTreeNode *getDown() const
    {
        return Down;
    }

    /** get unique identifier of this node
     ** @return unique identifier of this node
     */
    inline size_t getIdent() const
    {
        return Ident;
    }


  protected:

    /** get current value of global node counter
     ** @return current value of global node counter
     */
    inline static size_t getIdentCounter()
    {
        return IdentCounter;
    }

    /// pointer to previous tree node (if any)
    DSRTreeNode *Prev;
    /// pointer to next tree node (if any)
    DSRTreeNode *Next;
    /// pointer to first child node (if any)
    DSRTreeNode *Down;


  private:

    /// unique identifier (created automatically)
    const size_t Ident;

    /// global counter used to create the unique identifiers
    static size_t IdentCounter;


 // --- declaration of copy constructor and assignment operator

    DSRTreeNode(const DSRTreeNode &);
    DSRTreeNode &operator=(const DSRTreeNode &);
};


/** Class managing a tree of nodes
 */
template<typename T = DSRTreeNode> class DSRTree
  : public DSRTreeNodeCursor<T>,
    protected DSRTypes
{

  public:

    /** default constructor
     */
    DSRTree();

    /** copy constructor.
     *  Please note that the internal cursor is not copied but reset, i.e. set to the
     *  root node.
     ** @param  tree  tree to be copied
     */
    DSRTree(const DSRTree<T> &tree);

    /** destructor
     */
    virtual ~DSRTree();

    /** clear all member variables
     */
    virtual void clear();

    /** check whether tree has any nodes
     ** @return OFTrue if tree is empty, OFFalse otherwise
     */
    OFBool isEmpty() const;

    /** count number of nodes in the tree.
     *  This method iterates over all nodes that are stored in the tree.
     *  @return number of nodes, 0 if tree is empty
     */
    size_t countNodes() const;

    /** get ID of the next node to be created.
     *  The node ID uniquely identifies a content item in the document tree.
     ** @return ID of the next node to be created (should never be 0)
     */
    size_t getNextNodeID() const;

    /** set internal cursor to root node
     ** @return ID of root node if successful, 0 otherwise
     */
    size_t gotoRoot();

    /** set internal cursor to specified node
     ** @param  searchID       ID of the node to set the cursor to
     *  @param  startFromRoot  flag indicating whether to start from the root node
     *                         or the current one
     ** @return ID of the new current node if successful, 0 otherwise
     */
    size_t gotoNode(const size_t searchID,
                    const OFBool startFromRoot = OFTrue);

    /** set internal cursor to specified node
     ** @param  reference      position string of the node to set the cursor to.
     *                         (the format is e.g. "1.2.3" for the third child of the
     *                         second child of the first node - see DSRTreeNodeCursor).
     *  @param  startFromRoot  flag indicating whether to start from the root node
     *                         or the current one
     ** @return ID of the new current node if successful, 0 otherwise
     */
    size_t gotoNode(const OFString &reference,
                    const OFBool startFromRoot = OFTrue);

    /** add new node to the current one.
     *  Please note that no copy of the given node is created.  Therefore, the node
     *  should be created with new() - do not use a reference to a local variable.
     *  If the node could be added successfully the cursor is set to it automatically.
     ** @param  node     pointer to the new node to be added
     *  @param  addMode  flag specifying at which position to add the new node.
     *                   Possible values: DSRTypes::AM_afterCurrent,
     *                   DSRTypes::AM_beforeCurrent, DSRTypes::AM_belowCurrent)
     ** @return ID of the new added node if successful, 0 otherwise
     */
    virtual size_t addNode(T *node,
                           const E_AddMode addMode = AM_afterCurrent);

    /** extract current node from tree.
     *  Please note that not only the specified node but also all of its child nodes are
     *  extracted from the tree.  The cursor is set automatically to a new valid position.
     ** @return pointer to extracted node, NULL in case of error (or the tree was empty)
     */
    virtual T *extractNode();

    /** remove current node from tree.
     *  Please note that not only the specified node but also all of its child nodes are
     *  removed from the tree and then deleted.  The cursor is set automatically to a new
     *  valid position.
     ** @return ID of the node which became the current one after deletion, 0 if an error
     *          occurred or the tree is now empty.
     */
    virtual size_t removeNode();

    /** extract a subtree i.e.\ a fragment from this tree.
     *  The subtree is specified by the current node, which becomes the root of the subtree.
     ** @return pointer to the extracted subtree, NULL in case of error
     */
    virtual DSRTree<T> *extractSubTree();

    /** clone a subtree i.e.\ a fragment of this tree.
     *  The cloning starts with the current node and ends with the given node.
     ** @param  stopAfterNodeID  ID of the node after which the cloning should stop.
     *                           By default (0), the process ends after cloning the
     *                           current node with all of its child nodes (if any).
     ** @return pointer to a copy of the specified subtree, NULL in case of error
     */
    virtual DSRTree<T> *cloneSubTree(const size_t stopAfterNodeID = 0) const;


  protected:

    /** special constructor that receives a pointer to the root node.
     *  Please note that the 'rootNode' and the associated tree is not copied!
     ** @param  rootNode  pointer to the root node of the "new" tree
     */
    DSRTree(T *rootNode);

    /** special copy constructor that clones a particular subtree only
     ** @param  startCursor      first node of the subtree to be copied
     *  @param  stopAfterNodeID  ID of the node after which the cloning should stop
     */
    DSRTree(const DSRTreeNodeCursor<T> &startCursor,
            size_t stopAfterNodeID);

    /** get pointer to root node
     ** @return pointer to root node, might be NULL (empty tree)
     */
    virtual T *getRoot() const;

    /** get pointer to root node and "forget" the internal reference to this node.
     *  In other words: after calling this method, the stored tree will be empty.
     *  This also means that the caller is responsible for deleting the allocated memory.
     ** @return pointer to root node, might be NULL (empty tree)
     */
    virtual T* getAndRemoveRootNode();


  private:

    /// pointer to the root tree node
    T *RootNode;


 // --- declaration of assignment operator

    DSRTree &operator=(const DSRTree<T> &);
};


/*------------------*
 *  implementation  *
 *------------------*/

template<typename T>
DSRTree<T>::DSRTree()
  : DSRTreeNodeCursor<T>(),
    RootNode(NULL)
{
}


template<typename T>
DSRTree<T>::DSRTree(const DSRTree<T> &tree)
  : DSRTreeNodeCursor<T>(),
    RootNode(NULL)
{
    if (!tree.isEmpty())
    {
        E_AddMode addMode = AM_afterCurrent;
        T *newNode = NULL;
        T *nodeCursor = tree.getRoot();
        OFStack<T *> nodeCursorStack;
        /* perform a "deep search", just like DSRTreeNodeCursor<>::iterate() */
        while (nodeCursor != NULL)
        {
            /* clone current node and add it to the tree */
            if (addNode(newNode = nodeCursor->clone(), addMode) == 0)
            {
                /* failed to add node, so delete it and exit the loop */
                delete newNode;
                break;
            }
            /* then goto to the next node to be copied */
            if (nodeCursor->Down != NULL)
            {
                /* go one level down to the first child node */
                nodeCursorStack.push(nodeCursor);
                nodeCursor = nodeCursor->getDown();
                addMode = AM_belowCurrent;
            }
            else if (nodeCursor->Next != NULL)
            {
                /* proceed with the following sibling */
                nodeCursor = nodeCursor->getNext();
                addMode = AM_afterCurrent;
            } else {
                /* check whether there are any siblings on higher levels */
                do {
                    if (!nodeCursorStack.empty())
                    {
                        nodeCursor = nodeCursorStack.top();
                        nodeCursorStack.pop();
                        this->goUp();
                    } else
                        nodeCursor = NULL;
                } while ((nodeCursor != NULL) && (nodeCursor->Next == NULL));
                if (nodeCursor != NULL)
                {
                    nodeCursor = nodeCursor->getNext();
                    addMode = AM_afterCurrent;
                }
            }
        }
        /* initialize the cursor */
        gotoRoot();
    }
}


template<typename T>
DSRTree<T>::DSRTree(T *rootNode)
  : DSRTreeNodeCursor<T>(),
    RootNode(rootNode)
{
    /* initialize the cursor */
    gotoRoot();
}


template<typename T>
DSRTree<T>::DSRTree(const DSRTreeNodeCursor<T> &startCursor,
                    size_t stopAfterNodeID)
  : DSRTreeNodeCursor<T>(),
    RootNode(NULL)
{
    T *nodeCursor = startCursor.getNode();
    /* since we start from a particular node, we need to check it first */
    if (nodeCursor != NULL)
    {
        E_AddMode addMode = AM_afterCurrent;
        T *newNode = NULL;
        OFStack<T *> nodeCursorStack;
        /* use current node if none was specified */
        if (stopAfterNodeID == 0)
            stopAfterNodeID = nodeCursor->getIdent();
        /* perform a "deep search", just like DSRTreeNodeCursor<>::iterate() */
        while (nodeCursor != NULL)
        {
            /* clone current node and add it to the tree */
            if (addNode(newNode = nodeCursor->clone(), addMode) == 0)
            {
                /* failed to add node, so delete it and exit the loop */
                delete newNode;
                break;
            }
            /* then goto to the next node to be copied */
            if (nodeCursor->Down != NULL)
            {
                /* go one level down to the first child node */
                nodeCursorStack.push(nodeCursor);
                nodeCursor = nodeCursor->getDown();
                addMode = AM_belowCurrent;
            }
            else if (nodeCursor->Next != NULL)
            {
                /* check whether the last node has been processed */
                if (nodeCursor->getIdent() == stopAfterNodeID)
                {
                    /* exit the loop */
                    nodeCursor = NULL;
                } else {
                    /* proceed with the following sibling */
                    nodeCursor = nodeCursor->getNext();
                    addMode = AM_afterCurrent;
                }
            } else {
                /* check whether there are any siblings on higher levels */
                do {
                    if (!nodeCursorStack.empty())
                    {
                        nodeCursor = nodeCursorStack.top();
                        nodeCursorStack.pop();
                        this->goUp();
                        /* check whether the last node has been processed */
                        if ((nodeCursor != NULL) && (nodeCursor->getIdent() == stopAfterNodeID))
                        {
                            /* exit the loop */
                            nodeCursor = NULL;
                        }
                    } else
                        nodeCursor = NULL;
                } while ((nodeCursor != NULL) && (nodeCursor->Next == NULL));
                if (nodeCursor != NULL)
                {
                    nodeCursor = nodeCursor->getNext();
                    addMode = AM_afterCurrent;
                }
            }
        }
        /* initialize the cursor */
        gotoRoot();
    }
}


template<typename T>
DSRTree<T>::~DSRTree()
{
    clear();
}


template<typename T>
void DSRTree<T>::clear()
{
    if (gotoRoot())
    {
        size_t nodeID = 0;
        /* there might be more than one node at top-level */
        do {
            /* so delete them all */
            nodeID = removeNode();
        } while (nodeID > 0);
    }
}


template<typename T>
OFBool DSRTree<T>::isEmpty() const
{
    return (RootNode == NULL);
}


template<typename T>
size_t DSRTree<T>::countNodes() const
{
    size_t count = 0;
    DSRTreeNodeCursor<T> cursor(RootNode);
    if (cursor.isValid())
    {
        /* iterate over all nodes */
        do {
            ++count;
        } while (cursor.iterate());
    }
    return count;
}


template<typename T>
size_t DSRTree<T>::getNextNodeID() const
{
    /* current value of the counter used to create unique identifiers */
    return DSRTreeNode::getIdentCounter();
}


template<typename T>
size_t DSRTree<T>::gotoRoot()
{
    return this->setCursor(RootNode);
}


template<typename T>
size_t DSRTree<T>::gotoNode(const size_t searchID,
                            const OFBool startFromRoot)
{
    size_t nodeID = 0;
    if (searchID > 0)
    {
        if (startFromRoot)
            gotoRoot();
        /* call the real function */
        nodeID = DSRTreeNodeCursor<T>::gotoNode(searchID);
    }
    return nodeID;
}


template<typename T>
size_t DSRTree<T>::gotoNode(const OFString &reference,
                            const OFBool startFromRoot)
{
    size_t nodeID = 0;
    if (!reference.empty())
    {
        if (startFromRoot)
            gotoRoot();
        /* call the real function */
        nodeID = DSRTreeNodeCursor<T>::gotoNode(reference);
    }
    return nodeID;
}


template<typename T>
size_t DSRTree<T>::addNode(T *node,
                           const E_AddMode addMode)
{
    size_t nodeID = 0;
    if (node != NULL)
    {
        if (this->NodeCursor != NULL)
        {
            /* update references based on 'addMode' */
            switch (addMode)
            {
                case AM_afterCurrent:
                    node->Prev = this->NodeCursor;
                    node->Next = this->NodeCursor->Next;
                    /* connect to current node */
                    if (this->NodeCursor->Next != NULL)
                        (this->NodeCursor->Next)->Prev = node;
                    this->NodeCursor->Next = node;
                    ++this->Position;
                    break;
                case AM_beforeCurrent:
                    node->Prev = this->NodeCursor->Prev;
                    node->Next = this->NodeCursor;
                    /* connect to current node */
                    if ((this->NodeCursor->Prev != NULL) && (this->Position > 1))
                        (this->NodeCursor->Prev)->Next = node;
                    else if (!this->NodeCursorStack.empty() && (this->Position == 1))
                        this->NodeCursorStack.top()->Down = node;
                    this->NodeCursor->Prev = node;
                    break;
                case AM_belowCurrent:
                    /* store old position */
                    if (this->Position > 0)
                    {
                        this->PositionList.push_back(this->Position);
                        this->Position = 1;
                    }
                    this->NodeCursorStack.push(this->NodeCursor);
                    /* parent node has already child nodes */
                    if (this->NodeCursor->Down != NULL)
                    {
                        DSRTreeNode *tempNode = this->NodeCursor->Down;
                        /* goto last node (sibling) */
                        while (tempNode->Next != NULL)
                        {
                            tempNode = tempNode->Next;
                            ++this->Position;
                        }
                        tempNode->Next = node;
                        node->Prev = tempNode;
                    } else
                        this->NodeCursor->Down = node;
                    break;
            }
            this->NodeCursor = node;
        } else {
            this->RootNode = this->NodeCursor = node;
            this->Position = 1;
        }
        nodeID = this->NodeCursor->getIdent();
    }
    return nodeID;
}


template<typename T>
T *DSRTree<T>::extractNode()
{
    T *cursor = this->NodeCursor;
    /* extract current node (incl. subtree) from tree */
    if (cursor != NULL)
    {
        /* are there any siblings? */
        if ((cursor->Prev != NULL) || (cursor->Next != NULL))
        {
            /* connect to previous node */
            if (cursor->Prev != NULL)
                (cursor->Prev)->Next = cursor->Next;
            else
            {
                /* is there any direct parent node? */
                if (!this->NodeCursorStack.empty())
                {
                    DSRTreeNode *parent = this->NodeCursorStack.top();
                    if (parent != NULL)
                        parent->Down = cursor->Next;
                }
            }
            /* connect to next node */
            if (cursor->Next != NULL)
            {
                (cursor->Next)->Prev = cursor->Prev;
                if (this->NodeCursor == this->RootNode)
                    this->RootNode = cursor->getNext();     // old root node deleted
                this->NodeCursor = cursor->getNext();
            } else {
                /* set cursor to previous node since there is no next node */
                this->NodeCursor = cursor->getPrev();
                --this->Position;
            }
        } else {
            /* no siblings: check for child nodes */
            if (!this->NodeCursorStack.empty())
            {
                this->NodeCursor = this->NodeCursorStack.top();
                this->NodeCursorStack.pop();
                this->Position = this->PositionList.back();
                this->PositionList.pop_back();
                /* should never be NULL, but ... */
                if (this->NodeCursor != NULL)
                    this->NodeCursor->Down = NULL;
                else
                {
                    this->RootNode = NULL;                  // tree is now empty
                    this->Position = 0;
                }
            } else {
                this->RootNode = this->NodeCursor = NULL;   // tree is now empty
                this->Position = 0;
                this->PositionList.clear();
            }
        }
        /* remove references to former siblings */
        cursor->Prev = NULL;
        cursor->Next = NULL;
    }
    return cursor;
}


template<typename T>
size_t DSRTree<T>::removeNode()
{
    size_t nodeID = 0;
    /* extract current node (incl. subtree) from tree */
    T *cursor = extractNode();
    if (cursor != NULL)
    {
        /* delete all nodes from extracted subtree */
        /* (could also use the "new" DSRTreeNodeCursor class) */
        T *delNode = NULL;
        OFStack<T *> cursorStack;
        while (cursor != NULL)
        {
            delNode = cursor;
            if (cursor->Down != NULL)
            {
                if (cursor->Next != NULL)
                    cursorStack.push(cursor->getNext());
                cursor = cursor->getDown();
            }
            else if (cursor->Next != NULL)
                cursor = cursor->getNext();
            else if (!cursorStack.empty())
            {
                cursor = cursorStack.top();
                cursorStack.pop();
            } else
                cursor = NULL;
            delete delNode;
        }
        /* return identifier of (new) current node */
        if (this->NodeCursor != NULL)
            nodeID = this->NodeCursor->getIdent();
    }
    return nodeID;
}


template<typename T>
DSRTree<T> *DSRTree<T>::extractSubTree()
{
    DSRTree<T> *tree = NULL;
    /* extract current node from tree and create a new tree object (with this root) */
    T *node = extractNode();
    if (node != NULL)
        tree = new DSRTree<T>(node);
    return tree;
}


template<typename T>
DSRTree<T> *DSRTree<T>::cloneSubTree(const size_t stopAfterNodeID) const
{
    /* create a copy of the specified subtree */
    return new DSRTree<T>(this->NodeCursor, stopAfterNodeID);
}


// protected methods

template<typename T>
T *DSRTree<T>::getRoot() const
{
    return RootNode;
}


template<typename T>
T *DSRTree<T>::getAndRemoveRootNode()
{
    T* root = RootNode;
    /* "forget" reference to root node */
    RootNode = NULL;
    return root;
}


#endif
