#ifndef BINARY_SEARCH_TREE_CLASS
#define BINARY_SEARCH_TREE_CLASS

#include <iomanip>      // for setw()
#include <strstream>    // for format conversion
#include <string>       // node data formatted as a string
#include <queue>
#include <utility>      // pair class

#include "d_except.h"   // exception classes

using namespace std;

// declares a binary search tree node object
template <typename T>
class stnode
{
   public:
      // stnode is used to implement the binary search tree class
      // making the data public simplifies building the class functions

      T nodeValue;
         // node data
      stnode<T> *left, *right, *parent;
      // child pointers and pointer to the node's parent

      // constructor
      stnode (const T& item, stnode<T> *lptr = NULL, 
              stnode<T> *rptr = NULL, stnode<T> *pptr = NULL):
            nodeValue(item), left(lptr), right(rptr), parent(pptr)
      {}
};

template <typename T>
class stree
{
   public:

      // include the iterator nested classes
      #include "d_stiter.h"

      stree();
         // constructor. initialize root to NULL and size to 0
      stree(T *first, T *last);
         // constructor. insert the elements from the pointer
         // range [first, last) into the tree
      stree(const stree<T>& tree);
         // copy constructor
      ~stree();
         // destructor
      stree<T>& operator= (const stree<T>& rhs);
         // assignment operator

      iterator find(const T& item);
         // search for item. if found, return an iterator pointing
         // at it in the tree; otherwise, return end()
      const_iterator find(const T& item) const;
         // constant version

      int empty() const;
         // indicate whether the tree is empty
      int size() const;
         // return the number of data items in the tree

      pair<iterator, bool> insert(const T& item);
         // if item is not in the tree, insert it and
         // return a pair whose iterator component points
         // at item and whose bool component is true. if item
         // is in the tree, return a pair whose iterator
         // component points at the existing item and whose
         // bool component is false
         // Postcondition: the tree size increases by 1 if item
         // is not in the tree

      int erase(const T& item);
         // if item is in the tree, erase it and return 1;
         // otherwise, return 0
         // Postcondition: the tree size decreases by 1 if
         // item is in the tree

      void erase(iterator pos);
         // erase the item pointed to by pos.
         // Preconditions: the tree is not empty and pos points
         // to an item in the tree. if the tree is empty, the
         // function throws the underflowError exception. if the
         // iterator is invalid, the function throws the referenceError
         // exception.
         // Postcondition: the tree size decreases by 1

      void erase(iterator first, iterator last);
         // erase all items in the range [first, last).
         // Precondition: the tree is not empty. if the tree
         // is empty, the function throws the underflowError
         // exception.
         // Postcondition: the size of the tree decreases by
         // the number of elements in the range [first, last)

      iterator begin();
         // return an iterator pointing to the first item
         // inorder
      const_iterator begin() const;
         // constant version
      iterator end();
         // return an iterator pointing just past the end of
         // the tree data
      const_iterator end() const;
         // constant version

      void displayTree(int maxCharacters);
         // tree display function. maxCharacters is the
         // largest number of characters required to draw
         // the value of a node

   private:
      stnode<T> *root;
         // pointer to tree root
      int treeSize;
         // number of elements in the tree

      stnode<T> *getSTNode(const T& item,
                           stnode<T> *lptr,stnode<T> *rptr, stnode<T> *pptr);
         // allocate a new tree node and return a pointer to it.
         // if memory allocation fails, the function throws the
         // memoryAllocationError exception

      stnode<T> *copyTree(stnode<T> *t);
         // recursive function used by copy constructor and assignment
         // operator to assign the current tree as a copy of another tree

      void deleteTree(stnode<T> *t);
         // recursive function used by destructor and assignment
         // operator to delete all the nodes in the tree

      stnode<T> *findNode(const T& item) const;
         // search for item in the tree. if it is in the tree,
         // return a pointer to its node; otherwise, return NULL.
         // used by find() and erase()

      tnodeShadow *buildShadowTree(stnode<T> *t, int level, int& column);
         // recursive function that builds a subtree of the shadow tree
         // corresponding to node t of the tree we are drawing. level is the
         // level-coordinate for the root of the subtree, and column is the
         // changing column-coordinate of the tree nodes

      void deleteShadowTree(tnodeShadow *t);
         // remove the shadow tree from memory after displayTree()
         // displays the binary search tree
};

#endif  // BINARY_SEARCH_TREE_CLASS
