//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation 1995 - 1998
//
// File:        RowMan.hxx
//
// Contents:    Distributed HROW manager.
//
// Classes:     CHRowManager
//
// History:     05-Jun-95       KyleP       Created
//
//----------------------------------------------------------------------------

#pragma once

//+---------------------------------------------------------------------------
//
//  Class:      CHRowManager
//
//  Purpose:    Distributed HROW manager.
//
//  History:    05-Jun-95   KyleP       Created.
//
//----------------------------------------------------------------------------

class CHRowManager
{
public:

    CHRowManager();

    ~CHRowManager();

    //
    // For bookmark hints
    //

    void TrackSiblings( unsigned cChild );
    inline BOOL IsTrackingSiblings();

    //
    // Set modification
    //

    HROW Add( unsigned iChild, HROW hrow );
    HROW Add( unsigned iChild, HROW const * ahrow );
    void AddRef( HROW hrow );
    void Release( HROW hrow );

    //
    // Row access
    //

    inline HROW ChildHROW( HROW hrow );

    inline unsigned GetChildAndHROW( HROW hrow, HROW & hrowChild );

    HROW * GetChildAndHROWs( HROW hrow, unsigned & iChild );

    BOOL IsSame( HROW hrow1, HROW hrow2 );

private:

    inline unsigned ConvertAndValidate( HROW hrow );
    inline int InnerAdd( unsigned iChild, HROW hrow );
    void Grow();

    //
    // Tracks single row
    //

    class CHRow
    {
    public:

        CHRow() : _cRef(0) {}

        void Init( unsigned iChild, HROW hrow )
        {
            Win4Assert( !IsInUse() );
            _cRef = 1;
            _iChild = iChild;
            _hrow = hrow;
        }

        //
        // Refcounting
        //

        void AddRef()  { _cRef++; }
        void Release() { _cRef--; }
        BOOL IsInUse() { return _cRef > 0; }

        //
        // Member access
        //

        int Child() { return _iChild; }
        HROW HRow() { return _hrow; }

        //
        // Linking
        //

        void Link( int iNext )
        {
            Win4Assert( _cRef == 0 );
            _iChild = iNext;
        }

        int Next()
        {
            Win4Assert( _cRef == 0 );
            return _iChild;
        }

    private:

        unsigned _cRef;
        unsigned _iChild;
        HROW     _hrow;
    };

    //
    // Dynamic array of HROW, linked into a free list.
    //

    CHRow *  _aHRow;
    unsigned _cHRow;
    int      _iFirstFree;

    //
    // For tracking siblings. Two dimensional array: _cChild x _cHRow.
    //

    HROW *   _ahrowHint;
    unsigned _cChild;
};

//+---------------------------------------------------------------------------
//
//  Member:     CHRowManager::IsTrackingSiblings, public
//
//  Returns:    TRUE if the row manager is tracking HROW 'hints' for other
//              cursors.
//
//  History:    05-Jun-95   KyleP       Created.
//
//----------------------------------------------------------------------------

inline BOOL CHRowManager::IsTrackingSiblings()
{
    return( 0 != _ahrowHint );
}

//+---------------------------------------------------------------------------
//
//  Member:     CHRowManager::ChildHROW, public
//
//  Arguments:  [hrow] -- Distributed HROW
//
//  Returns:    HROW used by child cursor.
//
//  History:    05-Jun-95   KyleP       Created.
//
//----------------------------------------------------------------------------

inline HROW CHRowManager::ChildHROW( HROW hrow )
{
    unsigned iRow = ConvertAndValidate( hrow );

    return( _aHRow[iRow].HRow() );
}

//+---------------------------------------------------------------------------
//
//  Member:     CHRowManager::GetChildAndHROW, public
//
//  Arguments:  [hrow]      -- Distributed HROW
//              [hrowChild] -- HROW used by child cursor returned here.
//
//  Returns:    Index of child cursor for [hrow].
//
//  History:    05-Jun-95   KyleP       Created.
//
//----------------------------------------------------------------------------

inline unsigned CHRowManager::GetChildAndHROW( HROW hrow, HROW & hrowChild )
{
    unsigned iRow = ConvertAndValidate( hrow );

    hrowChild = _aHRow[iRow].HRow();
    return _aHRow[iRow].Child();
}

//+---------------------------------------------------------------------------
//
//  Member:     CHRowManager::GetChildAndHROWs, public
//
//  Arguments:  [hrow]   -- Distributed HROW
//              [iChild] -- Index of child cursor returned here.
//
//  Returns:    Array of HROW 'hints', one per cursor.  The [iChild] element
//              is not a hint.  It is the HROW for the owning cursor.
//
//  History:    05-Jun-95   KyleP       Created.
//
//----------------------------------------------------------------------------

inline HROW * CHRowManager::GetChildAndHROWs( HROW hrow, unsigned & iChild )
{
    Win4Assert( IsTrackingSiblings() );

    unsigned iRow = ConvertAndValidate( hrow );

    iChild = _aHRow[iRow].Child();

    return _ahrowHint + iRow * _cChild;
}

//+---------------------------------------------------------------------------
//
//  Member:     CHRowManager::ConvertAndValidate, public
//
//  Arguments:  [hrow]   -- Distributed HROW
//
//  Returns:    Index into _aHRow of row.
//
//  History:    05-Jun-95   KyleP       Created.
//
//----------------------------------------------------------------------------

inline unsigned CHRowManager::ConvertAndValidate( HROW hrow )
{
    unsigned iRow = (unsigned)hrow - 1; // we are 0 based while hrow is 1 based

    if ( iRow >= _cHRow || !_aHRow[iRow].IsInUse() )
    {
        Win4Assert( !"Invalid HROW" );
        vqDebugOut(( DEB_ERROR, "Invalid HROW 0x%x\n", iRow ));
        THROW( CException( DB_E_BADROWHANDLE ) );
    }

    return iRow;
}

//+---------------------------------------------------------------------------
//
//  Member:     CHRowManager::InnerAdd, public
//
//  Synopsis:   The guts of Add*
//
//  Arguments:  [iChild] -- Index of governing child cursor.
//              [hrow]   -- HROW used by child cursor
//
//  Returns:    Index of row in _aHRow
//
//  History:    05-Jun-95   KyleP       Created.
//
//----------------------------------------------------------------------------

inline int CHRowManager::InnerAdd( unsigned iChild, HROW hrow )
{
    if ( _iFirstFree == -1 )
        Grow();

    Win4Assert( _iFirstFree != -1 );

    int iCurrent = _iFirstFree;

    _iFirstFree = _aHRow[iCurrent].Next();

    _aHRow[iCurrent].Init( iChild, hrow );

    return iCurrent;
}