/**************************************************************
 * 
 * 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
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sw.hxx"


#include <doc.hxx>
#include <node.hxx>
#include <rootfrm.hxx>
#include <editsh.hxx>
#include <viscrs.hxx>
#include <IMark.hxx>
#include <bookmrk.hxx>
#include <redline.hxx>
#include <mvsave.hxx>
#include <docary.hxx>
#include <unocrsr.hxx>
#include <swundo.hxx>
#include <hints.hxx>

/*
 * MACROS um ueber alle CrsrShells zu iterieren
 */
#define PCURSH ((SwCrsrShell*)_pStartShell)
#define FOREACHSHELL_START( pEShell ) \
    {\
		ViewShell *_pStartShell = pEShell; \
		do { \
			if( _pStartShell->IsA( TYPE( SwCrsrShell )) ) \
			{

#define FOREACHSHELL_END( pEShell ) \
			} \
        } while((_pStartShell=(ViewShell*)_pStartShell->GetNext())!= pEShell ); \
	}

#define PCURCRSR (_pCurrCrsr)
#define FOREACHPAM_START(pSttCrsr) \
	{\
		SwPaM *_pStartCrsr = pSttCrsr, *_pCurrCrsr = pSttCrsr; \
		do {

#define FOREACHPAM_END() \
		} while( (_pCurrCrsr=(SwPaM *)_pCurrCrsr->GetNext()) != _pStartCrsr ); \
	}

namespace
{
    // find the relevant section in which the SwUnoCrsr may wander.
    // returns NULL if no restrictions apply
    static const SwStartNode* lcl_FindUnoCrsrSection( const SwNode& rNode )
    {
        const SwStartNode* pStartNode = rNode.StartOfSectionNode();
        while( ( pStartNode != NULL ) &&
               ( pStartNode->StartOfSectionNode() != pStartNode ) &&
               ( pStartNode->GetStartNodeType() == SwNormalStartNode ) )
            pStartNode = pStartNode->StartOfSectionNode();

        return pStartNode;
    }

    static inline bool lcl_PosCorrAbs(SwPosition & rPos,
        const SwPosition& rStart,
        const SwPosition& rEnd,
        const SwPosition& rNewPos)
    {
        if ((rStart <= rPos) && (rPos <= rEnd))
        {
            rPos = rNewPos;
            return true;
        }
        return false;
    };

    static inline bool lcl_PaMCorrAbs(SwPaM & rPam,
        const SwPosition& rStart,
        const SwPosition& rEnd,
        const SwPosition& rNewPos)
    {
        bool bRet = false;
        bRet |= lcl_PosCorrAbs(rPam.GetBound(true ), rStart, rEnd, rNewPos);
        bRet |= lcl_PosCorrAbs(rPam.GetBound(false), rStart, rEnd, rNewPos);
        return bRet;
    };

    static inline void lcl_PaMCorrRel1(SwPaM * pPam,
        SwNode const * const pOldNode,
        const SwPosition& rNewPos,
        const xub_StrLen nCntIdx)
    {
        for(int nb = 0; nb < 2; ++nb)
            if(&((pPam)->GetBound(sal_Bool(nb)).nNode.GetNode()) == pOldNode)
            {
                (pPam)->GetBound(sal_Bool(nb)).nNode = rNewPos.nNode;
                (pPam)->GetBound(sal_Bool(nb)).nContent.Assign(
                    const_cast<SwIndexReg*>(rNewPos.nContent.GetIdxReg()),
                    nCntIdx + (pPam)->GetBound(sal_Bool(nb)).nContent.GetIndex()); 
            }
    }
}


void PaMCorrAbs( const SwPaM& rRange,
				const SwPosition& rNewPos )
{
    SwPosition const aStart( *rRange.Start() );
    SwPosition const aEnd( *rRange.End() );
    SwPosition const aNewPos( rNewPos );
    SwDoc *const pDoc = aStart.nNode.GetNode().GetDoc();
    SwCrsrShell *const pShell = pDoc->GetEditShell();

	if( pShell )
	{
		FOREACHSHELL_START( pShell )
			SwPaM *_pStkCrsr = PCURSH->GetStkCrsr();
			if( _pStkCrsr )
			do {
                lcl_PaMCorrAbs( *_pStkCrsr, aStart, aEnd, aNewPos );
			} while ( (_pStkCrsr != 0 ) &&
				((_pStkCrsr=(SwPaM *)_pStkCrsr->GetNext()) != PCURSH->GetStkCrsr()) );

			FOREACHPAM_START( PCURSH->_GetCrsr() )
                lcl_PaMCorrAbs( *PCURCRSR, aStart, aEnd, aNewPos );
			FOREACHPAM_END()

			if( PCURSH->IsTableMode() )
                lcl_PaMCorrAbs( *PCURSH->GetTblCrs(), aStart, aEnd, aNewPos );

		FOREACHSHELL_END( pShell )
	}
	{
        SwUnoCrsrTbl& rTbl = const_cast<SwUnoCrsrTbl&>(pDoc->GetUnoCrsrTbl());

		for( sal_uInt16 n = 0; n < rTbl.Count(); ++n )
		{
            SwUnoCrsr *const pUnoCursor = rTbl[ n ];

            bool bChange = false; // has the UNO cursor been corrected?

            // determine whether the UNO cursor will leave it's designated
            // section
            bool const bLeaveSection =
                pUnoCursor->IsRemainInSection() &&
                ( lcl_FindUnoCrsrSection( aNewPos.nNode.GetNode() ) !=
                  lcl_FindUnoCrsrSection(
                      pUnoCursor->GetPoint()->nNode.GetNode() ) );

			FOREACHPAM_START( pUnoCursor )
                bChange |= lcl_PaMCorrAbs( *PCURCRSR, aStart, aEnd, aNewPos );
			FOREACHPAM_END()

            SwUnoTableCrsr *const pUnoTblCrsr =
                dynamic_cast<SwUnoTableCrsr *>(rTbl[ n ]);
			if( pUnoTblCrsr )
			{
				FOREACHPAM_START( &pUnoTblCrsr->GetSelRing() )
                    bChange |=
                        lcl_PaMCorrAbs( *PCURCRSR, aStart, aEnd, aNewPos );
				FOREACHPAM_END()
			}

            // if a UNO cursor leaves its designated section, we must inform
            // (and invalidate) said cursor
            if (bChange && bLeaveSection)
            {
                // the UNO cursor has left its section. We need to notify it!
                SwMsgPoolItem aHint( RES_UNOCURSOR_LEAVES_SECTION );
                pUnoCursor->ModifyNotification( &aHint, NULL );
            }
		}
	}
}

void SwDoc::CorrAbs(const SwNodeIndex& rOldNode,
    const SwPosition& rNewPos,
    const xub_StrLen nOffset,
    sal_Bool bMoveCrsr)
{
    SwCntntNode *const pCntntNode( rOldNode.GetNode().GetCntntNode() );
    SwPaM const aPam(rOldNode, 0,
                     rOldNode, (pCntntNode) ? pCntntNode->Len() : 0);
    SwPosition aNewPos(rNewPos);
    aNewPos.nContent += nOffset;

    getIDocumentMarkAccess()->correctMarksAbsolute(rOldNode, rNewPos, nOffset);
    {   // fix redlines
        SwRedlineTbl& rTbl = *pRedlineTbl;
        for (sal_uInt16 n = 0; n < rTbl.Count(); )
        {
            // is on position ??
            SwRedline *const pRedline( rTbl[ n ] );
            bool const bChanged =
                lcl_PaMCorrAbs(*pRedline, *aPam.Start(), *aPam.End(), aNewPos);
            // clean up empty redlines: docredln.cxx asserts these as invalid
            if (bChanged && (*pRedline->GetPoint() == *pRedline->GetMark())
                         && (pRedline->GetContentIdx() == NULL))
            {
                rTbl.DeleteAndDestroy(n);
            }
            else
            {
                ++n;
            }
        }
    }

    if(bMoveCrsr)
    {
        ::PaMCorrAbs(aPam, aNewPos);
    }
}

void SwDoc::CorrAbs(const SwPaM& rRange,
    const SwPosition& rNewPos,
    sal_Bool bMoveCrsr)
{
    SwPosition aStart(*rRange.Start());
    SwPosition aEnd(*rRange.End());
    SwPosition aNewPos(rNewPos);

    _DelBookmarks(aStart.nNode, aEnd.nNode, NULL,
        &aStart.nContent, &aEnd.nContent);
    if(bMoveCrsr)
        ::PaMCorrAbs(rRange, rNewPos);
}

void SwDoc::CorrAbs(const SwNodeIndex& rStartNode,
     const SwNodeIndex& rEndNode,
     const SwPosition& rNewPos,
     sal_Bool bMoveCrsr)
{
    _DelBookmarks(rStartNode, rEndNode);

    if(bMoveCrsr)
    {
        SwCntntNode *const pCntntNode( rEndNode.GetNode().GetCntntNode() );
        SwPaM const aPam(rStartNode, 0,
                         rEndNode, (pCntntNode) ? pCntntNode->Len() : 0);
        ::PaMCorrAbs(aPam, rNewPos);
    }
}





void PaMCorrRel( const SwNodeIndex &rOldNode,
				 const SwPosition &rNewPos,
				 const xub_StrLen nOffset )
{
	const SwNode* pOldNode = &rOldNode.GetNode();
	SwPosition aNewPos( rNewPos );
	const SwDoc* pDoc = pOldNode->GetDoc();

	xub_StrLen nCntIdx = rNewPos.nContent.GetIndex() + nOffset;

	SwCrsrShell* pShell = pDoc->GetEditShell();
	if( pShell )
	{
		FOREACHSHELL_START( pShell )
			SwPaM *_pStkCrsr = PCURSH->GetStkCrsr();
			if( _pStkCrsr )
			do {
				lcl_PaMCorrRel1( _pStkCrsr, pOldNode, aNewPos, nCntIdx );
			} while ( (_pStkCrsr != 0 ) &&
				((_pStkCrsr=(SwPaM *)_pStkCrsr->GetNext()) != PCURSH->GetStkCrsr()) );

			FOREACHPAM_START( PCURSH->_GetCrsr() )
				lcl_PaMCorrRel1( PCURCRSR, pOldNode, aNewPos, nCntIdx);
			FOREACHPAM_END()

			if( PCURSH->IsTableMode() )
				lcl_PaMCorrRel1( PCURSH->GetTblCrs(), pOldNode, aNewPos, nCntIdx );

		FOREACHSHELL_END( pShell )
	}
	{
		SwUnoCrsrTbl& rTbl = (SwUnoCrsrTbl&)pDoc->GetUnoCrsrTbl();
		for( sal_uInt16 n = 0; n < rTbl.Count(); ++n )
		{
			FOREACHPAM_START( rTbl[ n ] )
				lcl_PaMCorrRel1( PCURCRSR, pOldNode, aNewPos, nCntIdx );
			FOREACHPAM_END()

            SwUnoTableCrsr* pUnoTblCrsr =
                dynamic_cast<SwUnoTableCrsr*>(rTbl[ n ]);
			if( pUnoTblCrsr )
			{
				FOREACHPAM_START( &pUnoTblCrsr->GetSelRing() )
					lcl_PaMCorrRel1( PCURCRSR, pOldNode, aNewPos, nCntIdx );
				FOREACHPAM_END()
			}
		}
	}
}

void SwDoc::CorrRel(const SwNodeIndex& rOldNode,
    const SwPosition& rNewPos,
    const xub_StrLen nOffset,
    sal_Bool bMoveCrsr)
{
    getIDocumentMarkAccess()->correctMarksRelative(rOldNode, rNewPos, nOffset);

    { // dann die Redlines korrigieren
        SwRedlineTbl& rTbl = *pRedlineTbl;
        SwPosition aNewPos(rNewPos);
        for( sal_uInt16 n = 0; n < rTbl.Count(); ++n )
        {
            // liegt auf der Position ??
            lcl_PaMCorrRel1( rTbl[ n ], &rOldNode.GetNode(), aNewPos, aNewPos.nContent.GetIndex() + nOffset );
        }
    }

    if(bMoveCrsr)
        ::PaMCorrRel(rOldNode, rNewPos, nOffset);
}


SwEditShell* SwDoc::GetEditShell( ViewShell** ppSh ) const
{
	// Layout und OLE-Shells sollten vorhanden sein!
	if( pCurrentView )
	{
		ViewShell *pSh = pCurrentView, *pVSh = pSh;
		if( ppSh )
			*ppSh = pSh;

		// wir suchen uns eine EditShell, falls diese existiert
		do {
			if( pSh->IsA( TYPE( SwEditShell ) ) )
				return (SwEditShell*)pSh;

		} while( pVSh != ( pSh = (ViewShell*)pSh->GetNext() ));
	}
	else if( ppSh )
		*ppSh = 0;	//swmod 071029//swmod 071225

	return 0;
}

::sw::IShellCursorSupplier * SwDoc::GetIShellCursorSupplier()
{
    return GetEditShell(0);
}

