//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1998.
//
//  File:       winsplit.cxx
//
//  Contents:   Contains the code to do a window split.
//
//  Classes:    CTableWindowSplit
//
//  Functions:  
//
//  History:    1-08-95   srikants   Created
//
//----------------------------------------------------------------------------


#include "pch.cxx"
#pragma hdrstop

#include "winsplit.hxx"
#include "tabledbg.hxx"

//+---------------------------------------------------------------------------
//
//  Function:   CTableWindowSplit     ~ctor
//
//  Synopsis:   Constructor for the CTableWindowSplit class.
//
//  Arguments:  [srcWindow]             -   The source window that needs to be
//              split
//              [iSplitVisibleRowIndex] -   Index in the source window's visible
//              row index which is the split index. All rows <= 
//              iSplitVisibleRowIndex will be in the left hand window and the rest
//              in the right hand window after the split.
//
//  History:    1-09-95   srikants   Created
//
//  Notes:      
//
//----------------------------------------------------------------------------

CTableWindowSplit::CTableWindowSplit( CTableWindow & srcWindow,
                                      ULONG iSplitQueryRowIndex,
                                      ULONG segIdLeft, ULONG segIdRight,
                                      BOOL  fIsLastSegment )
    : _srcWindow(srcWindow),
      _pLeftWindow(0),
      _pRightWindow(0),
      _srcQueryRowIndex(srcWindow._GetInvisibleRowIndex()),
      _srcClientRowIndex(srcWindow._GetVisibleRowIndex()),
      _iSplitQueryRowIndex(iSplitQueryRowIndex),
      _iSplitClientRowIndex(-1),
      _segIdLeft(segIdLeft), _segIdRight(segIdRight),
      _fIsLastSegment(fIsLastSegment)
{

    Win4Assert( _srcQueryRowIndex.RowCount() >= 2 );
    Win4Assert( iSplitQueryRowIndex < _srcQueryRowIndex.RowCount()-1 );


    if ( _srcWindow.IsWatched() )
    {
        //
        // The source window has watch regions. The dynamic rowindex must
        // also be split.
        //
        TBL_OFF oQuerySplitRow = _srcQueryRowIndex.GetRow( iSplitQueryRowIndex );
        _iSplitClientRowIndex = _srcClientRowIndex.FindSplitPoint( oQuerySplitRow )-1;
    }
}


//+---------------------------------------------------------------------------
//
//  Function:   ~CTableWindowSplit  ~dtor for the CTableWindowSplit class
//
//  Synopsis:   Frees up the resources
//
//  History:    1-09-95   srikants   Created
//
//  Notes:      
//
//----------------------------------------------------------------------------

CTableWindowSplit::~CTableWindowSplit()
{
    delete _pLeftWindow;
    delete _pRightWindow;
}

//+---------------------------------------------------------------------------
//
//  Function:   CreateTargetWindows
//
//  Synopsis:   Creates the target windows and initializes their
//              notification region based on the source window notification
//              region.
//
//  History:    1-09-95   srikants   Created
//
//  Notes:      
//
//----------------------------------------------------------------------------

void CTableWindowSplit::CreateTargetWindows()
{

    //
    // First create the left and right windows
    //
    Win4Assert( 0 == _pLeftWindow );
    Win4Assert( 0 == _pRightWindow );

    _pLeftWindow  = new CTableWindow( _srcWindow, _segIdLeft );
    _pRightWindow = new CTableWindow( _srcWindow, _segIdRight );

}

//+---------------------------------------------------------------------------
//
//  Function:   TransferTargetWindows
//
//  Synopsis:   Transfers the control of the target windows to the caller.
//
//  Arguments:  [ppLeftWindow]  - (output) The pointer to the left window.
//              [ppRightWindow] - (output) The pointer to the right window.
//
//  History:    1-09-95   srikants   Created
//
//  Notes:      
//
//----------------------------------------------------------------------------

void CTableWindowSplit::TransferTargetWindows( CTableWindow ** ppLeftWindow,
                                              CTableWindow ** ppRightWindow )
{
    Win4Assert( 0 != ppLeftWindow && 0 != ppRightWindow );


    *ppLeftWindow  = _pLeftWindow;
    _pLeftWindow = 0;

    *ppRightWindow = _pRightWindow;
    _pRightWindow = 0;
}


//+---------------------------------------------------------------------------
//
//  Function:   _CopyWithoutNotifications
//
//  Synopsis:   Copies rows from the source window to the destination window. Only
//              the rows in the "srcRowIndex" will be copied to the
//              destination.
//
//  Arguments:  [destWindow]     -  Target window to copy to.
//              [srcRowIndex]    -  The source row index.
//              [iStartRowIndex] -  Starting offset in the row index to start
//              copying rows from.
//              [iEndRowIndex]   -  Ending offset in the row index to stop
//              copying.
//
//  History:    1-09-95   srikants   Created
//
//  Notes:      
//
//----------------------------------------------------------------------------

void CTableWindowSplit::_CopyWithoutNotifications( CTableWindow & destWindow,
                            CRowIndex & srcRowIndex,
                            ULONG iStartRowIndex, ULONG iEndRowIndex )
{

    Win4Assert( iEndRowIndex < srcRowIndex.RowCount() );

    for ( ULONG i = iStartRowIndex; i <= iEndRowIndex; i++ )
    {
        destWindow._PutRowToVisibleRowIndex( _srcWindow, srcRowIndex.GetRow(i) );
    }
}

//+---------------------------------------------------------------------------
//
//  Function:   _SimpleSplit
//
//  Synopsis:   Does a simple split in which the first half of the rows from
//              the source row index will be copied to the left window and the
//              second half to the right window.
//
//  History:    1-31-95   srikants   Created
//
//  Notes:      This must be called only when there is no notification region
//              in the source window.
//
//----------------------------------------------------------------------------

void CTableWindowSplit::_SimpleSplit()
{
    
    Win4Assert( !_srcWindow.IsWatched() );

    //
    // The source notifcation region is completely empty. We have to copy from
    // the visible row index only.
    //
    tbDebugOut(( DEB_WINSPLIT, "CTableWindowSplit::Simple Split\n" ));
    _CopyWithoutNotifications( *_pLeftWindow,  _srcQueryRowIndex,
                               0, _iSplitQueryRowIndex );
    _CopyWithoutNotifications( *_pRightWindow, _srcQueryRowIndex,
                               _iSplitQueryRowIndex+1,
                               _srcQueryRowIndex.RowCount()-1 );
    
}


//+---------------------------------------------------------------------------
//
//  Function:   DoSplit
//
//  Synopsis:   Splits the source window into left and right windows.
//
//  History:    1-09-95   srikants   Created
//
//  Notes:      
//
//----------------------------------------------------------------------------

void CTableWindowSplit::DoSplit()
{
    Win4Assert( 0 != _pLeftWindow && 0 != _pRightWindow );

    _pLeftWindow->_SetSplitInProgress();
    _pRightWindow->_SetSplitInProgress();

    if ( !_srcWindow.IsWatched() )
    {
        //
        // The source notifcation region is completely empty. We have to copy from
        // the visible row index only.
        //
        Win4Assert( -1 == _iSplitClientRowIndex );
        _SimpleSplit();

        _pLeftWindow->_SetSplitDone();
        _pRightWindow->_SetSplitDone();
        
    }
    else
    {
        //
        // If notifications are enabled in the target window, then we must copy
        // both the client row index and the query row index. O/W, just copy
        // the contents of the query row index.
        //

        //
        // Copy the contents to the target left window.
        //
        tbDebugOut(( DEB_WINSPLIT, "Left Window with Notifications\n" ));
        _CopyWithNotifications( *_pLeftWindow, 0, _iSplitQueryRowIndex,
                                0, _iSplitClientRowIndex );
        //
        // Copy the contents to the target right window.
        //
        tbDebugOut(( DEB_WINSPLIT, "Right Window with Notifications\n" ));
        _CopyWithNotifications( *_pRightWindow,
                   _iSplitQueryRowIndex+1,  _srcQueryRowIndex.RowCount()-1,
                   _iSplitClientRowIndex+1, _srcClientRowIndex.RowCount()-1 );

        //
        // Now apply the watch regions on the new windows.
        //
        _CopyWatchRegions();

        //
        // Indicate that the split is done.
        //
        _pLeftWindow->_SetSplitDone();
        _pRightWindow->_SetSplitDone();

        //
        // If any of the windows doesn't have any watch regions, we have to
        // do a state change to delete the dynamic/static split.
        //

        Win4Assert( _pLeftWindow->IsWatched() || _pRightWindow->IsWatched() );

        if ( !_pLeftWindow->IsWatched() )
        {
            _pLeftWindow->_EndStaticDynamicSplit();    
        }

        if ( !_pRightWindow->IsWatched() )
        {
            _pRightWindow->_EndStaticDynamicSplit();    
        }
    }

    //
    // Setup the lowest & highest keys for the left and right windows.
    //
    _srcWindow.GetSortKey( 0, _pLeftWindow->GetLowestKey() );
    _srcWindow.GetSortKey( _iSplitQueryRowIndex+1, _pRightWindow->GetLowestKey() );

    _srcWindow.GetSortKey( _iSplitQueryRowIndex, _pLeftWindow->GetHighestKey() );
    _srcWindow.GetSortKey( (ULONG) _srcWindow.RowCount()-1, _pRightWindow->GetHighestKey() );
}

//+---------------------------------------------------------------------------
//
//  Function:   _CopyWithNotifications
//
//  Synopsis:   Copies contents of both the "visible" and "dynamic" row index
//              from the source window to the destination window. 
//
//  Arguments:  [destWindow]        -   Destination window to copy to.
//              [iStartVisRowIndex] -   Starting offset in the visible rowindex.
//              [iEndVisRowIndex]   -   Ending offset in the visible rowindex.
//              [iStartDynRowIndex] -   Starting offset in the dynamic rowindex.
//              [iEndDynRowIndex]   -   Ending offset in the dynamic rowindex.
//
//  History:    1-09-95   srikants   Created
//
//  Notes:      
//
//----------------------------------------------------------------------------

void CTableWindowSplit::_CopyWithNotifications( CTableWindow & destWindow,
                                   ULONG iStartQueryRowIndex,  ULONG iEndQueryRowIndex,
                                   LONG  iStartClientRowIndex,  LONG iEndClientRowIndex )
{

    Win4Assert( _srcWindow.IsWatched() );
    
    Win4Assert( iStartQueryRowIndex < _srcQueryRowIndex.RowCount() );
    Win4Assert( iEndQueryRowIndex >= iStartQueryRowIndex &&
                iEndQueryRowIndex < _srcQueryRowIndex.RowCount() );
    
    //
    // Copy the rows from the client row index.
    //
    for ( LONG j = iStartClientRowIndex; j <= iEndClientRowIndex; j++ )
    {
        destWindow._PutRowToVisibleRowIndex( _srcWindow, _srcClientRowIndex.GetRow(j) );
    }

    //
    // Copy the rows from the query row index.
    //
    for ( ULONG i = iStartQueryRowIndex; i <= iEndQueryRowIndex; i++ )
    {
        destWindow._PutRowToDynRowIndex( _srcWindow, _srcQueryRowIndex.GetRow(i) );
    }
}

//+---------------------------------------------------------------------------
//
//  Member:     CTableWindowSplit::_IsOffsetInLeftWindow
//
//  Synopsis:   
//
//  Arguments:  [iOffset] - 
//
//  Returns:    
//
//  Modifies:   
//
//  History:    7-27-95   srikants   Created
//
//  Notes:      
//
//----------------------------------------------------------------------------

inline
BOOL CTableWindowSplit::_IsOffsetInLeftWindow( long iOffset )
{
    return iOffset <= _iSplitClientRowIndex;    
}

//+---------------------------------------------------------------------------
//
//  Member:     CTableWindowSplit::_CopyWatchRegions
//
//  Synopsis:   
//
//  Modifies:   
//
//  History:    7-27-95   srikants   Created
//
//  Notes:      
//
//----------------------------------------------------------------------------

void CTableWindowSplit::_CopyWatchRegions()
{
    Win4Assert( _srcWindow.IsWatched() );

    for ( unsigned i = 0; i < _srcWindow._aWindowWatch.Count(); i++ )
    {
        CWindowWatch & watch = _srcWindow._aWindowWatch.Get(i);

        long cResidual = watch._cRowsLeft;

        if ( _IsOffsetInLeftWindow( watch._iRowStart ) )
        {
            cResidual -= _pLeftWindow->AddWatch( watch._hRegion,
                                                     watch._iRowStart,
                                                     cResidual,
                                                     FALSE );
            if ( cResidual > 0 )
            {
                _pRightWindow->AddWatch( watch._hRegion,
                                         0,
                                         cResidual,
                                         _fIsLastSegment );
            }
        }
        else
        {
            //
            // The offset must be in the right hand side window.
            //
            Win4Assert( watch._iRowStart > _iSplitClientRowIndex &&
                        watch._iRowStart < (long) _srcWindow.RowCount() );

            Win4Assert( (long) _pLeftWindow->RowCount() == _iSplitClientRowIndex+1 );

           _pRightWindow->AddWatch( watch._hRegion,
                                    (LONG) (watch._iRowStart - _pLeftWindow->RowCount()),
                                    cResidual,
                                    _fIsLastSegment );
        }

    }
}