/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */
 
#include <tools/stream.hxx>
#include <tools/debug.hxx>
#include <regionband.hxx>
#include <osl/diagnose.h>
#include <sal/log.hxx>
 
RegionBand::RegionBand()
:   mpFirstBand(nullptr),
    mpLastCheckedBand(nullptr)
{
}
 
RegionBand::RegionBand(const RegionBand& rRef)
:   mpFirstBand(nullptr),
    mpLastCheckedBand(nullptr)
{
    *this = rRef;
}
 
RegionBand& RegionBand::operator=(const RegionBand& rRef)
{
    ImplRegionBand* pPrevBand = nullptr;
    ImplRegionBand* pBand = rRef.mpFirstBand;
 
    while(pBand)
    {
        ImplRegionBand* pNewBand = new ImplRegionBand(*pBand);
 
        // first element? -> set as first into the list
        if(pBand == rRef.mpFirstBand)
        {
            mpFirstBand = pNewBand;
        }
        else
        {
            pPrevBand->mpNextBand = pNewBand;
        }
 
        pPrevBand = pNewBand;
        pBand = pBand->mpNextBand;
    }
 
    return *this;
}
 
RegionBand::RegionBand(const tools::Rectangle& rRect)
:   mpFirstBand(nullptr),
    mpLastCheckedBand(nullptr)
{
    const long nTop(std::min(rRect.Top(), rRect.Bottom()));
    const long nBottom(std::max(rRect.Top(), rRect.Bottom()));
    const long nLeft(std::min(rRect.Left(), rRect.Right()));
    const long nRight(std::max(rRect.Left(), rRect.Right()));
 
    // add band with boundaries of the rectangle
    mpFirstBand = new ImplRegionBand(nTop, nBottom);
 
    // Set left and right boundaries of the band
    mpFirstBand->Union(nLeft, nRight);
 
}
 
void RegionBand::implReset()
{
    ImplRegionBand* pBand = mpFirstBand;
 
    while(pBand)
    {
        ImplRegionBand* pTempBand = pBand->mpNextBand;
        delete pBand;
        pBand = pTempBand;
    }
 
    mpLastCheckedBand = nullptr;
    mpFirstBand = nullptr;
}
 
RegionBand::~RegionBand()
{
    implReset();
}
 
bool RegionBand::operator==( const RegionBand& rRegionBand ) const
{
 
    // initialise pointers
    ImplRegionBand*      pOwnRectBand = mpFirstBand;
    ImplRegionBandSep*   pOwnRectBandSep = pOwnRectBand->mpFirstSep;
    ImplRegionBand*      pSecondRectBand = rRegionBand.mpFirstBand;
    ImplRegionBandSep*   pSecondRectBandSep = pSecondRectBand->mpFirstSep;
 
    while ( pOwnRectBandSep && pSecondRectBandSep )
    {
        // get boundaries of current rectangle
        long nOwnXLeft = pOwnRectBandSep->mnXLeft;
        long nSecondXLeft = pSecondRectBandSep->mnXLeft;
 
        if ( nOwnXLeft != nSecondXLeft )
        {
            return false;
        }
 
        long nOwnYTop = pOwnRectBand->mnYTop;
        long nSecondYTop = pSecondRectBand->mnYTop;
 
        if ( nOwnYTop != nSecondYTop )
        {
            return false;
        }
 
        long nOwnXRight = pOwnRectBandSep->mnXRight;
        long nSecondXRight = pSecondRectBandSep->mnXRight;
 
        if ( nOwnXRight != nSecondXRight )
        {
            return false;
        }
 
        long nOwnYBottom = pOwnRectBand->mnYBottom;
        long nSecondYBottom = pSecondRectBand->mnYBottom;
 
        if ( nOwnYBottom != nSecondYBottom )
        {
            return false;
        }
 
        // get next separation from current band
        pOwnRectBandSep = pOwnRectBandSep->mpNextSep;
 
        // no separation found? -> go to next band!
        if ( !pOwnRectBandSep )
        {
            // get next band
            pOwnRectBand = pOwnRectBand->mpNextBand;
 
            // get first separation in current band
            if( pOwnRectBand )
            {
                pOwnRectBandSep = pOwnRectBand->mpFirstSep;
            }
        }
 
        // get next separation from current band
        pSecondRectBandSep = pSecondRectBandSep->mpNextSep;
 
        // no separation found? -> go to next band!
        if ( !pSecondRectBandSep )
        {
            // get next band
            pSecondRectBand = pSecondRectBand->mpNextBand;
 
            // get first separation in current band
            if( pSecondRectBand )
            {
                pSecondRectBandSep = pSecondRectBand->mpFirstSep;
            }
        }
 
        if ( pOwnRectBandSep && !pSecondRectBandSep )
        {
            return false;
        }
 
        if ( !pOwnRectBandSep && pSecondRectBandSep )
        {
            return false;
        }
    }
 
    return true;
}
 
enum StreamEntryType { STREAMENTRY_BANDHEADER, STREAMENTRY_SEPARATION, STREAMENTRY_END };
 
bool RegionBand::load(SvStream& rIStrm)
{
    // clear this instance data
    implReset();
 
    // get all bands
    ImplRegionBand* pCurrBand = nullptr;
 
    // get header from first element
    sal_uInt16 nTmp16(STREAMENTRY_END);
    rIStrm.ReadUInt16(nTmp16);
 
    if (STREAMENTRY_END == static_cast<StreamEntryType>(nTmp16))
        return false;
 
    size_t nRecordsPossible = rIStrm.remainingSize() / (2*sizeof(sal_Int32));
    if (!nRecordsPossible)
    {
        OSL_ENSURE(false, "premature end of region stream" );
        implReset();
        return false;
    }
 
    do
    {
        // insert new band or new separation?
        if(STREAMENTRY_BANDHEADER == static_cast<StreamEntryType>(nTmp16))
        {
            sal_Int32 nYTop(0);
            sal_Int32 nYBottom(0);
 
            rIStrm.ReadInt32( nYTop );
            rIStrm.ReadInt32( nYBottom );
 
            // create band
            ImplRegionBand* pNewBand = new ImplRegionBand( nYTop, nYBottom );
 
            // first element? -> set as first into the list
            if ( !pCurrBand )
            {
                mpFirstBand = pNewBand;
            }
            else
            {
                pCurrBand->mpNextBand = pNewBand;
            }
 
            // save pointer for next creation
            pCurrBand = pNewBand;
        }
        else
        {
            sal_Int32 nXLeft(0);
            sal_Int32 nXRight(0);
 
            rIStrm.ReadInt32( nXLeft );
            rIStrm.ReadInt32( nXRight );
 
            // add separation
            if ( pCurrBand )
            {
                pCurrBand->Union( nXLeft, nXRight );
            }
        }
 
        if( rIStrm.eof() )
        {
            OSL_ENSURE(false, "premature end of region stream" );
            implReset();
            return false;
        }
 
        // get next header
        rIStrm.ReadUInt16( nTmp16 );
    }
    while (STREAMENTRY_END != static_cast<StreamEntryType>(nTmp16) && rIStrm.good());
    if (!CheckConsistency())
    {
        implReset();
        return false;
    }
    return true;
}
 
void RegionBand::save(SvStream& rOStrm) const
{
    ImplRegionBand* pBand = mpFirstBand;
 
    while(pBand)
    {
        // put boundaries
        rOStrm.WriteUInt16( STREAMENTRY_BANDHEADER );
        rOStrm.WriteInt32( pBand->mnYTop );
        rOStrm.WriteInt32( pBand->mnYBottom );
 
        // put separations of current band
        ImplRegionBandSep* pSep = pBand->mpFirstSep;
 
        while(pSep)
        {
            // put separation
            rOStrm.WriteUInt16( STREAMENTRY_SEPARATION );
            rOStrm.WriteInt32( pSep->mnXLeft );
            rOStrm.WriteInt32( pSep->mnXRight );
 
            // next separation from current band
            pSep = pSep->mpNextSep;
        }
 
        pBand = pBand->mpNextBand;
    }
 
    // put endmarker
    rOStrm.WriteUInt16( STREAMENTRY_END );
}
 
bool RegionBand::isSingleRectangle() const
{
    // just one band?
    if(mpFirstBand && !mpFirstBand->mpNextBand)
    {
        // just one sep?
        if(mpFirstBand->mpFirstSep && !mpFirstBand->mpFirstSep->mpNextSep)
        {
            return true;
        }
    }
 
    return false;
}
 
void RegionBand::InsertBand(ImplRegionBand* pPreviousBand, ImplRegionBand* pBandToInsert)
{
    OSL_ASSERT(pBandToInsert!=nullptr);
 
    if(!pPreviousBand)
    {
        // Insert band before all others.
        if(mpFirstBand)
        {
            mpFirstBand->mpPrevBand = pBandToInsert;
        }
 
        pBandToInsert->mpNextBand = mpFirstBand;
        mpFirstBand = pBandToInsert;
    }
    else
    {
        // Insert band directly after pPreviousBand.
        pBandToInsert->mpNextBand = pPreviousBand->mpNextBand;
        pPreviousBand->mpNextBand = pBandToInsert;
        pBandToInsert->mpPrevBand = pPreviousBand;
    }
 
}
 
void RegionBand::processPoints()
{
    ImplRegionBand* pRegionBand = mpFirstBand;
 
    while(pRegionBand)
    {
        // generate separations from the lines and process union
        pRegionBand->ProcessPoints();
        pRegionBand = pRegionBand->mpNextBand;
    }
 
}
 
/** This function is similar to the RegionBand::InsertBands() method.
    It creates a minimal set of missing bands so that the entire vertical
    interval from nTop to nBottom is covered by bands.
*/
void RegionBand::ImplAddMissingBands(const long nTop, const long nBottom)
{
    // Iterate over already existing bands and add missing bands atop the
    // first and between two bands.
    ImplRegionBand* pPreviousBand = nullptr;
    ImplRegionBand* pBand = ImplGetFirstRegionBand();
    long nCurrentTop (nTop);
 
    while (pBand != nullptr && nCurrentTop<nBottom)
    {
        if (nCurrentTop < pBand->mnYTop)
        {
            // Create new band above the current band.
            ImplRegionBand* pAboveBand = new ImplRegionBand(
                nCurrentTop,
                ::std::min(nBottom,pBand->mnYTop-1));
            InsertBand(pPreviousBand, pAboveBand);
        }
 
        // Adapt the top of the interval to prevent overlapping bands.
        nCurrentTop = ::std::max(nTop, pBand->mnYBottom+1);
 
        // Advance to next band.
        pPreviousBand = pBand;
        pBand = pBand->mpNextBand;
    }
 
    // We still have to cover two cases:
    // 1. The region does not yet contain any bands.
    // 2. The interval nTop->nBottom extends past the bottom most band.
    if (nCurrentTop <= nBottom
        && (pBand==nullptr || nBottom>pBand->mnYBottom))
    {
        // When there is no previous band then the new one will be the
        // first.  Otherwise the new band is inserted behind the last band.
        InsertBand(
            pPreviousBand,
            new ImplRegionBand(
                nCurrentTop,
                nBottom));
    }
 
}
 
void RegionBand::CreateBandRange(long nYTop, long nYBottom)
{
    // add top band
    mpFirstBand = new ImplRegionBand( nYTop-1, nYTop-1 );
 
    // begin first search from the first element
    mpLastCheckedBand = mpFirstBand;
    ImplRegionBand* pBand = mpFirstBand;
 
    for ( int i = nYTop; i <= nYBottom+1; i++ )
    {
        // create new band
        ImplRegionBand* pNewBand = new ImplRegionBand( i, i );
        pBand->mpNextBand = pNewBand;
 
        if ( pBand != mpFirstBand )
        {
            pNewBand->mpPrevBand = pBand;
        }
 
        pBand = pBand->mpNextBand;
    }
 
}
 
void RegionBand::InsertLine(const Point& rStartPt, const Point& rEndPt, long nLineId)
{
    long nX, nY;
 
    // lines consisting of a single point do not interest here
    if ( rStartPt == rEndPt )
    {
        return;
    }
 
    LineType eLineType = (rStartPt.Y() > rEndPt.Y()) ? LINE_DESCENDING : LINE_ASCENDING;
    if ( rStartPt.X() == rEndPt.X() )
    {
        // vertical line
        const long nEndY = rEndPt.Y();
 
        nX = rStartPt.X();
        nY = rStartPt.Y();
 
        if( nEndY > nY )
        {
            for ( ; nY <= nEndY; nY++ )
            {
                Point aNewPoint( nX, nY );
                InsertPoint( aNewPoint, nLineId,
                             (aNewPoint == rEndPt) || (aNewPoint == rStartPt),
                             eLineType );
            }
        }
        else
        {
            for ( ; nY >= nEndY; nY-- )
            {
                Point aNewPoint( nX, nY );
                InsertPoint( aNewPoint, nLineId,
                             (aNewPoint == rEndPt) || (aNewPoint == rStartPt),
                             eLineType );
            }
        }
    }
    else if ( rStartPt.Y() != rEndPt.Y() )
    {
        const long  nDX = labs( rEndPt.X() - rStartPt.X() );
        const long  nDY = labs( rEndPt.Y() - rStartPt.Y() );
        const long  nStartX = rStartPt.X();
        const long  nStartY = rStartPt.Y();
        const long  nEndX = rEndPt.X();
        const long  nEndY = rEndPt.Y();
        const long  nXInc = ( nStartX < nEndX ) ? 1 : -1;
        const long  nYInc = ( nStartY < nEndY ) ? 1 : -1;
 
        if ( nDX >= nDY )
        {
            const long  nDYX = ( nDY - nDX ) * 2;
            const long  nDY2 = nDY << 1;
            long        nD = nDY2 - nDX;
 
            for ( nX = nStartX, nY = nStartY; nX != nEndX; nX += nXInc )
            {
                InsertPoint( Point( nX, nY ), nLineId, nStartX == nX, eLineType );
 
                if ( nD < 0 )
                    nD += nDY2;
                else
                {
                    nD += nDYX;
                    nY += nYInc;
                }
            }
        }
        else
        {
            const long  nDYX = ( nDX - nDY ) * 2;
            const long  nDY2 = nDX << 1;
            long        nD = nDY2 - nDY;
 
            for ( nX = nStartX, nY = nStartY; nY != nEndY; nY += nYInc )
            {
                InsertPoint( Point( nX, nY ), nLineId, nStartY == nY, eLineType );
 
                if ( nD < 0 )
                    nD += nDY2;
                else
                {
                    nD += nDYX;
                    nX += nXInc;
                }
            }
        }
 
        // last point
        InsertPoint( Point( nEndX, nEndY ), nLineId, true, eLineType );
    }
}
 
void RegionBand::InsertPoint(const Point &rPoint, long nLineID, bool bEndPoint, LineType eLineType)
{
    SAL_WARN_IF( mpFirstBand == nullptr, "vcl", "RegionBand::InsertPoint - no bands available!" );
 
    if ( rPoint.Y() == mpLastCheckedBand->mnYTop )
    {
        mpLastCheckedBand->InsertPoint( rPoint.X(), nLineID, bEndPoint, eLineType );
        return;
    }
 
    if ( rPoint.Y() > mpLastCheckedBand->mnYTop )
    {
        // Search ascending
        while ( mpLastCheckedBand )
        {
            // Insert point if possible
            if ( rPoint.Y() == mpLastCheckedBand->mnYTop )
            {
                mpLastCheckedBand->InsertPoint( rPoint.X(), nLineID, bEndPoint, eLineType );
                return;
            }
 
            mpLastCheckedBand = mpLastCheckedBand->mpNextBand;
        }
 
        OSL_ENSURE(false, "RegionBand::InsertPoint reached the end of the list!" );
    }
    else
    {
        // Search descending
        while ( mpLastCheckedBand )
        {
            // Insert point if possible
            if ( rPoint.Y() == mpLastCheckedBand->mnYTop )
            {
                mpLastCheckedBand->InsertPoint( rPoint.X(), nLineID, bEndPoint, eLineType );
                return;
            }
 
            mpLastCheckedBand = mpLastCheckedBand->mpPrevBand;
        }
 
        OSL_ENSURE(false, "RegionBand::InsertPoint reached the beginning of the list!" );
    }
 
    OSL_ENSURE(false, "RegionBand::InsertPoint point not inserted!" );
 
    // reinitialize pointer (should never be reached!)
    mpLastCheckedBand = mpFirstBand;
}
 
bool RegionBand::OptimizeBandList()
{
    ImplRegionBand* pPrevBand = nullptr;
    ImplRegionBand* pBand = mpFirstBand;
 
    while ( pBand )
    {
        const bool bBTEqual = pBand->mpNextBand && (pBand->mnYBottom == pBand->mpNextBand->mnYTop);
 
        // no separation? -> remove!
        if ( pBand->IsEmpty() || (bBTEqual && (pBand->mnYBottom == pBand->mnYTop)) )
        {
            // save pointer
            ImplRegionBand* pOldBand = pBand;
 
            // previous element of the list
            if ( pBand == mpFirstBand )
                mpFirstBand = pBand->mpNextBand;
            else
                pPrevBand->mpNextBand = pBand->mpNextBand;
 
            pBand = pBand->mpNextBand;
            delete pOldBand;
        }
        else
        {
            // fixup
            if ( bBTEqual )
                pBand->mnYBottom = pBand->mpNextBand->mnYTop-1;
 
            // this and next band with equal separations? -> combine!
            if ( pBand->mpNextBand &&
                 ((pBand->mnYBottom+1) == pBand->mpNextBand->mnYTop) &&
                 (*pBand == *pBand->mpNextBand) )
            {
                // expand current height
                pBand->mnYBottom = pBand->mpNextBand->mnYBottom;
 
                // remove next band from list
                ImplRegionBand* pDeletedBand = pBand->mpNextBand;
                pBand->mpNextBand = pDeletedBand->mpNextBand;
                delete pDeletedBand;
 
                // check band again!
            }
            else
            {
                // count rectangles within band
                ImplRegionBandSep* pSep = pBand->mpFirstSep;
                while ( pSep )
                {
                    pSep = pSep->mpNextSep;
                }
 
                pPrevBand = pBand;
                pBand = pBand->mpNextBand;
            }
        }
    }
 
#ifdef DBG_UTIL
    pBand = mpFirstBand;
    while ( pBand )
    {
        SAL_WARN_IF( pBand->mpFirstSep == nullptr, "vcl", "Exiting RegionBand::OptimizeBandList(): empty band in region!" );
 
        if ( pBand->mnYBottom < pBand->mnYTop )
            OSL_ENSURE(false, "RegionBand::OptimizeBandList(): YBottomBoundary < YTopBoundary" );
 
        if ( pBand->mpNextBand )
        {
            if ( pBand->mnYBottom >= pBand->mpNextBand->mnYTop )
                OSL_ENSURE(false, "RegionBand::OptimizeBandList(): overlapping bands in region!" );
        }
 
        pBand = pBand->mpNextBand;
    }
#endif
 
    return (nullptr != mpFirstBand);
}
 
void RegionBand::Move(long nHorzMove, long nVertMove)
{
    ImplRegionBand* pBand = mpFirstBand;
 
    while(pBand)
    {
        // process the vertical move
        if(nVertMove)
        {
            pBand->mnYTop = pBand->mnYTop + nVertMove;
            pBand->mnYBottom = pBand->mnYBottom + nVertMove;
        }
 
        // process the horizontal move
        if(nHorzMove)
        {
            pBand->MoveX(nHorzMove);
        }
 
        pBand = pBand->mpNextBand;
    }
 
}
 
void RegionBand::Scale(double fScaleX, double fScaleY)
{
    ImplRegionBand* pBand = mpFirstBand;
 
    while(pBand)
    {
        // process the vertical move
        if(0.0 != fScaleY)
        {
            pBand->mnYTop = basegfx::fround(pBand->mnYTop * fScaleY);
            pBand->mnYBottom = basegfx::fround(pBand->mnYBottom * fScaleY);
        }
 
        // process the horizontal move
        if(0.0 != fScaleX)
        {
            pBand->ScaleX(fScaleX);
        }
 
        pBand = pBand->mpNextBand;
    }
 
}
 
void RegionBand::InsertBands(long nTop, long nBottom)
{
    // region empty? -> set rectangle as first entry!
    if ( !mpFirstBand )
    {
        // add band with boundaries of the rectangle
        mpFirstBand = new ImplRegionBand( nTop, nBottom );
        return;
    }
 
    // find/insert bands for the boundaries of the rectangle
    bool bTopBoundaryInserted = false;
    bool bTop2BoundaryInserted = false;
    bool bBottomBoundaryInserted = false;
 
    // special case: top boundary is above the first band
    ImplRegionBand* pNewBand;
 
    if ( nTop < mpFirstBand->mnYTop )
    {
        // create new band above the first in the list
        pNewBand = new ImplRegionBand( nTop, mpFirstBand->mnYTop );
 
        if ( nBottom < mpFirstBand->mnYTop )
        {
            pNewBand->mnYBottom = nBottom;
        }
 
        // insert band into the list
        pNewBand->mpNextBand = mpFirstBand;
        mpFirstBand = pNewBand;
 
        bTopBoundaryInserted = true;
    }
 
    // insert band(s) into the list
    ImplRegionBand* pBand = mpFirstBand;
 
    while ( pBand )
    {
        // Insert Bands if possible
        if ( !bTopBoundaryInserted )
        {
            bTopBoundaryInserted = InsertSingleBand( pBand, nTop - 1 );
        }
 
        if ( !bTop2BoundaryInserted )
        {
            bTop2BoundaryInserted = InsertSingleBand( pBand, nTop );
        }
 
        if ( !bBottomBoundaryInserted && (nTop != nBottom) )
        {
            bBottomBoundaryInserted = InsertSingleBand( pBand, nBottom );
        }
 
        // both boundaries inserted? -> nothing more to do
        if ( bTopBoundaryInserted && bTop2BoundaryInserted && bBottomBoundaryInserted )
        {
            break;
        }
 
        // insert bands between two bands if necessary
        if ( pBand->mpNextBand )
        {
            if ( (pBand->mnYBottom + 1) < pBand->mpNextBand->mnYTop )
            {
                // copy band with list and set new boundary
                pNewBand = new ImplRegionBand( pBand->mnYBottom+1, pBand->mpNextBand->mnYTop-1 );
 
                // insert band into the list
                pNewBand->mpNextBand = pBand->mpNextBand;
                pBand->mpNextBand = pNewBand;
            }
        }
 
        pBand = pBand->mpNextBand;
    }
 
}
 
bool RegionBand::InsertSingleBand(ImplRegionBand* pBand, long nYBandPosition)
{
    // boundary already included in band with height 1? -> nothing to do!
    if ( (pBand->mnYTop == pBand->mnYBottom) && (nYBandPosition == pBand->mnYTop) )
    {
        return true;
    }
 
    // insert single height band on top?
    ImplRegionBand* pNewBand;
 
    if ( nYBandPosition == pBand->mnYTop )
    {
        // copy band with list and set new boundary
        pNewBand = new ImplRegionBand( *pBand );
        pNewBand->mnYTop = nYBandPosition+1;
 
        // insert band into the list
        pNewBand->mpNextBand = pBand->mpNextBand;
        pBand->mnYBottom = nYBandPosition;
        pBand->mpNextBand = pNewBand;
 
        return true;
    }
 
    // top of new rectangle within the current band? -> insert new band and copy data
    if ( (nYBandPosition > pBand->mnYTop) && (nYBandPosition < pBand->mnYBottom) )
    {
        // copy band with list and set new boundary
        pNewBand = new ImplRegionBand( *pBand );
        pNewBand->mnYTop = nYBandPosition;
 
        // insert band into the list
        pNewBand->mpNextBand = pBand->mpNextBand;
        pBand->mnYBottom = nYBandPosition;
        pBand->mpNextBand = pNewBand;
 
        // copy band with list and set new boundary
        pNewBand = new ImplRegionBand( *pBand );
        pNewBand->mnYTop = nYBandPosition;
 
        // insert band into the list
        pBand->mpNextBand->mnYTop = nYBandPosition+1;
 
        pNewBand->mpNextBand = pBand->mpNextBand;
        pBand->mnYBottom = nYBandPosition - 1;
        pBand->mpNextBand = pNewBand;
 
        return true;
    }
 
    // create new band behind the current in the list
    if ( !pBand->mpNextBand )
    {
        if ( nYBandPosition == pBand->mnYBottom )
        {
            // copy band with list and set new boundary
            pNewBand = new ImplRegionBand( *pBand );
            pNewBand->mnYTop = pBand->mnYBottom;
            pNewBand->mnYBottom = nYBandPosition;
 
            pBand->mnYBottom = nYBandPosition-1;
 
            // append band to the list
            pBand->mpNextBand = pNewBand;
            return true;
        }
 
        if ( nYBandPosition > pBand->mnYBottom )
        {
            // create new band
            pNewBand = new ImplRegionBand( pBand->mnYBottom + 1, nYBandPosition );
 
            // append band to the list
            pBand->mpNextBand = pNewBand;
            return true;
        }
    }
 
    return false;
}
 
void RegionBand::Union(long nLeft, long nTop, long nRight, long nBottom)
{
    SAL_WARN_IF( nLeft > nRight, "vcl", "RegionBand::Union() - nLeft > nRight" );
    SAL_WARN_IF( nTop > nBottom, "vcl", "RegionBand::Union() - nTop > nBottom" );
 
    // process union
    ImplRegionBand* pBand = mpFirstBand;
    while ( pBand )
    {
        if ( pBand->mnYTop >= nTop )
        {
            if ( pBand->mnYBottom <= nBottom )
                pBand->Union( nLeft, nRight );
            else
            {
#ifdef DBG_UTIL
                long nCurY = pBand->mnYBottom;
                pBand = pBand->mpNextBand;
                while ( pBand )
                {
                    if ( (pBand->mnYTop < nCurY) || (pBand->mnYBottom < nCurY) )
                    {
                        OSL_ENSURE(false, "RegionBand::Union() - Bands not sorted!" );
                    }
                    pBand = pBand->mpNextBand;
                }
#endif
                break;
            }
        }
 
        pBand = pBand->mpNextBand;
    }
 
}
 
void RegionBand::Intersect(long nLeft, long nTop, long nRight, long nBottom)
{
    // process intersections
    ImplRegionBand* pPrevBand = nullptr;
    ImplRegionBand* pBand = mpFirstBand;
 
    while(pBand)
    {
        // band within intersection boundary? -> process. otherwise remove
        if((pBand->mnYTop >= nTop) && (pBand->mnYBottom <= nBottom))
        {
            // process intersection
            pBand->Intersect(nLeft, nRight);
            pPrevBand = pBand;
            pBand = pBand->mpNextBand;
        }
        else
        {
            ImplRegionBand* pOldBand = pBand;
 
            if(pBand == mpFirstBand)
            {
                mpFirstBand = pBand->mpNextBand;
            }
            else
            {
                pPrevBand->mpNextBand = pBand->mpNextBand;
            }
 
            pBand = pBand->mpNextBand;
            delete pOldBand;
        }
    }
 
}
 
void RegionBand::Union(const RegionBand& rSource)
{
    // apply all rectangles from rSource to this
    ImplRegionBand* pBand = rSource.mpFirstBand;
 
    while ( pBand )
    {
        // insert bands if the boundaries are not already in the list
        InsertBands(pBand->mnYTop, pBand->mnYBottom);
 
        // process all elements of the list
        ImplRegionBandSep* pSep = pBand->mpFirstSep;
 
        while(pSep)
        {
            Union(pSep->mnXLeft, pBand->mnYTop, pSep->mnXRight, pBand->mnYBottom);
            pSep = pSep->mpNextSep;
        }
 
        pBand = pBand->mpNextBand;
    }
 
}
 
void RegionBand::Exclude(long nLeft, long nTop, long nRight, long nBottom)
{
    SAL_WARN_IF( nLeft > nRight, "vcl", "RegionBand::Exclude() - nLeft > nRight" );
    SAL_WARN_IF( nTop > nBottom, "vcl", "RegionBand::Exclude() - nTop > nBottom" );
 
    // process exclude
    ImplRegionBand* pBand = mpFirstBand;
 
    while(pBand)
    {
        if(pBand->mnYTop >= nTop)
        {
            if(pBand->mnYBottom <= nBottom)
            {
                pBand->Exclude(nLeft, nRight);
            }
            else
            {
#ifdef DBG_UTIL
                long nCurY = pBand->mnYBottom;
                pBand = pBand->mpNextBand;
 
                while(pBand)
                {
                    if((pBand->mnYTop < nCurY) || (pBand->mnYBottom < nCurY))
                    {
                        OSL_ENSURE(false, "RegionBand::Exclude() - Bands not sorted!" );
                    }
 
                    pBand = pBand->mpNextBand;
                }
#endif
                break;
            }
        }
 
        pBand = pBand->mpNextBand;
    }
 
}
 
void RegionBand::XOr(long nLeft, long nTop, long nRight, long nBottom)
{
    SAL_WARN_IF( nLeft > nRight, "vcl", "RegionBand::Exclude() - nLeft > nRight" );
    SAL_WARN_IF( nTop > nBottom, "vcl", "RegionBand::Exclude() - nTop > nBottom" );
 
    // process xor
    ImplRegionBand* pBand = mpFirstBand;
 
    while(pBand)
    {
        if(pBand->mnYTop >= nTop)
        {
            if(pBand->mnYBottom <= nBottom)
            {
                pBand->XOr(nLeft, nRight);
            }
            else
            {
#ifdef DBG_UTIL
                long nCurY = pBand->mnYBottom;
                pBand = pBand->mpNextBand;
 
                while(pBand)
                {
                    if((pBand->mnYTop < nCurY) || (pBand->mnYBottom < nCurY))
                    {
                        OSL_ENSURE(false, "RegionBand::XOr() - Bands not sorted!" );
                    }
 
                    pBand = pBand->mpNextBand;
                }
#endif
                break;
            }
        }
 
        pBand = pBand->mpNextBand;
    }
 
}
 
void RegionBand::Intersect(const RegionBand& rSource)
{
    // mark all bands as untouched
    ImplRegionBand* pBand = mpFirstBand;
 
    while ( pBand )
    {
        pBand->mbTouched = false;
        pBand = pBand->mpNextBand;
    }
 
    pBand = rSource.mpFirstBand;
 
    while ( pBand )
    {
        // insert bands if the boundaries are not already in the list
        InsertBands( pBand->mnYTop, pBand->mnYBottom );
 
        // process all elements of the list
        ImplRegionBandSep* pSep = pBand->mpFirstSep;
 
        while ( pSep )
        {
            // left boundary?
            if ( pSep == pBand->mpFirstSep )
            {
                // process intersection and do not remove untouched bands
                Exclude( LONG_MIN+1, pBand->mnYTop, pSep->mnXLeft-1, pBand->mnYBottom );
            }
 
            // right boundary?
            if ( pSep->mpNextSep == nullptr )
            {
                // process intersection and do not remove untouched bands
                Exclude( pSep->mnXRight+1, pBand->mnYTop, LONG_MAX-1, pBand->mnYBottom );
            }
            else
            {
                // process intersection and do not remove untouched bands
                Exclude( pSep->mnXRight+1, pBand->mnYTop, pSep->mpNextSep->mnXLeft-1, pBand->mnYBottom );
            }
 
            pSep = pSep->mpNextSep;
        }
 
        pBand = pBand->mpNextBand;
    }
 
    // remove all untouched bands if bands already left
    ImplRegionBand* pPrevBand = nullptr;
    pBand = mpFirstBand;
 
    while ( pBand )
    {
        if ( !pBand->mbTouched )
        {
            // save pointer
            ImplRegionBand* pOldBand = pBand;
 
            // previous element of the list
            if ( pBand == mpFirstBand )
            {
                mpFirstBand = pBand->mpNextBand;
            }
            else
            {
                pPrevBand->mpNextBand = pBand->mpNextBand;
            }
 
            pBand = pBand->mpNextBand;
            delete pOldBand;
        }
        else
        {
            pPrevBand = pBand;
            pBand = pBand->mpNextBand;
        }
    }
 
}
 
bool RegionBand::Exclude(const RegionBand& rSource)
{
    // apply all rectangles to the region passed to this region
    ImplRegionBand* pBand = rSource.mpFirstBand;
 
    while ( pBand )
    {
        // insert bands if the boundaries are not already in the list
        InsertBands( pBand->mnYTop, pBand->mnYBottom );
 
        // process all elements of the list
        ImplRegionBandSep* pSep = pBand->mpFirstSep;
 
        while ( pSep )
        {
            Exclude( pSep->mnXLeft, pBand->mnYTop, pSep->mnXRight, pBand->mnYBottom );
            pSep = pSep->mpNextSep;
        }
 
        // to test less bands, already check in the loop
        if ( !OptimizeBandList() )
        {
            return false;
        }
 
        pBand = pBand->mpNextBand;
    }
 
    return true;
}
 
bool RegionBand::CheckConsistency() const
{
    if (!mpFirstBand)
        return true;
    // look in the band list (don't test first band again!)
    const ImplRegionBand* pBand = mpFirstBand->mpNextBand;
    while (pBand)
    {
        if (!pBand->mpFirstSep)
            return false;
        pBand = pBand->mpNextBand;
    }
    return true;
}
 
tools::Rectangle RegionBand::GetBoundRect() const
{
 
    // get the boundaries of the first band
    long nYTop(mpFirstBand->mnYTop);
    long nYBottom(mpFirstBand->mnYBottom);
    long nXLeft(mpFirstBand->GetXLeftBoundary());
    long nXRight(mpFirstBand->GetXRightBoundary());
 
    // look in the band list (don't test first band again!)
    ImplRegionBand* pBand = mpFirstBand->mpNextBand;
 
    while ( pBand )
    {
        nYBottom = pBand->mnYBottom;
        nXLeft = std::min( nXLeft, pBand->GetXLeftBoundary() );
        nXRight = std::max( nXRight, pBand->GetXRightBoundary() );
 
        pBand = pBand->mpNextBand;
    }
 
    return tools::Rectangle( nXLeft, nYTop, nXRight, nYBottom );
}
 
void RegionBand::XOr(const RegionBand& rSource)
{
    ImplRegionBand* pBand = rSource.mpFirstBand;
 
    while ( pBand )
    {
        // insert bands if the boundaries are not already in the list
        InsertBands( pBand->mnYTop, pBand->mnYBottom );
 
        // process all elements of the list
        ImplRegionBandSep* pSep = pBand->mpFirstSep;
 
        while ( pSep )
        {
            XOr( pSep->mnXLeft, pBand->mnYTop, pSep->mnXRight, pBand->mnYBottom );
            pSep = pSep->mpNextSep;
        }
 
        pBand = pBand->mpNextBand;
    }
}
 
bool RegionBand::IsInside(const Point& rPoint) const
{
 
    // search band list
    ImplRegionBand* pBand = mpFirstBand;
 
    while(pBand)
    {
        // is point within band?
        if((pBand->mnYTop <= rPoint.Y()) && (pBand->mnYBottom >= rPoint.Y()))
        {
            // is point within separation of the band?
            return pBand->IsInside(rPoint.X());
        }
 
        pBand = pBand->mpNextBand;
    }
 
    return false;
}
 
void RegionBand::GetRegionRectangles(RectangleVector& rTarget) const
{
    // clear result vector
    rTarget.clear();
    ImplRegionBand* pCurrRectBand = mpFirstBand;
    tools::Rectangle aRectangle;
 
    while(pCurrRectBand)
    {
        ImplRegionBandSep* pCurrRectBandSep = pCurrRectBand->mpFirstSep;
 
        aRectangle.SetTop( pCurrRectBand->mnYTop );
        aRectangle.SetBottom( pCurrRectBand->mnYBottom );
 
        while(pCurrRectBandSep)
        {
            aRectangle.SetLeft( pCurrRectBandSep->mnXLeft );
            aRectangle.SetRight( pCurrRectBandSep->mnXRight );
            rTarget.push_back(aRectangle);
            pCurrRectBandSep = pCurrRectBandSep->mpNextSep;
        }
 
        pCurrRectBand = pCurrRectBand->mpNextBand;
    }
}
 
sal_uInt32 RegionBand::getRectangleCount() const
{
    sal_uInt32 nCount = 0;
    const ImplRegionBand* pBand = mpFirstBand;
 
    while(pBand)
    {
        ImplRegionBandSep* pSep = pBand->mpFirstSep;
 
        while(pSep)
        {
            nCount++;
            pSep = pSep->mpNextSep;
        }
 
        pBand = pBand->mpNextBand;
    }
 
    return nCount;
}
 
#ifdef DBG_UTIL
const char* ImplDbgTestRegionBand(const void* pObj)
{
    const RegionBand* pRegionBand = static_cast< const RegionBand* >(pObj);
 
    if(pRegionBand)
    {
        const ImplRegionBand* pBand = pRegionBand->ImplGetFirstRegionBand();
 
        while(pBand)
        {
            if(pBand->mnYBottom < pBand->mnYTop)
            {
                return "YBottom < YTop";
            }
 
            if(pBand->mpNextBand)
            {
                if(pBand->mnYBottom >= pBand->mpNextBand->mnYTop)
                {
                    return "overlapping bands in region";
                }
            }
 
            if(pBand->mbTouched)
            {
                return "Band-mbTouched overwrite";
            }
 
            ImplRegionBandSep* pSep = pBand->mpFirstSep;
 
            while(pSep)
            {
                if(pSep->mnXRight < pSep->mnXLeft)
                {
                    return "XLeft < XRight";
                }
 
                if(pSep->mpNextSep)
                {
                    if(pSep->mnXRight >= pSep->mpNextSep->mnXLeft)
                    {
                        return "overlapping separations in region";
                    }
                }
 
                if ( pSep->mbRemoved )
                {
                    return "Sep-mbRemoved overwrite";
                }
 
                pSep = pSep->mpNextSep;
            }
 
            pBand = pBand->mpNextBand;
        }
    }
 
    return nullptr;
}
#endif
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V794 The assignment operator should be protected from the case of 'this == &rRef'.