/**************************************************************
 *
 * 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 <txtftn.hxx>
#include <fmtftn.hxx>
#include <ftninfo.hxx>
#include <doc.hxx>
#include <ftnidx.hxx>
#include <ndtxt.hxx>
#include <ndindex.hxx>
#include <section.hxx>
#include <fmtftntx.hxx>
#include <rootfrm.hxx>


_SV_IMPL_SORTAR_ALG( _SwFtnIdxs, SwTxtFtnPtr )
sal_Bool _SwFtnIdxs::Seek_Entry( const SwTxtFtnPtr rSrch, sal_uInt16* pFndPos ) const
{
	sal_uLong nIdx = _SwTxtFtn_GetIndex( rSrch );
	xub_StrLen nCntIdx = *rSrch->GetStart();

	sal_uInt16 nO = Count(), nM, nU = 0;
	if( nO > 0 )
	{
		nO--;
		while( nU <= nO )
		{
			nM = nU + ( nO - nU ) / 2;
			sal_uLong nFndIdx = _SwTxtFtn_GetIndex( (*this)[ nM ] );
			if( nFndIdx == nIdx && *(*this)[ nM ]->GetStart() == nCntIdx )
			{
				if( pFndPos )
					*pFndPos = nM;
				return sal_True;
			}
			else if( nFndIdx < nIdx ||
				(nFndIdx == nIdx && *(*this)[ nM ]->GetStart() < nCntIdx ))
				nU = nM + 1;
			else if( nM == 0 )
			{
				if( pFndPos )
					*pFndPos = nU;
				return sal_False;
			}
			else
				nO = nM - 1;
		}
	}
	if( pFndPos )
		*pFndPos = nU;
	return sal_False;
}


void SwFtnIdxs::UpdateFtn( const SwNodeIndex& rStt )
{
	if( !Count() )
		return;

	// besorge erstmal das Nodes-Array ueber den StartIndex der ersten Fussnote
	SwDoc* pDoc = rStt.GetNode().GetDoc();
	if( pDoc->IsInReading() )
		return ;
	SwTxtFtn* pTxtFtn;

	const SwEndNoteInfo& rEndInfo = pDoc->GetEndNoteInfo();
	const SwFtnInfo& rFtnInfo = pDoc->GetFtnInfo();

	//Fuer normale Fussnoten werden Chapter- und Dokumentweise Nummerierung
	//getrennt behandelt. Fuer Endnoten gibt es nur die Dokumentweise
	//Nummerierung.
	if( FTNNUM_CHAPTER == rFtnInfo.eNum )
	{
		const SwOutlineNodes& rOutlNds = pDoc->GetNodes().GetOutLineNds();
		const SwNode* pCapStt = &pDoc->GetNodes().GetEndOfExtras();
		sal_uLong nCapEnd = pDoc->GetNodes().GetEndOfContent().GetIndex();
		if( rOutlNds.Count() )
		{
			// suche den Start des Kapitels, in den rStt steht.
			sal_uInt16 n;

			for( n = 0; n < rOutlNds.Count(); ++n )
				if( rOutlNds[ n ]->GetIndex() > rStt.GetIndex() )
					break;		// gefunden
				//else if( !rOutlNds[ n ]->GetTxtNode()->GetTxtColl()->GetOutlineLevel() )	//#outline level,zhaojianwei
                else if ( rOutlNds[ n ]->GetTxtNode()->GetAttrOutlineLevel() == 1 )   //<-end,zhaojianwei
					pCapStt = rOutlNds[ n ];	// Start eines neuen Kapitels
			// dann suche jetzt noch das Ende vom Bereich
			for( ; n < rOutlNds.Count(); ++n )
				//if( !rOutlNds[ n ]->GetTxtNode()->GetTxtColl()->GetOutlineLevel() )//#outline level,zhaojianwei
                if ( rOutlNds[ n ]->GetTxtNode()->GetAttrOutlineLevel() == 1 )//<-end,zhaojianwei
				{
					nCapEnd = rOutlNds[ n ]->GetIndex();	// Ende des gefundenen Kapitels
					break;
				}
		}

		sal_uInt16 nPos, nFtnNo = 1;
		if( SeekEntry( *pCapStt, &nPos ) && nPos )
		{
			// gehe nach vorne bis der Index nicht mehr gleich ist
			const SwNode* pCmpNd = &rStt.GetNode();
			while( nPos && pCmpNd == &((*this)[ --nPos ]->GetTxtNode()) )
				;
			++nPos;
		}

		if( nPos == Count() )		// nichts gefunden
			return;

		if( !rOutlNds.Count() )
			nFtnNo = nPos+1;

		for( ; nPos < Count(); ++nPos )
		{
			pTxtFtn = (*this)[ nPos ];
			if( pTxtFtn->GetTxtNode().GetIndex() >= nCapEnd )
				break;

			const SwFmtFtn &rFtn = pTxtFtn->GetFtn();
			if( !rFtn.GetNumStr().Len() && !rFtn.IsEndNote() &&
				!SwUpdFtnEndNtAtEnd::FindSectNdWithEndAttr( *pTxtFtn ))
				pTxtFtn->SetNumber( rFtnInfo.nFtnOffset + nFtnNo++,
									&rFtn.GetNumStr() );
		}
	}

	SwUpdFtnEndNtAtEnd aNumArr;

	// sal_Bool, damit hier auch bei Chapter-Einstellung die Endnoten
	// durchlaufen.
	const sal_Bool bEndNoteOnly = FTNNUM_DOC != rFtnInfo.eNum;

	sal_uInt16 nPos, nFtnNo = 1, nEndNo = 1;
	sal_uLong nUpdNdIdx = rStt.GetIndex();
	for( nPos = 0; nPos < Count(); ++nPos )
	{
		pTxtFtn = (*this)[ nPos ];
		if( nUpdNdIdx <= pTxtFtn->GetTxtNode().GetIndex() )
			break;

		const SwFmtFtn &rFtn = pTxtFtn->GetFtn();
		if( !rFtn.GetNumStr().Len() )
		{
			if( !aNumArr.ChkNumber( *pTxtFtn ) )
			{
				if( pTxtFtn->GetFtn().IsEndNote() )
					nEndNo++;
				else
					nFtnNo++;
			}
		}
	}

	// ab nPos bei allen FootNotes die Array-Nummer setzen
	for( ; nPos < Count(); ++nPos )
	{
		pTxtFtn = (*this)[ nPos ];
		const SwFmtFtn &rFtn = pTxtFtn->GetFtn();
		if( !rFtn.GetNumStr().Len() )
		{
			sal_uInt16 nSectNo = aNumArr.ChkNumber( *pTxtFtn );
			if( !nSectNo && ( rFtn.IsEndNote() || !bEndNoteOnly ))
				nSectNo = rFtn.IsEndNote()
							? rEndInfo.nFtnOffset + nEndNo++
							: rFtnInfo.nFtnOffset + nFtnNo++;

			if( nSectNo )
			{
				if( rFtn.IsEndNote() )
					pTxtFtn->SetNumber( nSectNo, &rFtn.GetNumStr() );
				else
					pTxtFtn->SetNumber( nSectNo, &rFtn.GetNumStr() );
			}
		}
	}
	// Pageweise wird vom MA erfuellt !!
}


void SwFtnIdxs::UpdateAllFtn()
{
	if( !Count() )
		return;

	// besorge erstmal das Nodes-Array ueber den StartIndex der
	// ersten Fussnote
	SwDoc* pDoc = (SwDoc*) (*this)[ 0 ]->GetTxtNode().GetDoc();
	SwTxtFtn* pTxtFtn;
	const SwEndNoteInfo& rEndInfo = pDoc->GetEndNoteInfo();
	const SwFtnInfo& rFtnInfo = pDoc->GetFtnInfo();

	SwUpdFtnEndNtAtEnd aNumArr;

	SwRootFrm* pTmpRoot = pDoc->GetCurrentLayout();//swmod 080305
	std::set<SwRootFrm*> aAllLayouts = pDoc->GetAllLayouts();
	//Fuer normale Fussnoten werden Chapter- und Dokumentweise Nummerierung
	//getrennt behandelt. Fuer Endnoten gibt es nur die Dokumentweise
	//Nummerierung.
	if( FTNNUM_CHAPTER == rFtnInfo.eNum )
	{
		const SwOutlineNodes& rOutlNds = pDoc->GetNodes().GetOutLineNds();
		sal_uInt16 nNo = 1,			// Nummer fuer die Fussnoten
			   nFtnIdx = 0;		// Index in das FtnIdx-Array
		for( sal_uInt16 n = 0; n < rOutlNds.Count(); ++n )
		{
            if ( rOutlNds[ n ]->GetTxtNode()->GetAttrOutlineLevel() == 1 )//<-end,zhaojianwei
			{
				sal_uLong nCapStt = rOutlNds[ n ]->GetIndex();	// Start eines neuen Kapitels
				for( ; nFtnIdx < Count(); ++nFtnIdx )
				{
					pTxtFtn = (*this)[ nFtnIdx ];
					if( pTxtFtn->GetTxtNode().GetIndex() >= nCapStt )
						break;

					// Endnoten nur Dokumentweise
					const SwFmtFtn &rFtn = pTxtFtn->GetFtn();
					if( !rFtn.IsEndNote() && !rFtn.GetNumStr().Len() &&
						!SwUpdFtnEndNtAtEnd::FindSectNdWithEndAttr( *pTxtFtn ))
						pTxtFtn->SetNumber( rFtnInfo.nFtnOffset + nNo++,
											&rFtn.GetNumStr() );
				}
				if( nFtnIdx >= Count() )
					break;			// ok alles geupdatet
				nNo = 1;
			}
		}

		for( nNo = 1; nFtnIdx < Count(); ++nFtnIdx )
		{
			//Endnoten nur Dokumentweise
			pTxtFtn = (*this)[ nFtnIdx ];
			const SwFmtFtn &rFtn = pTxtFtn->GetFtn();
			if( !rFtn.IsEndNote() && !rFtn.GetNumStr().Len() &&
				!SwUpdFtnEndNtAtEnd::FindSectNdWithEndAttr( *pTxtFtn ))
				pTxtFtn->SetNumber( rFtnInfo.nFtnOffset + nNo++,
									&rFtn.GetNumStr() );
		}

	}

	// sal_Bool, damit hier auch bei Chapter-Einstellung die Endnoten
	// durchlaufen.
	const sal_Bool bEndNoteOnly = FTNNUM_DOC != rFtnInfo.eNum;
	sal_uInt16 nFtnNo = 0, nEndNo = 0;
	for( sal_uInt16 nPos = 0; nPos < Count(); ++nPos )
	{
		pTxtFtn = (*this)[ nPos ];
		const SwFmtFtn &rFtn = pTxtFtn->GetFtn();
		if( !rFtn.GetNumStr().Len() )
		{
			sal_uInt16 nSectNo = aNumArr.ChkNumber( *pTxtFtn );
			if( !nSectNo && ( rFtn.IsEndNote() || !bEndNoteOnly ))
				nSectNo = rFtn.IsEndNote()
								? rEndInfo.nFtnOffset + (++nEndNo)
								: rFtnInfo.nFtnOffset + (++nFtnNo);

			if( nSectNo )
			{
				if( rFtn.IsEndNote() )
					pTxtFtn->SetNumber( nSectNo, &rFtn.GetNumStr() );
				else
					pTxtFtn->SetNumber( nSectNo, &rFtn.GetNumStr() );
			}
		}
	}

	if( pTmpRoot && FTNNUM_PAGE == rFtnInfo.eNum )
		std::for_each( aAllLayouts.begin(), aAllLayouts.end(),std::mem_fun(&SwRootFrm::UpdateFtnNums));//swmod 0
}

SwTxtFtn* SwFtnIdxs::SeekEntry( const SwNodeIndex& rPos, sal_uInt16* pFndPos ) const
{
	sal_uLong nIdx = rPos.GetIndex();

	sal_uInt16 nO = Count(), nM, nU = 0;
	if( nO > 0 )
	{
		nO--;
		while( nU <= nO )
		{
			nM = nU + ( nO - nU ) / 2;
			sal_uLong nNdIdx = _SwTxtFtn_GetIndex( (*this)[ nM ] );
			if( nNdIdx == nIdx )
			{
				if( pFndPos )
					*pFndPos = nM;
				return (*this)[ nM ];
			}
			else if( nNdIdx < nIdx )
				nU = nM + 1;
			else if( nM == 0 )
			{
				if( pFndPos )
					*pFndPos = nU;
				return 0;
			}
			else
				nO = nM - 1;
		}
	}
	if( pFndPos )
		*pFndPos = nU;
	return 0;
}

/* */

const SwSectionNode* SwUpdFtnEndNtAtEnd::FindSectNdWithEndAttr(
				const SwTxtFtn& rTxtFtn )
{
	sal_uInt16 nWh = static_cast<sal_uInt16>( rTxtFtn.GetFtn().IsEndNote() ?
                        RES_END_AT_TXTEND :	RES_FTN_AT_TXTEND );
	sal_uInt16 nVal;
	const SwSectionNode* pNd = rTxtFtn.GetTxtNode().FindSectionNode();
	while( pNd && FTNEND_ATTXTEND_OWNNUMSEQ != ( nVal =
			((const SwFmtFtnAtTxtEnd&)pNd->GetSection().GetFmt()->
            GetFmtAttr( nWh, sal_True )).GetValue() ) &&
			FTNEND_ATTXTEND_OWNNUMANDFMT != nVal )
        pNd = pNd->StartOfSectionNode()->FindSectionNode();

	return pNd;
}

sal_uInt16 SwUpdFtnEndNtAtEnd::GetNumber( const SwTxtFtn& rTxtFtn,
									const SwSectionNode& rNd )
{
	sal_uInt16 nRet = 0, nWh;
	SvPtrarr* pArr;
	SvUShorts* pNum;
	if( rTxtFtn.GetFtn().IsEndNote() )
	{
		pArr = &aEndSects;
		pNum = &aEndNums;
		nWh = RES_END_AT_TXTEND;
	}
	else
	{
		pArr = &aFtnSects;
		pNum = &aFtnNums;
		nWh = RES_FTN_AT_TXTEND;
	}
	void* pNd = (void*)&rNd;

	for( sal_uInt16 n = pArr->Count(); n; )
		if( pArr->GetObject( --n ) == pNd )
		{
			nRet = ++pNum->GetObject( n );
			break;
		}

	if( !nRet )
	{
		pArr->Insert( pNd, pArr->Count() );
		nRet = ((SwFmtFtnEndAtTxtEnd&)rNd.GetSection().GetFmt()->
                                GetFmtAttr( nWh )).GetOffset();
		++nRet;
		pNum->Insert( nRet, pNum->Count() );
	}
	return nRet;
}

sal_uInt16 SwUpdFtnEndNtAtEnd::ChkNumber( const SwTxtFtn& rTxtFtn )
{
	const SwSectionNode* pSectNd = FindSectNdWithEndAttr( rTxtFtn );
	return pSectNd ? GetNumber( rTxtFtn, *pSectNd ) : 0;
}
