/**************************************************************
 * 
 * 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 <svx/svdmodel.hxx>
#include <svx/svdpage.hxx>
#include <crsrsh.hxx>
#include <doc.hxx>
#include <pagefrm.hxx>
#include <cntfrm.hxx>
#include <ftnfrm.hxx>
#include <viewimp.hxx>
#include <swcrsr.hxx>
#include <dflyobj.hxx>
#include <ndtxt.hxx>
#include <flyfrm.hxx>
#include <txtfrm.hxx>
#include <txtftn.hxx>
#include <ftnidx.hxx>
#include <viscrs.hxx>
#include <callnk.hxx>

sal_Bool SwCrsrShell::CallCrsrFN( FNCrsr fnCrsr )
{
	SwCallLink aLk( *this );        // Crsr-Moves ueberwachen,
	SwCursor* pCrsr = getShellCrsr( true );
	sal_Bool bRet = (pCrsr->*fnCrsr)();
	if( bRet )
		UpdateCrsr( SwCrsrShell::SCROLLWIN | SwCrsrShell::CHKRANGE |
					SwCrsrShell::READONLY );
	return bRet;
}

sal_Bool SwCursor::GotoFtnTxt()
{
	// springe aus dem Content zur Fussnote
	sal_Bool bRet = sal_False;
	SwTxtNode* pTxtNd = GetPoint()->nNode.GetNode().GetTxtNode();

    SwTxtAttr *const pFtn( (pTxtNd)
        ? pTxtNd->GetTxtAttrForCharAt(
            GetPoint()->nContent.GetIndex(), RES_TXTATR_FTN)
        : 0);
    if (pFtn)
    {
		SwCrsrSaveState aSaveState( *this );
		GetPoint()->nNode = *((SwTxtFtn*)pFtn)->GetStartNode();

		SwCntntNode* pCNd = GetDoc()->GetNodes().GoNextSection(
											&GetPoint()->nNode,
											sal_True, !IsReadOnlyAvailable() );
		if( pCNd )
		{
			GetPoint()->nContent.Assign( pCNd, 0 );
            bRet = !IsSelOvr( nsSwCursorSelOverFlags::SELOVER_CHECKNODESSECTION |
                              nsSwCursorSelOverFlags::SELOVER_TOGGLE );
		}
	}
	return bRet;
}

sal_Bool SwCrsrShell::GotoFtnTxt()
{
    sal_Bool bRet = CallCrsrFN( &SwCursor::GotoFtnTxt );
    if( !bRet )
    {
        SwTxtNode* pTxtNd = _GetCrsr() ?
                   _GetCrsr()->GetPoint()->nNode.GetNode().GetTxtNode() : NULL;
        if( pTxtNd )
        {
            const SwFrm *pFrm = pTxtNd->getLayoutFrm( GetLayout(), &_GetCrsr()->GetSttPos(),
                                                 _GetCrsr()->Start() );
            const SwFtnBossFrm* pFtnBoss;
            sal_Bool bSkip = pFrm && pFrm->IsInFtn();
            while( pFrm && 0 != ( pFtnBoss = pFrm->FindFtnBossFrm() ) )
            {
                if( 0 != ( pFrm = pFtnBoss->FindFtnCont() ) )
                {
                    if( bSkip )
                        bSkip = sal_False;
                    else
                    {
                        const SwCntntFrm* pCnt = static_cast<const SwLayoutFrm*>
                                                        (pFrm)->ContainsCntnt();
                        if( pCnt )
                        {
                            const SwCntntNode* pNode = pCnt->GetNode();
                            _GetCrsr()->GetPoint()->nNode = *pNode;
                            _GetCrsr()->GetPoint()->nContent.Assign(
                                const_cast<SwCntntNode*>(pNode),
                                static_cast<const SwTxtFrm*>(pCnt)->GetOfst() );
                            UpdateCrsr( SwCrsrShell::SCROLLWIN |
                                SwCrsrShell::CHKRANGE | SwCrsrShell::READONLY );
                            bRet = sal_True;
                            break;
                        }
                    }
                }
                if( pFtnBoss->GetNext() && !pFtnBoss->IsPageFrm() )
                    pFrm = pFtnBoss->GetNext();
                else
                    pFrm = pFtnBoss->GetUpper();
            }
        }
    }
    return bRet;
}


sal_Bool SwCursor::GotoFtnAnchor()
{
	// springe aus der Fussnote zum Anker
	const SwNode* pSttNd = GetNode()->FindFootnoteStartNode();
	if( pSttNd )
	{
		// durchsuche alle Fussnoten im Dokument nach diesem StartIndex
		const SwTxtFtn* pTxtFtn;
		const SwFtnIdxs& rFtnArr = pSttNd->GetDoc()->GetFtnIdxs();
		for( sal_uInt16 n = 0; n < rFtnArr.Count(); ++n )
			if( 0 != ( pTxtFtn = rFtnArr[ n ])->GetStartNode() &&
				pSttNd == &pTxtFtn->GetStartNode()->GetNode() )
			{
				SwCrsrSaveState aSaveState( *this );

				SwTxtNode& rTNd = (SwTxtNode&)pTxtFtn->GetTxtNode();

				GetPoint()->nNode = rTNd;
				GetPoint()->nContent.Assign( &rTNd, *pTxtFtn->GetStart() );

                return !IsSelOvr( nsSwCursorSelOverFlags::SELOVER_CHECKNODESSECTION |
                                  nsSwCursorSelOverFlags::SELOVER_TOGGLE );
			}
	}
	return sal_False;
}

sal_Bool SwCrsrShell::GotoFtnAnchor()
{
	// springe aus der Fussnote zum Anker
	SwCallLink aLk( *this );        // Crsr-Moves ueberwachen,
	sal_Bool bRet = pCurCrsr->GotoFtnAnchor();
	if( bRet )
	{
		// BUG 5996: Tabellen-Kopfzeile sonderbehandeln
		pCurCrsr->GetPtPos() = Point();
		UpdateCrsr( SwCrsrShell::SCROLLWIN | SwCrsrShell::CHKRANGE |
					SwCrsrShell::READONLY );
	}
	return bRet;
}

inline sal_Bool CmpLE( const SwTxtFtn& rFtn, sal_uLong nNd, xub_StrLen nCnt )
{
	sal_uLong nTNd = rFtn.GetTxtNode().GetIndex();
	return nTNd < nNd || ( nTNd == nNd && *rFtn.GetStart() <= nCnt );
}
inline sal_Bool CmpL( const SwTxtFtn& rFtn, sal_uLong nNd, xub_StrLen nCnt )
{
	sal_uLong nTNd = rFtn.GetTxtNode().GetIndex();
	return nTNd < nNd || ( nTNd == nNd && *rFtn.GetStart() < nCnt );
}

sal_Bool SwCursor::GotoNextFtnAnchor()
{
	const SwFtnIdxs& rFtnArr = GetDoc()->GetFtnIdxs();
	const SwTxtFtn* pTxtFtn = 0;
	sal_uInt16 nPos;

	if( rFtnArr.SeekEntry( GetPoint()->nNode, &nPos ))
	{
		// es gibt eine Fussnote mit dem Index, suche also die
		// naechstgelegene
		if( nPos < rFtnArr.Count() )
		{
			sal_uLong nNdPos = GetPoint()->nNode.GetIndex();
			xub_StrLen nCntPos = GetPoint()->nContent.GetIndex();

			pTxtFtn = rFtnArr[ nPos ];
			// suche vorewaerts zur naechsten
			if( CmpLE( *pTxtFtn, nNdPos, nCntPos ) )
			{
				pTxtFtn = 0;
				for( ++nPos; nPos < rFtnArr.Count(); ++nPos )
				{
					pTxtFtn = rFtnArr[ nPos ];
					if( !CmpLE( *pTxtFtn, nNdPos, nCntPos ) )
						break;		// gefunden
					pTxtFtn = 0;
				}
			}
			else if( nPos )
			{
				// suche rueckwaerts zur vorherigen
				pTxtFtn = 0;
				while( nPos )
				{
					pTxtFtn = rFtnArr[ --nPos ];
					if( CmpLE( *pTxtFtn, nNdPos, nCntPos ) )
					{
						pTxtFtn = rFtnArr[ ++nPos ];
						break;		// gefunden
					}
//					pTxtFtn = 0;
				}
			}
		}
	}
	else if( nPos < rFtnArr.Count() )
		pTxtFtn = rFtnArr[ nPos ];

	sal_Bool bRet = 0 != pTxtFtn;
	if( bRet )
	{
		SwCrsrSaveState aSaveState( *this );

		SwTxtNode& rTNd = (SwTxtNode&)pTxtFtn->GetTxtNode();
		GetPoint()->nNode = rTNd;
		GetPoint()->nContent.Assign( &rTNd, *pTxtFtn->GetStart() );
		bRet = !IsSelOvr();
	}
	return bRet;
}

sal_Bool SwCursor::GotoPrevFtnAnchor()
{
	const SwFtnIdxs& rFtnArr = GetDoc()->GetFtnIdxs();
	const SwTxtFtn* pTxtFtn = 0;
	sal_uInt16 nPos;

	if( rFtnArr.SeekEntry( GetPoint()->nNode, &nPos ) )
	{
		// es gibt eine Fussnote mit dem Index, suche also die
		// naechstgelegene
		sal_uLong nNdPos = GetPoint()->nNode.GetIndex();
		xub_StrLen nCntPos = GetPoint()->nContent.GetIndex();

		pTxtFtn = rFtnArr[ nPos ];
		// suche vorwaerts zur naechsten
		if( CmpL( *pTxtFtn, nNdPos, nCntPos ))
		{
			for( ++nPos; nPos < rFtnArr.Count(); ++nPos )
			{
				pTxtFtn = rFtnArr[ nPos ];
				if( !CmpL( *pTxtFtn, nNdPos, nCntPos ) )
				{
					pTxtFtn = rFtnArr[ nPos-1 ];
					break;
				}
			}
		}
		else if( nPos )
		{
			// suche rueckwaerts zur vorherigen
			pTxtFtn = 0;
			while( nPos )
			{
				pTxtFtn = rFtnArr[ --nPos ];
				if( CmpL( *pTxtFtn, nNdPos, nCntPos ))
					break;		// gefunden
				pTxtFtn = 0;
			}
		}
		else
			pTxtFtn = 0;
	}
	else if( nPos )
		pTxtFtn = rFtnArr[ nPos-1 ];

	sal_Bool bRet = 0 != pTxtFtn;
	if( bRet )
	{
		SwCrsrSaveState aSaveState( *this );

		SwTxtNode& rTNd = (SwTxtNode&)pTxtFtn->GetTxtNode();
		GetPoint()->nNode = rTNd;
		GetPoint()->nContent.Assign( &rTNd, *pTxtFtn->GetStart() );
		bRet = !IsSelOvr();
	}
	return bRet;
}

sal_Bool SwCrsrShell::GotoNextFtnAnchor()
{
	return CallCrsrFN( &SwCursor::GotoNextFtnAnchor );
}

sal_Bool SwCrsrShell::GotoPrevFtnAnchor()
{
	return CallCrsrFN( &SwCursor::GotoPrevFtnAnchor );
}

// springe aus dem Rahmen zum Anker


sal_Bool SwCrsrShell::GotoFlyAnchor()
{
	SET_CURR_SHELL( this );
	const SwFrm* pFrm = GetCurrFrm();
	do {
		pFrm = pFrm->GetUpper();
	} while( pFrm && !pFrm->IsFlyFrm() );

	if( !pFrm )		// ist kein FlyFrame
		return sal_False;

	SwCallLink aLk( *this );        // Crsr-Moves ueberwachen,
	SwCrsrSaveState aSaveState( *pCurCrsr );

	// springe in den BodyFrame, der am naechsten vom Fly liegt
	SwRect aTmpRect( aCharRect );
	if( !pFrm->Frm().IsInside( aTmpRect ))
		aTmpRect = pFrm->Frm();
	Point aPt( aTmpRect.Left(), aTmpRect.Top() +
				( aTmpRect.Bottom() - aTmpRect.Top() ) / 2 );
	aPt.X() = aPt.X() > (pFrm->Frm().Left() + (pFrm->Frm().SSize().Width() / 2 ))
				? pFrm->Frm().Right()
				: pFrm->Frm().Left();

	const SwPageFrm* pPageFrm = pFrm->FindPageFrm();
	const SwCntntFrm* pFndFrm = pPageFrm->GetCntntPos( aPt, sal_False, sal_True );
	pFndFrm->GetCrsrOfst( pCurCrsr->GetPoint(), aPt );

	sal_Bool bRet = !pCurCrsr->IsInProtectTable() && !pCurCrsr->IsSelOvr();
	if( bRet )
		UpdateCrsr( SwCrsrShell::SCROLLWIN | SwCrsrShell::CHKRANGE |
					SwCrsrShell::READONLY );
	return bRet;
}
