/*++

Copyright (c) 1995  Microsoft Corporation

Module Name:


Abstract:

     map.hxx

Author:

    Benjamin Leis (benl)  11/13/95

Revision History:

--*/

#ifndef  _MAP
    #define _MAP

    #define MAP_STACK_SIZE 40

//+---------------------------------------------------------------------------
//
//  Class:      CMap ()
//
//  Purpose:
//
//  Interface:  CMap       --
//              ~CMap      --
//              Insert     --  insert a key and item into map
//              Replace    --  replaces a key's data or inserts it if is not in
//                            the map already
//              Remove     --
//              Lookup     --
//              DeleteAll  -- removes all the entries in the map and sets it to
//                            empty
//              Lookup     --
//              Enum       --
//              DeleteNode --
//              pRoot      --
//
//  History:    12-05-1996   benl   Created
//              12-18-1997   benl   modified
//
//  Notes:      works by copying values not be reference - so need constructors
//              / destructors
//
//----------------------------------------------------------------------------

template <class T, class S> class CMapIter;

template <class T, class S> class CMap {
public:
    friend CMapIter<T,S>;

    CMap(void);
    ~CMap(void);

    void Insert(const T & tKey, const S & sItem);
    void Replace(const T & tkey, const S& sItem);
    BOOL Remove(const T & tKey);
    BOOL Lookup(const T & tKey, S & sItem) const;
    void DeleteAll();

    // Second style of lookup
    S * Lookup(const T & tKey);
    CMapIter<T,S> * Enum();

protected:

    class CNode {
    public:
        CNode(const T & t, const S & s) {
            tKey = t;
            sData = s;
            pLeft = NULL;
            pRight = NULL;
            bDeleted = FALSE;
        }

        CNode() {
            pLeft = NULL;
            pRight = NULL;
            bDeleted = FALSE;
        }

        CNode * pLeft;
        CNode * pRight;
        T    tKey;
        S    sData;
        BOOL bDeleted;
    };

    void DeleteNode(CNode * pNode);

    CNode * pRoot;
};


//+---------------------------------------------------------------------------
//
//  Class:      CMapIter ()
//
//  Purpose:
//
//  Interface:  CMapIter   --
//              ~CMapIter  --
//              Next       --
//              _nodeStack --
//              _iTop      --
//              _nodeDummy --
//
//  History:    2-13-1997   benl   Created
//
//  Notes:
//
//----------------------------------------------------------------------------

template <class T, class S> class CMapIter {
public:
    CMapIter ( CMap<T,S>::CNode * pRoot);
    ~CMapIter();

    BOOLEAN Next(T & t, S & s);

private:
    //data
    CMap<T,S>::CNode * _nodeStack[MAP_STACK_SIZE];
    INT                _iTop;
    CMap<T,S>::CNode   _nodeDummy;
};


//+---------------------------------------------------------------------------
//
//  Member:     CMapIter::CMapiter
//
//  Synopsis:   Constructor
//
//  Arguments:  [pRoot] --
//
//  Returns:
//
//  History:    7-10-1997   benl   Created
//
//  Notes:      called by CMap and given root to traverse
//
//----------------------------------------------------------------------------

template <class T, class S>
CMapIter<T,S>::CMapIter(CMap<T,S>::CNode * pRoot)
{
    _iTop = 0;
    _nodeStack[_iTop] = pRoot;

    while (_nodeStack[_iTop]->pLeft != NULL) {
//        assert(_iTop < MAP_STACK_SIZE);
        _nodeStack[_iTop + 1] = _nodeStack[_iTop]->pLeft;
        _iTop++;
    }

    //add a dummy node at the bottom
//    assert(_iTop < MAP_STACK_SIZE);
    _nodeStack[_iTop + 1] = &_nodeDummy;
    _iTop++;
} // ::CNode


//+---------------------------------------------------------------------------
//
//  Member:     ::~CMapIter
//
//  Synopsis:
//
//  Arguments:  (none)
//
//  Returns:
//
//  History:    2-13-1997   benl   Created
//
//  Notes:
//
//----------------------------------------------------------------------------

template <class T, class S>
CMapIter<T,S>::~CMapIter()
{
} //::~CMapIter


//+---------------------------------------------------------------------------
//
//  Member:     ::Next
//
//  Synopsis:
//
//  Arguments:  [t] --
//              [s] --
//
//  Returns:
//
//  History:    2-13-1997   benl   Created
//
//  Notes:
//
//----------------------------------------------------------------------------

template <class T, class S>
BOOLEAN CMapIter<T,S>::Next(T & t, S & s)
{
    CMap<T,S>::CNode * pTemp;

    if (_nodeStack[_iTop]->pRight != NULL) {
        //go to the leftmostest item below
        _nodeStack[_iTop] = _nodeStack[_iTop]->pRight;
        while (_nodeStack[_iTop]->pLeft != NULL) {
//            assert(_iTop < MAP_STACK_SIZE);
            _nodeStack[_iTop + 1] = _nodeStack[_iTop]->pLeft;
            _iTop++;
        }
    } else if (_iTop != 0) {
        // o.w backtrack up one level
        _iTop--;
    } else {
        //o.w we're done
        return FALSE;
    }

    //if current element is deleted recurse
    if (_nodeStack[_iTop]->bDeleted) {
        return Next(t, s);
    } else {
        t = _nodeStack[_iTop]->tKey;
        s = _nodeStack[_iTop]->sData;
        return TRUE;
    }

} //::Next



//+---------------------------------------------------------------------------
//
//  Member:     CMap::CMap
//
//  Synopsis:
//
//  Arguments:  (none)
//
//  Returns:
//
//  History:    12-05-1996   benl   Created
//
//  Notes:
//
//----------------------------------------------------------------------------

template<class T, class S>
inline CMap<T,S>::CMap(void)
{
    pRoot = NULL;
} //::CMap


//+---------------------------------------------------------------------------
//
//  Member:     ::DeleteNode
//
//  Synopsis:
//
//  Arguments:  [pNode] --
//
//  Returns:
//
//  History:    12-05-1996   benl   Created
//
//  Notes:
//
//----------------------------------------------------------------------------

template<class T, class S>
inline void CMap<T,S>::DeleteNode(CNode * pNode)
{
    if (pNode != NULL) {
        DeleteNode(pNode->pLeft);
        DeleteNode(pNode->pRight);
        delete pNode;
    }
} //::DeleteNode



//+---------------------------------------------------------------------------
//
//  Member:     ::~CMap
//
//  Synopsis:
//
//  Arguments:  (none)
//
//  Returns:
//
//  History:    12-05-1996   benl   Created
//
//  Notes:
//
//----------------------------------------------------------------------------

template<class T, class S>
inline CMap<T,S>::~CMap(void)
{
//     printf("map destruct %x\n", this);
    DeleteAll();
} //::~CMap


//+---------------------------------------------------------------------------
//
//  Member:     ::Insert
//
//  Synopsis:
//
//  Arguments:  [tKey]  --
//              [sData] --
//
//  Returns:
//
//  History:    12-05-1996   benl   Created
//
//  Notes:
//
//----------------------------------------------------------------------------

template<class T, class S>
inline void CMap<T,S>::Insert(const T & tKey, const S & sData)
{
    CNode * pnodeTemp = pRoot;

    if (pRoot == NULL)
        pRoot = new CNode(tKey,sData);
    else {
        while (pnodeTemp != NULL) {
            if (pnodeTemp->tKey <= tKey) {
                if (pnodeTemp->pRight == NULL) {
                    pnodeTemp->pRight = new CNode(tKey,sData);
                    return;
                } else pnodeTemp = pnodeTemp->pRight;
            } else {
                if (pnodeTemp->pLeft == NULL) {
                    pnodeTemp->pLeft = new CNode(tKey,sData);
                    return;
                } else pnodeTemp = pnodeTemp->pLeft;
            }
        } //endwhile
    } //endif
} //::Insert


//+---------------------------------------------------------------------------
//
//  Member:     ::Replace
//
//  Synopsis:   if the key exits replace its data with the new data
//              o.w just insert it
//
//  Arguments:  [tKey]  -- the key to search for
//              [sData] -- the new data
//
//  Returns:    always succeeds
//
//  History:    12-05-1996   benl   Created
//
//  Notes:
//
//----------------------------------------------------------------------------

template<class T, class S>
inline void CMap<T,S>::Replace(const T & tKey, const S & sData)
{
    CNode * pnodeTemp = pRoot;

    if (pRoot == NULL) {
        pRoot = new CNode(tKey,sData);
    } else {
        while (pnodeTemp != NULL) {
            //match - so replace its data
            if (pnodeTemp->tKey == tKey && !pnodeTemp->bDeleted) {
                pnodeTemp->sData = sData;
                return;
            }
            //recurse right
            else if (pnodeTemp->tKey < tKey) {
                if (NULL == pnodeTemp->pRight) {
                    pnodeTemp->pRight = new CNode(tKey,sData);
                    return;
                } else pnodeTemp = pnodeTemp->pRight;
            }
            //recurse left
            else {
                if (NULL == pnodeTemp->pLeft) {
                    pnodeTemp->pLeft = new CNode(tKey,sData);
                    return;
                } else pnodeTemp = pnodeTemp->pLeft;
            }
        } //endwhile
    } //endif
} //::Replace

//+---------------------------------------------------------------------------
//
//  Member:     ::Remove
//
//  Synopsis:
//
//  Arguments:  [tKey] --
//
//  Returns:
//
//  History:    12-05-1996   benl   Created
//
//  Notes:
//
//----------------------------------------------------------------------------

template<class T, class S>
inline BOOL CMap<T,S>::Remove(const T & tKey)
{
    CNode * pnodeTemp = pRoot;
    CNode * pnodeParent = NULL;
    CNode * pnodeOrphan = NULL;

    if (pRoot == NULL)
        return FALSE;
    else {
        while (pnodeTemp != NULL) {
            if (pnodeTemp->tKey == tKey && (pnodeTemp->bDeleted == FALSE)) {

                if (pnodeParent == NULL) {

                    //
                    //  deleting the root
                    // 

                    if (pnodeTemp->pLeft != NULL) {
                        pRoot = pnodeTemp->pLeft;
                        pnodeOrphan = pnodeTemp->pRight;
                    } else {
                        pRoot = pnodeTemp->pRight;
                        pnodeOrphan = pnodeTemp->pLeft;
                    }
                    
                } else {

                    //
                    //  deleting an interor node
                    // 

                    if (pnodeParent->pLeft == pnodeTemp) {
                        pnodeParent->pLeft = pnodeTemp->pRight;
                        pnodeOrphan = pnodeTemp->pLeft;
                    } else {
                        pnodeParent->pRight = pnodeTemp->pLeft;
                        pnodeOrphan = pnodeTemp->pRight;
                    }
                }

                delete pnodeTemp;

                //
                //  re-insert the orphan
                // 

                if (pnodeOrphan != NULL) {

                    pnodeTemp = pRoot;

                    while (pnodeTemp != NULL) {
                        if (pnodeTemp->tKey <= pnodeOrphan->tKey) {
                            if (pnodeTemp->pRight == NULL) {
                                pnodeTemp->pRight = pnodeOrphan;
                                break;
                            } else pnodeTemp = pnodeTemp->pRight;
                        } else {
                            if (pnodeTemp->pLeft == NULL) {
                                pnodeTemp->pLeft = pnodeOrphan;
                                break;
                            } else pnodeTemp = pnodeTemp->pLeft;
                        }
                    } //endwhile
                }

                return TRUE;
            } else if (pnodeTemp->tKey < tKey) {
                pnodeParent = pnodeTemp;
                pnodeTemp = pnodeTemp->pRight;
            } else {
                pnodeParent = pnodeTemp;
                pnodeTemp = pnodeTemp->pLeft;
            }
        } //endwhile
    } //endif
    return FALSE;
} //::Remove


//+---------------------------------------------------------------------------
//
//  Member:     ::Lookup
//
//  Synopsis:
//
//  Arguments:  [tKey]  --
//              [sItem] --
//
//  Returns:
//
//  History:    12-05-1996   benl   Created
//
//  Notes:
//
//----------------------------------------------------------------------------

template<class T, class S>
inline BOOL CMap<T,S>::Lookup(const T & tKey, S & sItem) const
{
    CNode * pnodeTemp = pRoot;

    if (pRoot == NULL)
        return FALSE;
    else {
        while (pnodeTemp != NULL) {
            if (pnodeTemp->tKey == tKey && pnodeTemp->bDeleted == FALSE) {
                sItem = pnodeTemp->sData;
                return TRUE;
            } else if (pnodeTemp->tKey <= tKey) {
                pnodeTemp = pnodeTemp->pRight;
            } else {
                pnodeTemp = pnodeTemp->pLeft;
            }
        } //endwhile
    } //endif
    return FALSE;
} //::Lookup


//+---------------------------------------------------------------------------
//
//  Member:     ::Lookup
//
//  Synopsis:
//
//  Arguments:  [tKey] --
//
//  Returns:
//
//  History:    12-05-1996   benl   Created
//
//  Notes:
//
//----------------------------------------------------------------------------

template<class T, class S>
inline S * CMap<T,S>::Lookup(const T & tKey)
{
    CNode * pnodeTemp = pRoot;

    if (pRoot == NULL)
        return NULL;
    else {
        while (pnodeTemp != NULL) {
            if (pnodeTemp->tKey == tKey && pnodeTemp->bDeleted == FALSE) {
                return &(pnodeTemp->sData);
            } else if (pnodeTemp->tKey < tKey) {
                pnodeTemp = pnodeTemp->pRight;
            } else {
                pnodeTemp = pnodeTemp->pLeft;
            }
        } //endwhile
    } //endif
    return NULL;
} //::Lookup


//+---------------------------------------------------------------------------
//
//  Member:     ::Enum
//
//  Synopsis:   returns the iterator
//
//  Arguments:  (none)
//
//  Returns:    iterator or null if map is empty
//
//  History:    2-13-1997   benl   Created
//
//  Notes:
//
//----------------------------------------------------------------------------

template<class T, class S>
inline CMapIter<T,S> * CMap<T,S>::Enum()
{
    if (pRoot) {
        return new CMapIter<T,S>(pRoot);
    } else {
        return NULL;
    }

} //::Enum


//+---------------------------------------------------------------------------
//
//  Member:      ::DeleteAll
//
//  Synopsis: NonRecursive delete
//
//  Arguments:  (none)
//
//  Returns:
//
//  History:    12-18-1997   benl   Created
//
//  Notes:      Cleans up the entire tree and sets it to empty
//              Does it nonrecursively but swiveling ptrs around
//
//----------------------------------------------------------------------------

template <class T, class S>
inline void CMap<T,S>::DeleteAll()
{
    CNode * pParent = 0;
    CNode * pNext =  0;
    CNode * pCurrent = pRoot;

    while (pCurrent) {
        //advance next to left and the set the  left back to parent
        pNext = pCurrent->pLeft;
        pCurrent->pLeft = pParent;

        //If there is a left child move onto it
        //else if there is a right child move to it
        //o.w move up thru left ptr and delete current node
        if (pNext) {
            pParent = pCurrent;
            pCurrent = pNext;
        } else if (pCurrent->pRight) {
            pParent = pCurrent;
            pCurrent = pCurrent->pRight;
            pParent->pRight = 0;
        } else {
            //this is really a move up back the parent is now in pLeft
            pParent = pCurrent->pLeft;

            delete pCurrent;
            pCurrent = pParent;
            if (pCurrent) {
                pParent = pCurrent->pLeft;
                pCurrent->pLeft = 0;
            }
        }
    }

    pRoot = NULL;
} // ::DeleteAll



#endif