/**************************************************************
 * 
 * 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 <ftnidx.hxx>
#include <rootfrm.hxx>
#include <txtftn.hxx>
#include <fmtftn.hxx>
#include <pam.hxx>
#include <pagedesc.hxx>
#include <charfmt.hxx>
#include <UndoAttribute.hxx>
#include <hints.hxx>
#include <rolbck.hxx>
#include <doc.hxx>
#include <IDocumentUndoRedo.hxx>
#include <ndtxt.hxx>
#include <poolfmt.hxx>
#include <ftninfo.hxx>

/*********************** SwFtnInfo ***************************/

SwEndNoteInfo& SwEndNoteInfo::operator=(const SwEndNoteInfo& rInfo)
{
	if( rInfo.GetFtnTxtColl() )
		rInfo.GetFtnTxtColl()->Add(this);
	else if ( GetRegisteredIn())
		GetRegisteredInNonConst()->Remove(this);

	if ( rInfo.aPageDescDep.GetRegisteredIn() )
		((SwModify*)rInfo.aPageDescDep.GetRegisteredIn())->Add( &aPageDescDep );
	else if ( aPageDescDep.GetRegisteredIn() )
		((SwModify*)aPageDescDep.GetRegisteredIn())->Remove( &aPageDescDep );

	if ( rInfo.aCharFmtDep.GetRegisteredIn() )
		((SwModify*)rInfo.aCharFmtDep.GetRegisteredIn())->Add( &aCharFmtDep );
	else if ( aCharFmtDep.GetRegisteredIn() )
		((SwModify*)aCharFmtDep.GetRegisteredIn())->Remove( &aCharFmtDep );

	if ( rInfo.aAnchorCharFmtDep.GetRegisteredIn() )
		((SwModify*)rInfo.aAnchorCharFmtDep.GetRegisteredIn())->Add(
													&aAnchorCharFmtDep );
	else if( aAnchorCharFmtDep.GetRegisteredIn() )
		((SwModify*)aAnchorCharFmtDep.GetRegisteredIn())->Remove(
													&aAnchorCharFmtDep );

	aFmt = rInfo.aFmt;
	nFtnOffset = rInfo.nFtnOffset;
    m_bEndNote = rInfo.m_bEndNote;
	sPrefix = rInfo.sPrefix;
	sSuffix = rInfo.sSuffix;
	return *this;
}


sal_Bool SwEndNoteInfo::operator==( const SwEndNoteInfo& rInfo ) const
{
	return  aPageDescDep.GetRegisteredIn() ==
								rInfo.aPageDescDep.GetRegisteredIn() &&
			aCharFmtDep.GetRegisteredIn() ==
								rInfo.aCharFmtDep.GetRegisteredIn() &&
			aAnchorCharFmtDep.GetRegisteredIn() ==
								rInfo.aAnchorCharFmtDep.GetRegisteredIn() &&
			GetFtnTxtColl() == rInfo.GetFtnTxtColl() &&
			aFmt.GetNumberingType() == rInfo.aFmt.GetNumberingType() &&
			nFtnOffset == rInfo.nFtnOffset &&
            m_bEndNote == rInfo.m_bEndNote &&
			sPrefix == rInfo.sPrefix &&
			sSuffix == rInfo.sSuffix;
}


SwEndNoteInfo::SwEndNoteInfo(const SwEndNoteInfo& rInfo) :
	SwClient( rInfo.GetFtnTxtColl() ),
	aPageDescDep( this, 0 ),
	aCharFmtDep( this, 0 ),
	aAnchorCharFmtDep( this, 0 ),
	sPrefix( rInfo.sPrefix ),
	sSuffix( rInfo.sSuffix ),
    m_bEndNote( true ),
	aFmt( rInfo.aFmt ),
	nFtnOffset( rInfo.nFtnOffset )
{
	if( rInfo.aPageDescDep.GetRegisteredIn() )
		((SwModify*)rInfo.aPageDescDep.GetRegisteredIn())->Add( &aPageDescDep );

	if( rInfo.aCharFmtDep.GetRegisteredIn() )
		((SwModify*)rInfo.aCharFmtDep.GetRegisteredIn())->Add( &aCharFmtDep );

	if( rInfo.aAnchorCharFmtDep.GetRegisteredIn() )
		((SwModify*)rInfo.aAnchorCharFmtDep.GetRegisteredIn())->Add(
				&aAnchorCharFmtDep );
}

SwEndNoteInfo::SwEndNoteInfo(SwTxtFmtColl *pFmt) :
	SwClient(pFmt),
	aPageDescDep( this, 0 ),
	aCharFmtDep( this, 0 ),
	aAnchorCharFmtDep( this, 0 ),
    m_bEndNote( true ),
	nFtnOffset( 0 )
{
	aFmt.SetNumberingType(SVX_NUM_ROMAN_LOWER);
}

SwPageDesc *SwEndNoteInfo::GetPageDesc( SwDoc &rDoc ) const
{
	if ( !aPageDescDep.GetRegisteredIn() )
	{
		SwPageDesc *pDesc = rDoc.GetPageDescFromPool( static_cast<sal_uInt16>(
            m_bEndNote ? RES_POOLPAGE_ENDNOTE   : RES_POOLPAGE_FOOTNOTE ) );
		pDesc->Add( &((SwClient&)aPageDescDep) );
	}

	return (SwPageDesc*)( aPageDescDep.GetRegisteredIn() );
}

bool SwEndNoteInfo::KnowsPageDesc() const
{
    return (aPageDescDep.GetRegisteredIn() != 0);
}

bool SwEndNoteInfo::DependsOn( const SwPageDesc* pDesc ) const
{
    return ( aPageDescDep.GetRegisteredIn() == pDesc );
}

void SwEndNoteInfo::ChgPageDesc( SwPageDesc *pDesc )
{
	pDesc->Add( &((SwClient&)aPageDescDep) );
}

void SwEndNoteInfo::SetFtnTxtColl(SwTxtFmtColl& rFmt)
{
	rFmt.Add(this);
}

SwCharFmt* SwEndNoteInfo::GetCharFmt(SwDoc &rDoc) const
{
	if ( !aCharFmtDep.GetRegisteredIn() )
	{
		SwCharFmt* pFmt = rDoc.GetCharFmtFromPool( static_cast<sal_uInt16>(
            m_bEndNote ? RES_POOLCHR_ENDNOTE : RES_POOLCHR_FOOTNOTE ) );
		pFmt->Add( &((SwClient&)aCharFmtDep) );
	}
	return (SwCharFmt*)aCharFmtDep.GetRegisteredIn();
}

void SwEndNoteInfo::SetCharFmt( SwCharFmt* pChFmt )
{
	DBG_ASSERT(pChFmt, "kein CharFmt?");
	pChFmt->Add( &((SwClient&)aCharFmtDep) );
}

SwCharFmt* SwEndNoteInfo::GetAnchorCharFmt(SwDoc &rDoc) const
{
	if( !aAnchorCharFmtDep.GetRegisteredIn() )
	{
		SwCharFmt* pFmt = rDoc.GetCharFmtFromPool( static_cast<sal_uInt16>(
            m_bEndNote ? RES_POOLCHR_ENDNOTE_ANCHOR : RES_POOLCHR_FOOTNOTE_ANCHOR ) );
		pFmt->Add( &((SwClient&)aAnchorCharFmtDep) );
	}
	return (SwCharFmt*)aAnchorCharFmtDep.GetRegisteredIn();
}

void SwEndNoteInfo::SetAnchorCharFmt( SwCharFmt* pChFmt )
{
	DBG_ASSERT(pChFmt, "kein CharFmt?");
	pChFmt->Add( &((SwClient&)aAnchorCharFmtDep) );
}

void SwEndNoteInfo::Modify( const SfxPoolItem* pOld, const SfxPoolItem* pNew )
{
	sal_uInt16 nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0 ;

	if( RES_ATTRSET_CHG == nWhich ||
		RES_FMT_CHG == nWhich )
	{
		SwDoc* pDoc;
		if( aCharFmtDep.GetRegisteredIn() )
			pDoc = ((SwCharFmt*)aCharFmtDep.GetRegisteredIn())->GetDoc();
		else
			pDoc = ((SwCharFmt*)aAnchorCharFmtDep.GetRegisteredIn())->GetDoc();
		SwFtnIdxs& rFtnIdxs = pDoc->GetFtnIdxs();
		for( sal_uInt16 nPos = 0; nPos < rFtnIdxs.Count(); ++nPos )
		{
			SwTxtFtn *pTxtFtn = rFtnIdxs[ nPos ];
			const SwFmtFtn &rFtn = pTxtFtn->GetFtn();
            if ( rFtn.IsEndNote() == m_bEndNote )
            {
				pTxtFtn->SetNumber( rFtn.GetNumber(), &rFtn.GetNumStr());
            }
		}
	}
	else
		CheckRegistration( pOld, pNew );
}

SwFtnInfo& SwFtnInfo::operator=(const SwFtnInfo& rInfo)
{
	SwEndNoteInfo::operator=(rInfo);
	aQuoVadis =  rInfo.aQuoVadis;
	aErgoSum = rInfo.aErgoSum;
	ePos = rInfo.ePos;
	eNum = rInfo.eNum;
	return *this;
}


sal_Bool SwFtnInfo::operator==( const SwFtnInfo& rInfo ) const
{
	return	ePos == rInfo.ePos &&
			eNum == rInfo.eNum &&
			SwEndNoteInfo::operator==(rInfo) &&
			aQuoVadis == rInfo.aQuoVadis &&
			aErgoSum == rInfo.aErgoSum;
}


SwFtnInfo::SwFtnInfo(const SwFtnInfo& rInfo) :
	SwEndNoteInfo( rInfo ),
	aQuoVadis( rInfo.aQuoVadis ),
	aErgoSum( rInfo.aErgoSum ),
	ePos( rInfo.ePos ),
	eNum( rInfo.eNum )
{
    m_bEndNote = false;
}

SwFtnInfo::SwFtnInfo(SwTxtFmtColl *pFmt) :
	SwEndNoteInfo( pFmt ),
	ePos( FTNPOS_PAGE ),
	eNum( FTNNUM_DOC )
{
	aFmt.SetNumberingType(SVX_NUM_ARABIC);
    m_bEndNote = false;
}

/*********************** SwDoc ***************************/


void SwDoc::SetFtnInfo(const SwFtnInfo& rInfo)
{
	SwRootFrm* pTmpRoot = GetCurrentLayout();//swmod 080219
	if( !(GetFtnInfo() == rInfo) )
	{
		const SwFtnInfo &rOld = GetFtnInfo();

        if (GetIDocumentUndoRedo().DoesUndo())
        {
            GetIDocumentUndoRedo().AppendUndo( new SwUndoFootNoteInfo(rOld) );
        }

		sal_Bool bFtnPos  = rInfo.ePos != rOld.ePos;
		sal_Bool bFtnDesc = rOld.ePos == FTNPOS_CHAPTER &&
							rInfo.GetPageDesc( *this ) != rOld.GetPageDesc( *this );
		sal_Bool bExtra   = rInfo.aQuoVadis != rOld.aQuoVadis ||
							rInfo.aErgoSum != rOld.aErgoSum ||
							rInfo.aFmt.GetNumberingType() != rOld.aFmt.GetNumberingType() ||
							rInfo.GetPrefix() != rOld.GetPrefix() ||
							rInfo.GetSuffix() != rOld.GetSuffix();
		SwCharFmt *pOldChrFmt = rOld.GetCharFmt( *this ),
				  *pNewChrFmt = rInfo.GetCharFmt( *this );
		sal_Bool bFtnChrFmts = pOldChrFmt != pNewChrFmt;

		*pFtnInfo = rInfo;

		if (pTmpRoot)
		{
			std::set<SwRootFrm*> aAllLayouts = GetAllLayouts();//swmod 080304
			if ( bFtnPos )
				//pTmpRoot->RemoveFtns();
				std::for_each( aAllLayouts.begin(), aAllLayouts.end(),std::mem_fun(&SwRootFrm::AllRemoveFtns));//swmod 080305
			else
			{
				//pTmpRoot->UpdateFtnNums();
				std::for_each( aAllLayouts.begin(), aAllLayouts.end(),std::mem_fun(&SwRootFrm::UpdateFtnNums));//swmod 080304
				if ( bFtnDesc )
					//pTmpRoot->CheckFtnPageDescs( FALSE );
					std::for_each( aAllLayouts.begin(), aAllLayouts.end(),std::bind2nd(std::mem_fun(&SwRootFrm::CheckFtnPageDescs), sal_False));//swmod 080304
				if ( bExtra )
				{
					//Fuer die Benachrichtung bezueglich ErgoSum usw. sparen wir uns
					//extra-Code und nutzen die vorhandenen Wege.
					SwFtnIdxs& rFtnIdxs = GetFtnIdxs();
					for( sal_uInt16 nPos = 0; nPos < rFtnIdxs.Count(); ++nPos )
					{
						SwTxtFtn *pTxtFtn = rFtnIdxs[ nPos ];
						const SwFmtFtn &rFtn = pTxtFtn->GetFtn();
						if ( !rFtn.IsEndNote() )
							pTxtFtn->SetNumber( rFtn.GetNumber(), &rFtn.GetNumStr());
					}
				}
			}
		}	//swmod 080219
		if( FTNNUM_PAGE != rInfo.eNum )
			GetFtnIdxs().UpdateAllFtn();
		else if( bFtnChrFmts )
		{
			SwFmtChg aOld( pOldChrFmt );
			SwFmtChg aNew( pNewChrFmt );
			pFtnInfo->ModifyNotification( &aOld, &aNew );
		}

        // --> OD 2008-01-09 #i81002#
        // no update during loading
        if ( !IsInReading() )
        {
            UpdateRefFlds(NULL);
        }
        SetModified();
	}
}

void SwDoc::SetEndNoteInfo(const SwEndNoteInfo& rInfo)
{
	SwRootFrm* pTmpRoot = GetCurrentLayout();//swmod 080219
	if( !(GetEndNoteInfo() == rInfo) )
	{
        if(GetIDocumentUndoRedo().DoesUndo())
        {
            SwUndo *const pUndo( new SwUndoEndNoteInfo( GetEndNoteInfo() ) );
            GetIDocumentUndoRedo().AppendUndo(pUndo);
        }

		sal_Bool bNumChg  = rInfo.nFtnOffset != GetEndNoteInfo().nFtnOffset;
        // this seems to be an optimization: UpdateAllFtn() is only called
        // if the offset changes; if the offset is the same,
        // but type/prefix/suffix changes, just set new numbers.
        bool const bExtra = !bNumChg &&
                (   (rInfo.aFmt.GetNumberingType() !=
                        GetEndNoteInfo().aFmt.GetNumberingType())
                ||  (rInfo.GetPrefix() != GetEndNoteInfo().GetPrefix())
                ||  (rInfo.GetSuffix() != GetEndNoteInfo().GetSuffix())
                );
		sal_Bool bFtnDesc = rInfo.GetPageDesc( *this ) !=
							GetEndNoteInfo().GetPageDesc( *this );
		SwCharFmt *pOldChrFmt = GetEndNoteInfo().GetCharFmt( *this ),
				  *pNewChrFmt = rInfo.GetCharFmt( *this );
		sal_Bool bFtnChrFmts = pOldChrFmt != pNewChrFmt;

		*pEndNoteInfo = rInfo;

		if ( pTmpRoot )
		{
			if ( bFtnDesc )
				//pTmpRoot->CheckFtnPageDescs( TRUE );
			{
				std::set<SwRootFrm*> aAllLayouts = GetAllLayouts();
			    std::for_each( aAllLayouts.begin(), aAllLayouts.end(),std::bind2nd(std::mem_fun(&SwRootFrm::CheckFtnPageDescs), sal_True));//swmod 080304
			}
			if ( bExtra )
			{
				//Fuer die Benachrichtung bezueglich ErgoSum usw. sparen wir uns
				//extra-Code und nutzen die vorhandenen Wege.
				SwFtnIdxs& rFtnIdxs = GetFtnIdxs();
				for( sal_uInt16 nPos = 0; nPos < rFtnIdxs.Count(); ++nPos )
				{
					SwTxtFtn *pTxtFtn = rFtnIdxs[ nPos ];
					const SwFmtFtn &rFtn = pTxtFtn->GetFtn();
					if ( rFtn.IsEndNote() )
						pTxtFtn->SetNumber( rFtn.GetNumber(), &rFtn.GetNumStr());
				}
			}
		}	//swmod 080219
		if( bNumChg )
			GetFtnIdxs().UpdateAllFtn();
		else if( bFtnChrFmts )
		{
			SwFmtChg aOld( pOldChrFmt );
			SwFmtChg aNew( pNewChrFmt );
			pEndNoteInfo->ModifyNotification( &aOld, &aNew );
		}

        // --> OD 2008-01-09 #i81002#
        // no update during loading
        if ( !IsInReading() )
        {
            UpdateRefFlds(NULL);
        }
		SetModified();
	}
}


bool SwDoc::SetCurFtn( const SwPaM& rPam, const String& rNumStr,
                       sal_uInt16 nNumber, bool bIsEndNote )
{
	SwFtnIdxs& rFtnArr = GetFtnIdxs();
	SwRootFrm* pTmpRoot = GetCurrentLayout();//swmod 080219

	const SwPosition* pStt = rPam.Start(), *pEnd = rPam.End();
	const sal_uLong nSttNd = pStt->nNode.GetIndex();
	const xub_StrLen nSttCnt = pStt->nContent.GetIndex();
	const sal_uLong nEndNd = pEnd->nNode.GetIndex();
	const xub_StrLen nEndCnt = pEnd->nContent.GetIndex();

	sal_uInt16 nPos;
	rFtnArr.SeekEntry( pStt->nNode, &nPos );

    SwUndoChangeFootNote* pUndo = 0;
    if (GetIDocumentUndoRedo().DoesUndo())
    {
        GetIDocumentUndoRedo().ClearRedo(); // AppendUndo far below, so leave it
        pUndo = new SwUndoChangeFootNote( rPam, rNumStr, nNumber, bIsEndNote );
    }

	SwTxtFtn* pTxtFtn;
	sal_uLong nIdx;
	sal_Bool bChg = sal_False;
	sal_Bool bTypeChgd = sal_False;
	sal_uInt16 n = nPos;		// sichern
	while( nPos < rFtnArr.Count() &&
			(( nIdx = _SwTxtFtn_GetIndex((pTxtFtn = rFtnArr[ nPos++ ] )))
				< nEndNd || ( nIdx == nEndNd &&
				nEndCnt >= *pTxtFtn->GetStart() )) )
		if( nIdx > nSttNd || ( nIdx == nSttNd &&
				nSttCnt <= *pTxtFtn->GetStart() ) )
		{
			const SwFmtFtn& rFtn = pTxtFtn->GetFtn();
			if( /*rFtn.GetNumber() != nNumber ||*/
				rFtn.GetNumStr() != rNumStr ||
				rFtn.IsEndNote() != bIsEndNote )
			{
				bChg = sal_True;
                if ( pUndo )
                {
                    pUndo->GetHistory().Add( *pTxtFtn );
                }

				pTxtFtn->SetNumber( nNumber, &rNumStr );
				if( rFtn.IsEndNote() != bIsEndNote )
				{
					((SwFmtFtn&)rFtn).SetEndNote( bIsEndNote );
					bTypeChgd = sal_True;
					pTxtFtn->CheckCondColl();
                    //#i11339# dispose UNO wrapper when a footnote is changed to an endnote or vice versa
                    SwPtrMsgPoolItem aMsgHint( RES_FOOTNOTE_DELETED, (void*)&pTxtFtn->GetAttr() );
                    GetUnoCallBack()->ModifyNotification( &aMsgHint, &aMsgHint );
                }
			}
		}

	nPos = n;		// nach vorne gibt es auch noch welche !
	while( nPos &&
			(( nIdx = _SwTxtFtn_GetIndex((pTxtFtn = rFtnArr[ --nPos ] )))
				> nSttNd || ( nIdx == nSttNd &&
				nSttCnt <= *pTxtFtn->GetStart() )) )
		if( nIdx < nEndNd || ( nIdx == nEndNd &&
			nEndCnt >= *pTxtFtn->GetStart() ) )
		{
			const SwFmtFtn& rFtn = pTxtFtn->GetFtn();
			if( /*rFtn.GetNumber() != nNumber ||*/
				rFtn.GetNumStr() != rNumStr ||
				rFtn.IsEndNote() != bIsEndNote )
			{
				bChg = sal_True;
                if ( pUndo )
                {
                    pUndo->GetHistory().Add( *pTxtFtn );
                }

				pTxtFtn->SetNumber( nNumber, &rNumStr );
				if( rFtn.IsEndNote() != bIsEndNote )
				{
					((SwFmtFtn&)rFtn).SetEndNote( bIsEndNote );
					bTypeChgd = sal_True;
					pTxtFtn->CheckCondColl();
				}
			}
		}

	// wer muss angestossen werden ??
	if( bChg )
	{
		if( pUndo )
        {
            GetIDocumentUndoRedo().AppendUndo(pUndo);
        }

		if ( bTypeChgd )
			rFtnArr.UpdateAllFtn();
		if( FTNNUM_PAGE != GetFtnInfo().eNum )
		{
			if ( !bTypeChgd )
				rFtnArr.UpdateAllFtn();
		}
		else if( pTmpRoot )
			//			
		{
			std::set<SwRootFrm*> aAllLayouts = GetAllLayouts();
			std::for_each( aAllLayouts.begin(), aAllLayouts.end(),std::mem_fun(&SwRootFrm::UpdateFtnNums));
		}	//swmod 080304pTmpRoot->UpdateFtnNums();	//swmod 080219
        SetModified();
	}
	else
		delete pUndo;
	return bChg;
}





