#ifndef _GRAPGT_H_INCLUDED_
#define _GRAPGT_H_INCLUDED_
///////////////////////////////////////////////////////////////////////////////
//                                                         
// GrapgT.h
// -------
// Binary tree template implementation                                   
//
// Design and Implementation by Bjoern Lemke               
//                                                         
// (C)opyright 2020 Bjoern Lemke 
//
// INTERFACE MODULE
//
// Class: GraphT template class
// 
// Description: Dependency graph implementation
//
// Status: CLEAN
//
///////////////////////////////////////////////////////////////////////////////

#include "TreeT.h"
#include "SetT.h"

template <class T>
class GraphT {
    
public:
    
    GraphT();
    ~GraphT();
    
    bool isEmpty() const;
    void Empty();
    bool Insert(const T& element);
    bool Remove(const T& element);
    
    bool addDependency(const T& from, const T& to);

    SetT<T> getDependencySet(T& element) const;
    SetT<T> getDependencies(T& element) const;

        
    T* First() const;
    T* Next() const;
    
    unsigned long Size() const;
        
private:

    class GraphElement {
	
    public:

	GraphElement()
	{
	}

	GraphElement(const GraphElement& ge)
	{
	    *this = ge;
	}

	GraphElement(const T& element)
	{
	    _element = element;
	}

	~GraphElement()
	{
	}
	
	GraphElement& operator = ( const GraphElement& ge)
	{
	    _element = ge._element;
	    _depList = ge._depList;
	    return (*this);
	}
	
	bool operator == ( const GraphElement ge) const
	{
	    return ge._element == _element;
	}

	bool operator < ( const GraphElement ge) const
	{
	    return ge._element < _element;
	}

	bool operator > ( const GraphElement ge) const
	{
	    return ge._element > _element;
	}

	bool addDependency(const T& ge)
	{
	    return _depList.Insert(ge);
	}    

	T _element;
	SetT<T> _depList;
    };


    TreeT<GraphElement> _graphTree;

    void traverseDependencies(T& element, SetT<T>& depList) const;
};

template<class T> GraphT<T>::GraphT()
{
    
}

template<class T> GraphT<T>::~GraphT()
{
    
}

template<class T> bool GraphT<T>::isEmpty() const
{
    return _graphTree.isEmpty();
}

template<class T> void GraphT<T>::Empty()
{
    _graphTree.Empty();
}

template<class T> bool GraphT<T>::Insert(const T& element)
{
    return _graphTree.Insert( GraphElement(element) );
}

template<class T> bool GraphT<T>::Remove(const T& element)
{
    return _graphTree.Remove( GraphElement(element) );
}

template<class T> bool GraphT<T>::addDependency(const T& from, const T& to)
{
    GraphElement* pFrom = _graphTree.Find(GraphElement(from));
    GraphElement* pTo = _graphTree.Find(GraphElement(to));
    if ( pFrom && pTo )
    {
	return pFrom->addDependency(to);
    }
    return false;
}

template<class T> SetT<T> GraphT<T>::getDependencySet(T& element) const
{
    GraphElement* pGE = _graphTree.Find(GraphElement(element));
    if ( pGE )
    {
	return pGE->_depList;
    }
    else
    {
	SetT<T> nullSet;
	return nullSet;
    }
}

template<class T> SetT<T> GraphT<T>::getDependencies(T& element) const
{
    SetT<T> depList;
    traverseDependencies(element, depList);
    return depList;
}

template<class T> void GraphT<T>::traverseDependencies(T& element, SetT<T>& depList) const
{   
    GraphElement* pGE = _graphTree.Find(GraphElement(element));
    if ( pGE )
    {
	T* pDep = pGE->_depList.First();
	while ( pDep )
	{
	    if ( depList.Find(*pDep) == 0 )
	    {
		depList.Insert(*pDep);
		traverseDependencies(*pDep, depList);
	    }
	    pDep = pGE->_depList.Next();
	}
    }
}

template<class T> T* GraphT<T>::First() const
{
    GraphElement* ge = _graphTree.First();
    if ( ge )
	return (&(ge->_element));
    return 0; 
}

template<class T> T* GraphT<T>::Next() const
{
    GraphElement* ge = _graphTree.Next();
    if ( ge )
	return (&(ge->_element));
    return 0;
}

template<class T> unsigned long GraphT<T>::Size() const
{
    return _graphTree.Size();
}

#endif
