#ifndef _AVLTREET_H_INCLUDED_
#define _AVLTREET_H_INCLUDED_
///////////////////////////////////////////////////////////////////////////////
//                                                         
// AVLTreeT.h
// ----------
// Implementation of the Adelson-Velski-Landis tree algorithm 
//                                                         
// Design and Implementation by Bjoern Lemke               
//                                                         
// (C)opyright 2000-2019 Bjoern Lemke
//
// INTERFACE MODULE
//
// Class: AVLTreeT template class
// 
// Description: template native binary tree implementation (not balanced)
//
// Status: CLEAN
//
///////////////////////////////////////////////////////////////////////////////

#include <iostream>
using namespace std;

template <class T>
class AVLTreeT {
    
public:
    
    AVLTreeT();
    AVLTreeT(const AVLTreeT<T>& x);
    ~AVLTreeT();
    bool isEmpty();
    void Empty();
    void Insert(const T& element);
    bool Remove(const T& element);
    T* Find(const T& element) const;
    T* First();
    T* Next();
    
    unsigned long Size();
    
    void* getTreePointer();
    void setTreePointer(void* treePointer);
    
    AVLTreeT<T>& operator = (const AVLTreeT<T>& x);
    bool operator == (const AVLTreeT<T>& x);
    bool operator < (const AVLTreeT<T>& x);
    bool operator > (const AVLTreeT<T>& x);
    
    void printTree();

private:

    class AVLElement {
	
    public:
	
	AVLElement() 
	{
	    parent=0;
	    left=0;
	    right=0;
	    height=0;
	}
	
	~AVLElement() 
	{
	    if ( left )
		delete left;
	    if ( right ) 
		delete right;	    
	}
	
	T element;
	AVLElement* parent;
	AVLElement* left;
	AVLElement* right;
	int height;
    };
        
    
    void balanceTree(AVLElement* pElement);
    void rotateRR(AVLElement* pElement);
    void rotateLL(AVLElement* pElement);
    void rotateRL(AVLElement* pElement);
    void rotateLR(AVLElement* pElement);

    void printNode(AVLElement *pNode, int level);

    AVLElement* getFirst() const
    {
	AVLElement* pElement = _rootElement;
	
	if (pElement)
	{	
	    while (pElement->left)
	    {
		pElement = pElement->left;
	    }
	    if (pElement)
	    {	
		return pElement;	
	    }
	    else
	    {
		return (AVLElement*)0;
	    }
	}
	return (AVLElement*)0;
    }
    
    
    AVLElement* getNext(AVLElement* pElement) const
    {
	if (pElement)
	{
	    if (pElement->right)
	    {	    
		pElement = pElement->right;
		while (pElement->left)
		{
		    pElement = pElement->left;
		}
		return pElement;
	    }
	    else
	    {
		if (pElement->parent)
		{	
		    if (pElement->parent->left == pElement)
		    {		    
			pElement = pElement->parent;
			return pElement;
		    }
		    else
		    {
			while (pElement->parent->left != pElement)
			{
			    pElement = pElement->parent;
			    if (!pElement->parent) 
			    {
				return (AVLElement*)0;
			    }			
			}
			pElement = pElement->parent;
			return pElement;
		    }		
		}
		else
		{
		    return (AVLElement*)0;
		}	    
	    }
	}
	return (AVLElement*)0;
    }
    
    AVLElement* _rootElement;
    AVLElement* _treePointer;
    unsigned long _size;
};

template<class T> AVLTreeT<T>::AVLTreeT()
{
    _rootElement = 0;
    _treePointer = 0;	
    _size = 0;
}

template<class T> AVLTreeT<T>::AVLTreeT(const AVLTreeT<T>& x)
{    
    _rootElement = 0;
    _treePointer = 0;
    _size = 0;

    if (x._rootElement) 
    {	
	_rootElement = new AVLElement;
	_rootElement->parent=0;
	_rootElement->left=0;
	_rootElement->right=0;
	_rootElement->element = x._rootElement->element;
	_size++;
    }
    AVLElement* sourcePointer = x._rootElement;    
    AVLElement* targetPointer = _rootElement;    
    
    while (sourcePointer)
    {
	if (sourcePointer->left)
	{	    
	    sourcePointer = sourcePointer->left;
	    targetPointer->left = new AVLElement;
	    targetPointer->left->parent = targetPointer;	    
	    targetPointer = targetPointer->left;
	    targetPointer->element = sourcePointer->element;
	    targetPointer->left = 0;
	    targetPointer->right = 0;	    
	    _size++;
	}
	else if (sourcePointer->right)
	{
	    sourcePointer = sourcePointer->right; 
	    targetPointer->right = new AVLElement;
	    targetPointer->right->parent = targetPointer;	    
	    targetPointer = targetPointer->right;
	    targetPointer->element = sourcePointer->element;
	    targetPointer->left = 0;
	    targetPointer->right = 0;
	    _size++;
	} 
	else    
	{
	    if (sourcePointer->parent)
	    {		
		while(sourcePointer->parent->right == sourcePointer)
		{
		    sourcePointer = sourcePointer->parent;
		    targetPointer = targetPointer->parent;		    
		    if (!sourcePointer->parent)
		    {
			return;
		    }		    
		}
		AVLElement* parentPointer=sourcePointer;		
		sourcePointer = sourcePointer->parent;
		targetPointer = targetPointer->parent;
		while (sourcePointer->right == parentPointer || sourcePointer->right == 0)
		{		 
		    parentPointer = sourcePointer;			
		    sourcePointer = sourcePointer->parent;
		    targetPointer = targetPointer->parent;		    
		    if (!sourcePointer)
		    {
			return;		  		    
		    } 		    		    
		} 
		sourcePointer = sourcePointer->right;
		targetPointer->right = new AVLElement;
		targetPointer->right->parent = targetPointer;
		targetPointer = targetPointer->right;
		targetPointer->element = sourcePointer->element;	      
		targetPointer->left = 0;
		targetPointer->right = 0;
		_size++;
	    }
	    else
	    {
		return;
	    }	    
	}
    }  
    return;    
}

template<class T> AVLTreeT<T>::~AVLTreeT()
{
    if (_rootElement)
	delete _rootElement;
    _rootElement = 0;

    _size = 0;
    _treePointer = 0;
}

template<class T> bool AVLTreeT<T>::isEmpty()
{
    if (_rootElement == 0)
    {
	return true;
    }
    return false;
}

template<class T> void AVLTreeT<T>::Empty()
{
    if ( _rootElement )
	delete _rootElement;
    _rootElement = 0;

    _size = 0;
    _treePointer = 0;
}

template<class T> void AVLTreeT<T>::Insert(const T& element)
{
    if (_rootElement)
    {
	AVLElement* pElement = _rootElement;
	
	bool inserted = false;
	
	while ( ! inserted ) 
	{
	    if  ( pElement->element > element ) 
	    {
		if (  pElement->left == 0 )
		{
		    pElement->left = new AVLElement;
		    pElement->left->element = element;
		    pElement->left->parent = pElement;
		    pElement->left->height = 1;
		    inserted=true;
		}
		else
		{
		    pElement = pElement->left;
		}
	    }
	    else
	    {
		if ( pElement->right == 0 )
		{
		    // insert at right
		    pElement->right = new AVLElement;
		    pElement->right->element = element;
		    pElement->right->parent = pElement;
		    pElement->right->height = 1;
		    inserted = true;		
		}
		else
		{
		    pElement = pElement->right;
		}	    
	    }
	}

	if ( pElement->height == 1 )
	{
	    pElement->height = 2;
	    balanceTree(pElement);
	}
    }
    else
    {
	_rootElement = new AVLElement;
	_rootElement->element = element;
	_rootElement->height = 1;
    }
    _size++;
}

template<class T> void AVLTreeT<T>::balanceTree(AVLElement *pElement)
{

    bool doBalance = true;

    AVLElement* pParent = pElement->parent;
    
    bool wasLeft = false;

    if ( pParent )
	wasLeft = pParent->left == pElement;

    while ( doBalance && pParent  )
    {
	int leftHeight = 0;
	if ( pParent->left )
	    leftHeight = pParent->left->height;

	int rightHeight = 0;
	if ( pParent->right )
	    rightHeight = pParent->right->height;

	/*
	cout << "Balance at " << pParent->element << endl;
	if ( wasLeft )
	    cout << "Sub node is left" << endl;
	cout << "RH=" << rightHeight << endl;
	cout << "LH=" << leftHeight << endl;
	*/

	if ( wasLeft ) 
	{		
	    pElement = pParent->left;

	    if ( rightHeight > leftHeight ) 
	    {	
		// right tree still higher, no need to rotate
		// pParent->height = rightHeight;

		doBalance=false;
		/*
		if ( pParent->parent )
		    wasLeft = pParent->parent->left == pParent;
		pParent = pParent->parent;
		*/
	    }
	    else if ( leftHeight == rightHeight )
	    {
		if ( leftHeight != pParent->height )
		{
		    pParent->height = leftHeight + 1;
		 
		    if ( pParent->parent )
			wasLeft = pParent->parent->left == pParent;

		    pParent = pParent->parent;
		}
		else
		{	    
		    doBalance = false;
		}
	    }
	    else 
	    {		
		if ( rightHeight + 1 < leftHeight )
		{
		    int subLeftHeight = 0;
		    if ( pElement->left )
			subLeftHeight = pElement->left->height;
		    
		    int subRightHeight = 0;
		    if ( pElement->right )
			subRightHeight = pElement->right->height;
		    
		    if ( pParent->parent )
			wasLeft = pParent->parent->left == pParent;
		    
		    AVLElement *pRotate = pParent;
		    pParent = pParent->parent;
		    
		    if (subLeftHeight > subRightHeight)
		    {		    
			rotateRR(pRotate);
		    }
		    else
		    {
			rotateRL(pRotate);
		    }
		}		
		else
		{

		    // just propagte height
		    pParent->height = leftHeight + 1;
		    
		    if ( pParent->parent )
			wasLeft = pParent->parent->left == pParent;

		    pParent = pParent->parent;
		}
	    }
	} 
	else 
	{	    

	    pElement = pParent->right;

	    if ( leftHeight > rightHeight )
	    {
		// pParent->height = rightHeight + 1;
		
		doBalance=false;

		/*
		if ( pParent->parent )
		    wasLeft = pParent->parent->left == pParent;

		pParent = pParent->parent;
		*/
	    }
	    else if ( leftHeight == rightHeight )
	    {
		if ( leftHeight != pParent->height )
		{		    
		    pParent->height = leftHeight + 1;

		    if ( pParent->parent )
			wasLeft = pParent->parent->left == pParent;
		   
		    pParent = pParent->parent;
		}
		else
		{
		    doBalance = false;
		}
	    }
	    else
	    {		

		if ( leftHeight + 1 < rightHeight )
		{

		    int subLeftHeight = 0;
		    if ( pElement->left )
			subLeftHeight = pElement->left->height;
		    
		    int subRightHeight = 0;
		    if ( pElement->right )
			subRightHeight = pElement->right->height;

		    if ( pParent->parent )
			wasLeft = pParent->parent->left == pParent;
		    

		    AVLElement *pRotate = pParent;		    
		    pParent = pParent->parent;
		    
		    if (subLeftHeight < subRightHeight)
		    {		    
			rotateLL(pRotate);
		    }
		    else
		    {
			rotateLR(pRotate);
		    }
		}		
		else
		{

		    // just propagte height
		    pParent->height = rightHeight + 1;
		    
		    if ( pParent->parent )
			wasLeft = pParent->parent->left == pParent;
		    
		    pParent = pParent->parent;

		}
	    }
	}
	
	/*
	cout << "Tree Status" << endl;
	printTree();
	*/
    }
}

template<class T> bool AVLTreeT<T>::Remove(const T& element)
{
    AVLElement* pElement = _rootElement;
    
    bool found = false;
    
    while (pElement && !found)
    {
	if (pElement->element < element)
	{
	    pElement = pElement->right;
	}
	else if (pElement->element > element)
	{
	    pElement = pElement->left;
	}
	else
	{
	    found = true;
	}	
    }
    
    if (found)
    {
	// TODO	
	cout << "Remove method for AVLTreeT still not implemented" << endl;
	return false;
    }	
    else
    {
	return false;
    }
}

template<class T> T* AVLTreeT<T>::Find(const T& element) const
{
    AVLElement* pElement = _rootElement;
    
    while (pElement)
    {
	if (pElement->element < element)
	{
	    pElement = pElement->right;
	}
	else if (pElement->element > element)
	{
	    pElement = pElement->left;
	}
	else
	{
	    return (&(pElement->element));
	}	
    }
    return (T*)0;    
}

template<class T> T* AVLTreeT<T>::First()
{
    _treePointer = getFirst();
    if (_treePointer)
    {
	return (&(_treePointer->element));
    }
    else
    {
	return (T*)0;
    }    
}

template<class T> T* AVLTreeT<T>::Next()
{    
    _treePointer = getNext(_treePointer);
    
    if (_treePointer)
    {
	return (&(_treePointer->element));
    }
    else
    {
	return (T*)0;
    }    
}

template<class T> unsigned long AVLTreeT<T>::Size()
{
    return _size;
}

template<class T> void* AVLTreeT<T>::getTreePointer()
{
    return (void*)_treePointer;
}

template<class T> void AVLTreeT<T>::setTreePointer(void* treePointer)
{
    _treePointer = (AVLElement*)treePointer;
}

template<class T> AVLTreeT<T>& AVLTreeT<T>::operator=(const AVLTreeT<T>& x)
{    
    
    Empty();
    _rootElement = 0;
    _treePointer = 0;	 
    _size=0;

    if (x._rootElement) 
    {	
	_rootElement = new AVLElement;
	_rootElement->parent=0;
	_rootElement->left=0;
	_rootElement->right=0;	
	_rootElement->element = x._rootElement->element;
	_size++;
    }
    AVLElement* sourcePointer = x._rootElement;    
    AVLElement* targetPointer = _rootElement;    
  
    while (sourcePointer)
    {
	if (sourcePointer->left)
	{	    
	    sourcePointer = sourcePointer->left;
	    targetPointer->left = new AVLElement;
	    targetPointer->left->parent = targetPointer;	    
	    targetPointer = targetPointer->left;
	    targetPointer->element = sourcePointer->element;
	    targetPointer->left = 0;
	    targetPointer->right = 0;	    
	    _size++;
	}
	else if (sourcePointer->right)
	{
	    sourcePointer = sourcePointer->right; 
	    targetPointer->right = new AVLElement;
	    targetPointer->right->parent = targetPointer;	    
	    targetPointer = targetPointer->right;
	    targetPointer->element = sourcePointer->element;
	    targetPointer->left = 0;
	    targetPointer->right = 0;	    
	    _size++;
	} 
	else    
	{
	    if (sourcePointer->parent)
	    {		
		while(sourcePointer->parent->right == sourcePointer)
		{
		    sourcePointer = sourcePointer->parent;
		    targetPointer = targetPointer->parent;		    
		    if (!sourcePointer->parent)
		    {
		      return (*this);
		    }		    
		}
		AVLElement* parentPointer=sourcePointer;		
		sourcePointer = sourcePointer->parent;
		targetPointer = targetPointer->parent;
		while (sourcePointer->right == parentPointer || sourcePointer->right == 0)
		{		 
		    parentPointer = sourcePointer;			
		    sourcePointer = sourcePointer->parent;
		    targetPointer = targetPointer->parent;		    
		    if (!sourcePointer)
		    {
			return (*this);		  		    
		    } 		    		    
		} 
		sourcePointer = sourcePointer->right;
		targetPointer->right = new AVLElement;
		targetPointer->right->parent = targetPointer;
		targetPointer = targetPointer->right;
		targetPointer->element = sourcePointer->element;	      
		targetPointer->left = 0;
		targetPointer->right = 0;	    
		_size++;
	    }
	    else
	    {
		return (*this);
	    }	    
	}
    }
    return (*this);
}
    
template<class T> bool AVLTreeT<T>::operator==(const AVLTreeT<T>& x)
{
    
    AVLElement* pElement1 = getFirst();
    AVLElement* pElement2 = x.getFirst();
    
    while (pElement1 && pElement2)
    {
	if (pElement1->element == pElement2->element)
	{
	    pElement1 = getNext(pElement1);
	    pElement2 = x.getNext(pElement2);
	}
	else
	{
	    return false;
	}
    }
    
    if (pElement1 || pElement2)
    {
	return false;
    }
    else
    {
	return true;
    }
}
	
template<class T> bool AVLTreeT<T>::operator < (const AVLTreeT<T>& x)
{
    
    AVLElement* pElement1 = getFirst();
    AVLElement* pElement2 = x.getFirst();
    
    while (pElement1 && pElement2)
    {

	if (pElement1->element < pElement2->element)
	{
	    return true;	    
	}
	else if (pElement1->element > pElement2->element)
	{
	    return false;
	}
	else
	{
	    pElement1 = getNext(pElement1);
	    pElement2 = x.getNext(pElement2);
	}
    }

    if (pElement1)
    {
	return false;
    }
    else if (pElement2)
    {
	return true;
    }
    else
    {
	return false;
    }
}

template<class T> bool AVLTreeT<T>::operator > (const AVLTreeT<T>& x)
{

    AVLElement* pElement1 = getFirst();
    AVLElement* pElement2 = x.getFirst();
    
    while (pElement1 && pElement2)
    {
	if (pElement1->element > pElement2->element)
	{
	    return true;	    
	}
	else if (pElement1->element < pElement2->element)
	{
	    return false;
	}
	else
	{
	    pElement1 = getNext(pElement1);
	    pElement2 = x.getNext(pElement2);
	}
    }
    
    if (pElement1)
    {
	return true;
    }
    else if (pElement2)
    {
	return false;
    }
    else
    {
	return false;
    }
}

template<class T> void AVLTreeT<T>::rotateLL(AVLElement* pNode)
{

    // cout << "Rotate LL at " << pNode->element << endl;

    AVLElement *pNodeP = 0;
    AVLElement *pNodeA = 0;
    AVLElement *pNodeB = 0;

    // setting up required nodes

    pNodeP = pNode->parent;
    pNodeA = pNode->right;
    if ( pNodeA )
    {
	pNodeB = pNodeA->left;
    }

    // making rotation 

    if ( pNodeA )
    {
	pNodeA->left = pNode;
	pNodeA->parent = pNodeP;
    }

    if ( pNodeB )
    {
	pNodeB->parent = pNode;
    }

    pNode->right = pNodeB; 
    pNode->parent = pNodeA;
    
    if ( pNodeP )
    {
	if ( pNodeP->right == pNode )
	{
	    pNodeP->right = pNodeA;
	}
	else
	{
	    pNodeP->left = pNodeA;
	}
    }
    else
    {
	_rootElement = pNodeA;
    }


    int leftHeight = 0;
    int rightHeight = 0;

    if ( pNode->left )
	leftHeight = pNode->left->height;
    if ( pNode->right )
	rightHeight = pNode->right->height;    
    pNode->height = leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;

    if ( pNodeA )
    {
	leftHeight = 0;
	rightHeight = 0;

	if ( pNodeA->left )
	    leftHeight = pNodeA->left->height;
	if ( pNodeA->right )
	    rightHeight = pNodeA->right->height;

	pNodeA->height = leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
    }

    if ( pNodeP )
    {
	leftHeight = 0;
	rightHeight = 0;
	
	if ( pNodeP->left )
	    leftHeight = pNodeP->left->height;
	if ( pNodeP->right )
	    rightHeight = pNodeP->right->height;
	pNodeP->height = leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
    }
} 

template<class T> void AVLTreeT<T>::rotateRR(AVLElement* pNode)
{

    // cout << "Rotate RR at " << pNode->element << endl;

    AVLElement *pNodeP = 0;
    AVLElement *pNodeA = 0;
    AVLElement* pNodeB = 0;
     
    // setting up required nodes
    pNodeP = pNode->parent;
    pNodeA = pNode->left;
    if ( pNodeA )
    {
	pNodeB = pNodeA->right;
    }

    // making rotation
    if ( pNodeA )
    {
	pNodeA->right = pNode;
	pNodeA->parent = pNodeP;
    }

    if ( pNodeB )
    {
	pNodeB->parent = pNode;
    }

    pNode->parent = pNodeA;
    pNode->left = pNodeB;

    if ( pNodeP )
    {
	if ( pNodeP->right == pNode )
	{
	    pNodeP->right = pNodeA;
	}
	else
	{
	    pNodeP->left = pNodeA;       
	}
    }
    else
    {
	_rootElement = pNodeA;
    }


    int leftHeight = 0;
    int rightHeight = 0;
    
    if ( pNode->left )
	leftHeight = pNode->left->height;
    if ( pNode->right )
	rightHeight = pNode->right->height;
    
    pNode->height = leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;

    if ( pNodeA )
    {
	leftHeight = 0;
	rightHeight = 0;
	if ( pNodeA->left )
	    leftHeight = pNodeA->left->height;
	if ( pNodeA->right )
	    rightHeight = pNodeA->right->height;
	
	pNodeA->height = leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
    }

    if ( pNodeP )
    {
	leftHeight = 0;
	rightHeight = 0;
	if ( pNodeP->left )
	    leftHeight = pNodeP->left->height;
	if ( pNodeP->right )
	    rightHeight = pNodeP->right->height;
	pNodeP->height = leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
    }
}

template<class T> void AVLTreeT<T>::rotateLR(AVLElement* pNode)
{

    // cout << "Rotate LR at " << pNode->element << endl;

    AVLElement *pNodeP = 0;
    AVLElement *pNodeA = 0;
    AVLElement* pNodeB = 0;
    AVLElement* pNodeC = 0;
    AVLElement* pNodeD = 0;

    pNodeP = pNode->parent;
    pNodeA = pNode->right;

    if ( pNodeA )
    {
	pNodeB = pNodeA->left;

    }
    if ( pNodeB )
    {
	pNodeD = pNodeB->right;	
	pNodeC = pNodeB->left;
    }
    
    if ( pNodeA )
    {
	pNodeA->left = pNodeD;
	pNodeA->parent = pNodeB;
    }
    
    if ( pNodeB )
    {
	pNodeB->left = pNode;
	pNodeB->parent = pNodeP;
	pNodeB->right = pNodeA;
    }

    if ( pNodeC )
	pNodeC->parent = pNode;

    if ( pNodeD )
	pNodeD->parent = pNodeA;

    if ( pNodeP ) 
    {
	if ( pNodeP->right == pNode )
	{
	    pNodeP->right = pNodeB;
	}
	else
	{
	    pNodeP->left = pNodeB;
	}
    }
    else
    {
	_rootElement = pNodeB;
    }

    pNode->parent = pNodeB;
    pNode->right = pNodeC;    
    
    int leftHeight = 0;
    int rightHeight = 0;

    if ( pNode->left )
	leftHeight = pNode->left->height;
    if ( pNode->right )
	rightHeight = pNode->right->height;    
    pNode->height = leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;

    if ( pNodeA )
    {
	leftHeight = 0;
	rightHeight = 0;

	if ( pNodeA->left )
	    leftHeight = pNodeA->left->height;
	if ( pNodeA->right )
	    rightHeight = pNodeA->right->height;
	pNodeA->height = leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
    }

    if ( pNodeB )
    {
	leftHeight = 0;
	rightHeight = 0;
	
	if ( pNodeB->left )
	    leftHeight = pNodeB->left->height;
	if ( pNodeB->right )
	    rightHeight = pNodeB->right->height;
	pNodeB->height = leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
    }

    if ( pNodeP )
    {
	leftHeight = 0;
	rightHeight = 0;

	if ( pNodeP->left )
	    leftHeight = pNodeP->left->height;
	if ( pNodeP->right )
	    rightHeight = pNodeP->right->height;
	pNodeP->height = leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
    }
}

template<class T> void AVLTreeT<T>::rotateRL(AVLElement* pNode)
{

    // cout << "Rotate RL at " << pNode->element << endl;

    AVLElement *pNodeP = 0;
    AVLElement *pNodeA = 0;
    AVLElement* pNodeB = 0;
    AVLElement* pNodeC = 0;
    AVLElement* pNodeD = 0;

    pNodeP = pNode->parent;
    pNodeA = pNode->left;
    
    if ( pNodeA )
	pNodeB = pNodeA->right;
    
    if ( pNodeB )
    {
	pNodeC = pNodeB->left;
	pNodeD = pNodeB->right;
    }
    
    if ( pNodeC )
	pNodeC->parent = pNodeA;
    
    if ( pNodeP )
    {
	if ( pNodeP->right == pNode)
	{
	    pNodeP->right = pNodeB;
	}
	else
	{
	    pNodeP->left = pNodeB;
	}
    }
    else
    {
	_rootElement = pNodeB;
    }

    if ( pNodeB )
    {
	pNodeB->parent = pNodeP;	
	pNodeB->left = pNodeA;
	pNodeB->right = pNode;
    }

    if ( pNodeA )
    {
	pNodeA->parent = pNodeB;
	pNodeA->right = pNodeC;
    }

    if ( pNodeD )
	pNodeD->parent = pNode;

    pNode->parent = pNodeB;
    pNode->left = pNodeD;

    // set up balance

    int leftHeight = 0;
    int rightHeight = 0;

    if ( pNode->left )
	leftHeight = pNode->left->height;

    if ( pNode->right )
	rightHeight = pNode->right->height;    

    pNode->height = leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;

    if ( pNodeA )
    {
	leftHeight = 0;
	rightHeight = 0;

	if ( pNodeA->left )
	    leftHeight = pNodeA->left->height;
	if ( pNodeA->right )
	    rightHeight = pNodeA->right->height;

	pNodeA->height = leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
    }

    if ( pNodeB )
    {
	leftHeight = 0;
	rightHeight = 0;
	
	if ( pNodeB->left )
	    leftHeight = pNodeB->left->height;
	if ( pNodeB->right )
	    rightHeight = pNodeB->right->height;

	pNodeB->height = leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
    }

    if ( pNodeP )
    {

	leftHeight = 0;
	rightHeight = 0;
	
	if ( pNodeP->left )
	    leftHeight = pNodeP->left->height;
	if ( pNodeP->right )
	    rightHeight = pNodeP->right->height;
	
	pNodeP->height = leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
    }
}

template<class T> void AVLTreeT<T>::printTree()
{        
    cout << "-------------------------" << endl;
    printNode(_rootElement, 0);
    cout << "-------------------------" << endl;
}

template<class T> void AVLTreeT<T>::printNode(AVLElement *pNode, int level)
{

    if (pNode)
    {	

	if ( pNode->right)
	{
	    printNode(pNode->right,level+1);
	}

	int i=level;
	while ( i )
	{
	    cout << "   ";
	    i--;
	}

	cout << pNode->element << "/" << pNode->height;
	if ( pNode->parent )
	    cout << "/" << pNode->parent->element << endl;
	else
	    cout << " nil " << endl;

	if ( pNode->left)
	{
	    printNode(pNode->left, level+1);
	}
    }
}

#endif
