/**************************************************************
 * 
 * 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"



#define _SVSTDARR_USHORTS
#define _SVSTDARR_USHORTSSORT
#include <svl/svstdarr.hxx>
#include <doc.hxx>
#include <cntfrm.hxx>       // ASSERT in ~SwTxtFtn()
#include <pagefrm.hxx>      // RemoveFtn()
#include <fmtftn.hxx>
#include <txtftn.hxx>
#include <ftnidx.hxx>
#include <ftninfo.hxx>
#include <swfont.hxx>
#include <ndtxt.hxx>
#include <poolfmt.hxx>
#include <ftnfrm.hxx>
#include <ndindex.hxx>
#include <fmtftntx.hxx>
#include <section.hxx>
#include <switerator.hxx>

/*************************************************************************
|*
|*    class SwFmtFtn
|*
*************************************************************************/


SwFmtFtn::SwFmtFtn( bool bEndNote )
	: SfxPoolItem( RES_TXTATR_FTN ),
    pTxtAttr( 0 ),
    nNumber( 0 ),
    m_bEndNote( bEndNote )
{
}


int SwFmtFtn::operator==( const SfxPoolItem& rAttr ) const
{
	ASSERT( SfxPoolItem::operator==( rAttr ), "keine gleichen Attribute" );
	return nNumber	== ((SwFmtFtn&)rAttr).nNumber &&
		   aNumber	== ((SwFmtFtn&)rAttr).aNumber &&
           m_bEndNote == ((SwFmtFtn&)rAttr).m_bEndNote;
}


SfxPoolItem* SwFmtFtn::Clone( SfxItemPool* ) const
{
	SwFmtFtn* pNew	= new SwFmtFtn;
	pNew->aNumber	= aNumber;
	pNew->nNumber	= nNumber;
    pNew->m_bEndNote = m_bEndNote;
	return pNew;
}

void SwFmtFtn::SetEndNote( bool b )
{
    if ( b != m_bEndNote )
    {
        if ( GetTxtFtn() )
        {
            GetTxtFtn()->DelFrms(0);
        }
        m_bEndNote = b;
    }
}

SwFmtFtn::~SwFmtFtn()
{
}


void SwFmtFtn::GetFtnText( XubString& rStr ) const
{
	if( pTxtAttr->GetStartNode() )
	{
		SwNodeIndex aIdx( *pTxtAttr->GetStartNode(), 1 );
		SwCntntNode* pCNd = aIdx.GetNode().GetTxtNode();
		if( !pCNd )
			pCNd = aIdx.GetNodes().GoNext( &aIdx );

		if( pCNd->IsTxtNode() )
			rStr = ((SwTxtNode*)pCNd)->GetExpandTxt();
	}
}

	// returnt den anzuzeigenden String der Fuss-/Endnote
XubString SwFmtFtn::GetViewNumStr( const SwDoc& rDoc, sal_Bool bInclStrings ) const
{
	XubString sRet( GetNumStr() );
	if( !sRet.Len() )
	{
		// dann ist die Nummer von Interesse, also ueber die Info diese
		// besorgen.
		sal_Bool bMakeNum = sal_True;
		const SwSectionNode* pSectNd = pTxtAttr
					? SwUpdFtnEndNtAtEnd::FindSectNdWithEndAttr( *pTxtAttr )
					: 0;

		if( pSectNd )
		{
			const SwFmtFtnEndAtTxtEnd& rFtnEnd = (SwFmtFtnEndAtTxtEnd&)
                pSectNd->GetSection().GetFmt()->GetFmtAttr(
					            IsEndNote() ?
                                static_cast<sal_uInt16>(RES_END_AT_TXTEND) :
                                static_cast<sal_uInt16>(RES_FTN_AT_TXTEND) );

			if( FTNEND_ATTXTEND_OWNNUMANDFMT == rFtnEnd.GetValue() )
			{
				bMakeNum = sal_False;
				sRet = rFtnEnd.GetSwNumType().GetNumStr( GetNumber() );
				if( bInclStrings )
				{
					sRet.Insert( rFtnEnd.GetPrefix(), 0 );
					sRet += rFtnEnd.GetSuffix();
				}
			}
		}

		if( bMakeNum )
		{
			const SwEndNoteInfo* pInfo;
			if( IsEndNote() )
				pInfo = &rDoc.GetEndNoteInfo();
			else
				pInfo = &rDoc.GetFtnInfo();
			sRet = pInfo->aFmt.GetNumStr( GetNumber() );
			if( bInclStrings )
			{
				sRet.Insert( pInfo->GetPrefix(), 0 );
				sRet += pInfo->GetSuffix();
			}
		}
	}
	return sRet;
}

/*************************************************************************
 *						class SwTxt/FmtFnt
 *************************************************************************/

SwTxtFtn::SwTxtFtn( SwFmtFtn& rAttr, xub_StrLen nStartPos )
    : SwTxtAttr( rAttr, nStartPos )
    , m_pStartNode( 0 )
    , m_pTxtNode( 0 )
    , m_nSeqNo( USHRT_MAX )
{
    rAttr.pTxtAttr = this;
    SetHasDummyChar(true);
}


SwTxtFtn::~SwTxtFtn()
{
	SetStartNode( 0 );
}



void SwTxtFtn::SetStartNode( const SwNodeIndex *pNewNode, sal_Bool bDelNode )
{
	if( pNewNode )
	{
        if ( !m_pStartNode )
        {
            m_pStartNode = new SwNodeIndex( *pNewNode );
        }
        else
        {
            *m_pStartNode = *pNewNode;
        }
    }
    else if ( m_pStartNode )
	{
		// Zwei Dinge muessen erledigt werden:
		// 1) Die Fussnoten muessen bei ihren Seiten abgemeldet werden
		// 2) Die Fussnoten-Sektion in den Inserts muss geloescht werden.
		SwDoc* pDoc;
        if ( m_pTxtNode )
        {
            pDoc = m_pTxtNode->GetDoc();
        }
		else
		{
			//JP 27.01.97: der sw3-Reader setzt einen StartNode aber das
			//				Attribut ist noch nicht im TextNode verankert.
			//				Wird es geloescht (z.B. bei Datei einfuegen mit
			//				Ftn in einen Rahmen), muss auch der Inhalt
			//				geloescht werden
            pDoc = m_pStartNode->GetNodes().GetDoc();
		}

		// Wir duerfen die Fussnotennodes nicht loeschen
		// und brauchen die Fussnotenframes nicht loeschen, wenn
		// wir im ~SwDoc() stehen.
		if( !pDoc->IsInDtor() )
		{
			if( bDelNode )
			{
				// 1) Die Section fuer die Fussnote wird beseitigt
				// Es kann sein, dass die Inserts schon geloescht wurden.
                pDoc->DeleteSection( &m_pStartNode->GetNode() );
			}
			else
				// Werden die Nodes nicht geloescht mussen sie bei den Seiten
				// abmeldet (Frms loeschen) werden, denn sonst bleiben sie
				// stehen (Undo loescht sie nicht!)
				DelFrms( 0 );
		}
        DELETEZ( m_pStartNode );

		// loesche die Fussnote noch aus dem Array am Dokument
		for( sal_uInt16 n = 0; n < pDoc->GetFtnIdxs().Count(); ++n )
			if( this == pDoc->GetFtnIdxs()[n] )
			{
				pDoc->GetFtnIdxs().Remove( n );
				// gibt noch weitere Fussnoten
				if( !pDoc->IsInDtor() && n < pDoc->GetFtnIdxs().Count() )
				{
					SwNodeIndex aTmp( pDoc->GetFtnIdxs()[n]->GetTxtNode() );
					pDoc->GetFtnIdxs().UpdateFtn( aTmp );
				}
				break;
			}
	}
}


void SwTxtFtn::SetNumber( const sal_uInt16 nNewNum, const XubString* pStr )
{
	SwFmtFtn& rFtn = (SwFmtFtn&)GetFtn();
	if( pStr && pStr->Len() )
		rFtn.aNumber = *pStr;
	else
	{
		rFtn.nNumber = nNewNum;
		rFtn.aNumber = aEmptyStr;
	}

    ASSERT( m_pTxtNode, "SwTxtFtn: where is my TxtNode?" );
    SwNodes &rNodes = m_pTxtNode->GetDoc()->GetNodes();
    m_pTxtNode->ModifyNotification( 0, &rFtn );
    if ( m_pStartNode )
    {
        // must iterate over all TxtNodes because of footnotes on other pages
        SwNode* pNd;
        sal_uLong nSttIdx = m_pStartNode->GetIndex() + 1;
        sal_uLong nEndIdx = m_pStartNode->GetNode().EndOfSectionIndex();
		for( ; nSttIdx < nEndIdx; ++nSttIdx )
		{
			// Es koennen ja auch Grafiken in der Fussnote stehen ...
			if( ( pNd = rNodes[ nSttIdx ] )->IsTxtNode() )
				((SwTxtNode*)pNd)->ModifyNotification( 0, &rFtn );
		}
	}
}

// Die Fussnoten duplizieren
void SwTxtFtn::CopyFtn(SwTxtFtn & rDest, SwTxtNode & rDestNode) const
{
    if (m_pStartNode && !rDest.GetStartNode())
    {
        // dest missing node section? create it here!
        // (happens in SwTxtNode::CopyText if pDest == this)
        rDest.MakeNewTextSection( rDestNode.GetNodes() );
    }
    if (m_pStartNode && rDest.GetStartNode())
    {
        // footnotes not necessarily in same document!
        SwDoc *const pDstDoc = rDestNode.GetDoc();
		SwNodes &rDstNodes = pDstDoc->GetNodes();

        // copy only the content of the section
        SwNodeRange aRg( *m_pStartNode, 1,
                    *m_pStartNode->GetNode().EndOfSectionNode() );

        // insert at the end of rDest, i.e., the nodes are appended.
        // nDestLen contains number of CntntNodes in rDest _before_ copy.
        SwNodeIndex aStart( *(rDest.GetStartNode()) );
		SwNodeIndex aEnd( *aStart.GetNode().EndOfSectionNode() );
		sal_uLong  nDestLen = aEnd.GetIndex() - aStart.GetIndex() - 1;

        m_pTxtNode->GetDoc()->CopyWithFlyInFly( aRg, 0, aEnd, sal_True );

        // in case the destination section was not empty, delete the old nodes
        // before:   Src: SxxxE,  Dst: SnE
        // now:      Src: SxxxE,  Dst: SnxxxE
        // after:    Src: SxxxE,  Dst: SxxxE
		aStart++;
		rDstNodes.Delete( aStart, nDestLen );
	}

    // also copy user defined number string
	if( GetFtn().aNumber.Len() )
    {
        const_cast<SwFmtFtn &>(rDest.GetFtn()).aNumber = GetFtn().aNumber;
    }
}


	// lege eine neue leere TextSection fuer diese Fussnote an
void SwTxtFtn::MakeNewTextSection( SwNodes& rNodes )
{
    if ( m_pStartNode )
        return;

	// Nun verpassen wir dem TxtNode noch die Fussnotenvorlage.
	SwTxtFmtColl *pFmtColl;
	const SwEndNoteInfo* pInfo;
	sal_uInt16 nPoolId;

	if( GetFtn().IsEndNote() )
	{
		pInfo = &rNodes.GetDoc()->GetEndNoteInfo();
		nPoolId = RES_POOLCOLL_ENDNOTE;
	}
	else
	{
		pInfo = &rNodes.GetDoc()->GetFtnInfo();
		nPoolId = RES_POOLCOLL_FOOTNOTE;
	}

	if( 0 == (pFmtColl = pInfo->GetFtnTxtColl() ) )
		pFmtColl = rNodes.GetDoc()->GetTxtCollFromPool( nPoolId );

	SwStartNode* pSttNd = rNodes.MakeTextSection( SwNodeIndex( rNodes.GetEndOfInserts() ),
										SwFootnoteStartNode, pFmtColl );
    m_pStartNode = new SwNodeIndex( *pSttNd );
}


void SwTxtFtn::DelFrms( const SwFrm* pSib )
{
    // delete the FtnFrames from the pages
    ASSERT( m_pTxtNode, "SwTxtFtn: where is my TxtNode?" );
    if ( !m_pTxtNode )
        return;

    const SwRootFrm* pRoot = pSib ? pSib->getRootFrm() : 0;
	sal_Bool bFrmFnd = sal_False;
	{
        SwIterator<SwCntntFrm,SwTxtNode> aIter( *m_pTxtNode );
		for( SwCntntFrm* pFnd = aIter.First(); pFnd; pFnd = aIter.Next() )
		{
            if( pRoot != pFnd->getRootFrm() && pRoot )
                continue;
            SwPageFrm* pPage = pFnd->FindPageFrm();
            if( pPage )
            {
                pPage->RemoveFtn( pFnd, this );
                bFrmFnd = sal_True;
            }
		}
	}
	//JP 13.05.97: falls das Layout vorm loeschen der Fussnoten entfernt
	//				wird, sollte man das ueber die Fussnote selbst tun
    if ( !bFrmFnd && m_pStartNode )
    {
        SwNodeIndex aIdx( *m_pStartNode );
        SwCntntNode* pCNd = m_pTxtNode->GetNodes().GoNext( &aIdx );
		if( pCNd )
		{
			SwIterator<SwCntntFrm,SwCntntNode> aIter( *pCNd );
			for( SwCntntFrm* pFnd = aIter.First(); pFnd; pFnd = aIter.Next() )
			{
                if( pRoot != pFnd->getRootFrm() && pRoot )
                    continue;
				SwPageFrm* pPage = pFnd->FindPageFrm();

				SwFrm *pFrm = pFnd->GetUpper();
				while ( pFrm && !pFrm->IsFtnFrm() )
					pFrm = pFrm->GetUpper();

				SwFtnFrm *pFtn = (SwFtnFrm*)pFrm;
				while ( pFtn && pFtn->GetMaster() )
					pFtn = pFtn->GetMaster();
                ASSERT( pFtn->GetAttr() == this, "Ftn mismatch error." );

				while ( pFtn )
				{
					SwFtnFrm *pFoll = pFtn->GetFollow();
					pFtn->Cut();
					delete pFtn;
					pFtn = pFoll;
				}

                // #i20556# During hiding of a section, the connection
                // to the layout is already lost. pPage may be 0:
                if ( pPage )
                    pPage->UpdateFtnNum();
			}
		}
	}
}


sal_uInt16 SwTxtFtn::SetSeqRefNo()
{
    if( !m_pTxtNode )
		return USHRT_MAX;

    SwDoc* pDoc = m_pTxtNode->GetDoc();
	if( pDoc->IsInReading() )
		return USHRT_MAX;

	sal_uInt16 n, nFtnCnt = pDoc->GetFtnIdxs().Count();

	const sal_uInt8 nTmp = 255 < nFtnCnt ? 255 : static_cast<sal_uInt8>(nFtnCnt);
	SvUShortsSort aArr( nTmp, nTmp );

	// dann testmal, ob die Nummer schon vergeben ist oder ob eine neue
	// bestimmt werden muss.
	SwTxtFtn* pTxtFtn;
	for( n = 0; n < nFtnCnt; ++n )
    {
        pTxtFtn = pDoc->GetFtnIdxs()[ n ];
        if ( pTxtFtn != this )
        {
            aArr.Insert( pTxtFtn->m_nSeqNo );
        }
    }

    // test if number is already in use
    if ( USHRT_MAX != m_nSeqNo )
    {
		for( n = 0; n < aArr.Count(); ++n )
        {
            if ( aArr[ n ] > m_nSeqNo )
            {
                return m_nSeqNo;    // free -> use
            }
            else if ( aArr[ n ] == m_nSeqNo )
            {
                break;              // used -> create new one
            }
        }

        if ( n == aArr.Count() )
        {
            return m_nSeqNo;        // free -> use
        }
    }

	// alle Nummern entsprechend geflag, also bestimme die richtige Nummer
	for( n = 0; n < aArr.Count(); ++n )
		if( n != aArr[ n ] )
			break;

    return m_nSeqNo = n;
}

void SwTxtFtn::SetUniqueSeqRefNo( SwDoc& rDoc )
{
	sal_uInt16 n, nStt = 0, nFtnCnt = rDoc.GetFtnIdxs().Count();

	const sal_uInt8 nTmp = 255 < nFtnCnt ? 255 : static_cast<sal_uInt8>(nFtnCnt);
	SvUShortsSort aArr( nTmp, nTmp );

	// dann alle Nummern zusammensammeln die schon existieren
	SwTxtFtn* pTxtFtn;
	for( n = 0; n < nFtnCnt; ++n )
    {
        pTxtFtn = rDoc.GetFtnIdxs()[ n ];
        if ( USHRT_MAX != pTxtFtn->m_nSeqNo )
        {
            aArr.Insert( pTxtFtn->m_nSeqNo );
        }
    }


	for( n = 0; n < nFtnCnt; ++n )
    {
        pTxtFtn = rDoc.GetFtnIdxs()[ n ];
        if ( USHRT_MAX == pTxtFtn->m_nSeqNo )
        {
			for( ; nStt < aArr.Count(); ++nStt )
            {
                if ( nStt != aArr[ nStt ] )
                {
                    pTxtFtn->m_nSeqNo = nStt;
					break;
				}
            }

            if ( USHRT_MAX == pTxtFtn->m_nSeqNo )
            {
                break; // found nothing
            }
        }
    }

	// alle Nummern schon vergeben, also mit nStt++ weitermachen
	for( ; n < nFtnCnt; ++n )
    {
        pTxtFtn = rDoc.GetFtnIdxs()[ n ];
        if ( USHRT_MAX == pTxtFtn->m_nSeqNo )
        {
            pTxtFtn->m_nSeqNo = nStt++;
        }
    }
}

void SwTxtFtn::CheckCondColl()
{
//FEATURE::CONDCOLL
	if( GetStartNode() )
		((SwStartNode&)GetStartNode()->GetNode()).CheckSectionCondColl();
//FEATURE::CONDCOLL
}




