/**************************************************************
 * 
 * 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 <hintids.hxx>

#include <editeng/tstpitem.hxx>
#include <editeng/lrspitem.hxx>
#include <editeng/scripttypeitem.hxx>
#include <com/sun/star/i18n/ScriptType.hdl>
#include <txatbase.hxx>
#include <txtftn.hxx>
#include <fmtftn.hxx>
#include <editsh.hxx>
#include <edimp.hxx>	// fuer MACROS
#include <doc.hxx>
#include <swundo.hxx>	// fuer UNDO-Ids
#include <ndtxt.hxx>
#include <ftnidx.hxx>
#include <expfld.hxx>
#include <rootfrm.hxx>
#include <cntfrm.hxx>
#include <breakit.hxx>
#include <txtfld.hxx>
#include <fmtfld.hxx>
#include <crsskip.hxx>
#include <txtfrm.hxx>		// SwTxtFrm
#include <scriptinfo.hxx>
#include <svl/ctloptions.hxx>
#include <charfmt.hxx>  // #i27615#
#include <numrule.hxx>


/*************************************
 * harte Formatierung (Attribute)
 *************************************/

// wenn Selektion groesser Max Nodes oder mehr als Max Selektionen
// => keine Attribute
const sal_uInt16& getMaxLookup()
{
	static const sal_uInt16 nMaxLookup = 1000;
	return nMaxLookup;
}

// --> OD 2008-01-16 #newlistlevelattrs#
sal_Bool SwEditShell::GetCurAttr( SfxItemSet& rSet,
                              const bool bMergeIndentValuesOfNumRule ) const
// <--
{
	if( GetCrsrCnt() > getMaxLookup() )
	{
		rSet.InvalidateAllItems();
		return sal_False;
	}

	SfxItemSet aSet( *rSet.GetPool(), rSet.GetRanges() );
	SfxItemSet *pSet = &rSet;

	FOREACHPAM_START(this)

        // #i27615# if the cursor is in front of the numbering label
        // the attributes to get are those from the numbering format.
        if (PCURCRSR->IsInFrontOfLabel())
        {
            SwTxtNode * pTxtNd =
                PCURCRSR->GetPoint()->nNode.GetNode().GetTxtNode();

            if (pTxtNd)
            {
                SwNumRule * pNumRule = pTxtNd->GetNumRule();

                if (pNumRule)
                {
                    const String & aCharFmtName =
                        pNumRule->Get(static_cast<sal_uInt16>(pTxtNd->GetActualListLevel())).GetCharFmtName();
                    SwCharFmt * pCharFmt =
                        GetDoc()->FindCharFmtByName(aCharFmtName);

                    if (pCharFmt)
                        rSet.Put(pCharFmt->GetAttrSet());
                }
            }

            continue;
        }

		sal_uLong nSttNd = PCURCRSR->GetMark()->nNode.GetIndex(),
			  nEndNd = PCURCRSR->GetPoint()->nNode.GetIndex();
		xub_StrLen nSttCnt = PCURCRSR->GetMark()->nContent.GetIndex(),
				   nEndCnt = PCURCRSR->GetPoint()->nContent.GetIndex();

		if( nSttNd > nEndNd || ( nSttNd == nEndNd && nSttCnt > nEndCnt ))
		{
			sal_uLong nTmp = nSttNd; nSttNd = nEndNd; nEndNd = nTmp;
			nTmp = nSttCnt; nSttCnt = nEndCnt; nEndCnt = (xub_StrLen)nTmp;
		}

		if( nEndNd - nSttNd >= getMaxLookup() )
		{
			rSet.ClearItem();
			rSet.InvalidateAllItems();
			return sal_False;
		}

		// beim 1.Node traegt der Node die Werte in den GetSet ein (Initial)
		// alle weiteren Nodes werden zum GetSet zu gemergt
		for( sal_uLong n = nSttNd; n <= nEndNd; ++n )
		{
			SwNode* pNd = GetDoc()->GetNodes()[ n ];
			switch( pNd->GetNodeType() )
			{
			case ND_TEXTNODE:
				{
					xub_StrLen nStt = n == nSttNd ? nSttCnt : 0,
						   	   nEnd = n == nEndNd ? nEndCnt
										: ((SwTxtNode*)pNd)->GetTxt().Len();
                    // --> OD 2008-01-16 #newlistlevelattrs#
                    ((SwTxtNode*)pNd)->GetAttr( *pSet, nStt, nEnd,
                                                sal_False, sal_True,
                                                bMergeIndentValuesOfNumRule );
                    // <--
				}
				break;
			case ND_GRFNODE:
			case ND_OLENODE:
				((SwCntntNode*)pNd)->GetAttr( *pSet );
				break;

			default:
				pNd = 0;
			}

			if( pNd )
			{
				if( pSet != &rSet )
					rSet.MergeValues( aSet );

				if( aSet.Count() )
					aSet.ClearItem();
			}
			pSet = &aSet;
		}

	FOREACHPAM_END()

	return sal_True;
}

SwTxtFmtColl* SwEditShell::GetCurTxtFmtColl() const
{
	SwTxtFmtColl *pFmt = 0;

	if ( GetCrsrCnt() > getMaxLookup() )
		return 0;

	FOREACHPAM_START(this)

		sal_uLong nSttNd = PCURCRSR->GetMark()->nNode.GetIndex(),
			  nEndNd = PCURCRSR->GetPoint()->nNode.GetIndex();
		xub_StrLen nSttCnt = PCURCRSR->GetMark()->nContent.GetIndex(),
				   nEndCnt = PCURCRSR->GetPoint()->nContent.GetIndex();

		if( nSttNd > nEndNd || ( nSttNd == nEndNd && nSttCnt > nEndCnt ))
		{
			sal_uLong nTmp = nSttNd; nSttNd = nEndNd; nEndNd = nTmp;
			nTmp = nSttCnt; nSttCnt = nEndCnt; nEndCnt = (xub_StrLen)nTmp;
		}

		if( nEndNd - nSttNd >= getMaxLookup() )
		{
			pFmt = 0;
			break;
		}

		for( sal_uLong n = nSttNd; n <= nEndNd; ++n )
		{
			SwNode* pNd = GetDoc()->GetNodes()[ n ];
			if( pNd->IsTxtNode() )
			{
				if( !pFmt )
					pFmt = ((SwTxtNode*)pNd)->GetTxtColl();
				else if( pFmt == ((SwTxtNode*)pNd)->GetTxtColl() ) // ???
					break;
			}
		}

	FOREACHPAM_END()
	return pFmt;
}



sal_Bool SwEditShell::GetCurFtn( SwFmtFtn* pFillFtn )
{
	// der Cursor muss auf dem akt. Fussnoten-Anker stehen:
	SwPaM* pCrsr = GetCrsr();
	SwTxtNode* pTxtNd = pCrsr->GetNode()->GetTxtNode();
	if( !pTxtNd )
		return sal_False;

    SwTxtAttr *const pFtn = pTxtNd->GetTxtAttrForCharAt(
        pCrsr->GetPoint()->nContent.GetIndex(), RES_TXTATR_FTN);
	if( pFtn && pFillFtn )
	{
		// Daten vom Attribut uebertragen
		const SwFmtFtn &rFtn = ((SwTxtFtn*)pFtn)->GetFtn();
		pFillFtn->SetNumber( rFtn );
		pFillFtn->SetEndNote( rFtn.IsEndNote() );
	}
	return 0 != pFtn;
}


bool SwEditShell::SetCurFtn( const SwFmtFtn& rFillFtn )
{
    bool bChgd = false;
	StartAllAction();

	SwPaM* pCrsr = GetCrsr(), *pFirst = pCrsr;
	do {
		bChgd |=  pDoc->SetCurFtn( *pCrsr, rFillFtn.GetNumStr(),
											rFillFtn.GetNumber(),
											rFillFtn.IsEndNote() );

	} while( pFirst != ( pCrsr = (SwPaM*)pCrsr->GetNext() ));

	EndAllAction();
	return bChgd;
}



/*sal_uInt16 SwEditShell::GetFtnCnt( sal_Bool bEndNotes = sal_False ) const
{
	const SwFtnIdxs &rIdxs = pDoc->GetFtnIdxs();
	sal_uInt16 nCnt = 0;
	for ( sal_uInt16 i = 0; i < rIdxs.Count(); ++i )
	{
		const SwFmtFtn &rFtn = rIdxs[i]->GetFtn();
		if ( bEndNotes == rFtn.IsEndNote() )
			nCnt++;
	}
	return nCnt;
} */


bool SwEditShell::HasFtns( bool bEndNotes ) const
{
	const SwFtnIdxs &rIdxs = pDoc->GetFtnIdxs();
	for ( sal_uInt16 i = 0; i < rIdxs.Count(); ++i )
	{
		const SwFmtFtn &rFtn = rIdxs[i]->GetFtn();
		if ( bEndNotes == rFtn.IsEndNote() )
			return sal_True;
	}
	return sal_False;
}


	// gebe Liste aller Fussnoten und deren Anfangstexte
sal_uInt16 SwEditShell::GetSeqFtnList( SwSeqFldList& rList, bool bEndNotes )
{
	if( rList.Count() )
		rList.Remove( 0, rList.Count() );

	sal_uInt16 n, nFtnCnt = pDoc->GetFtnIdxs().Count();
	SwTxtFtn* pTxtFtn;
	for( n = 0; n < nFtnCnt; ++n )
	{
		pTxtFtn = pDoc->GetFtnIdxs()[ n ];
		const SwFmtFtn& rFtn = pTxtFtn->GetFtn();
		if ( rFtn.IsEndNote() != bEndNotes )
			continue;

		SwNodeIndex* pIdx = pTxtFtn->GetStartNode();
		if( pIdx )
		{
			SwNodeIndex aIdx( *pIdx, 1 );
			SwTxtNode* pTxtNd = aIdx.GetNode().GetTxtNode();
			if( !pTxtNd )
				pTxtNd = (SwTxtNode*)pDoc->GetNodes().GoNext( &aIdx );

			if( pTxtNd )
			{
				String sTxt( rFtn.GetViewNumStr( *pDoc ));
				if( sTxt.Len() )
					sTxt += ' ';
                sTxt += pTxtNd->GetExpandTxt( 0, USHRT_MAX );

				_SeqFldLstElem* pNew = new _SeqFldLstElem( sTxt,
											pTxtFtn->GetSeqRefNo() );
				while( rList.InsertSort( pNew ) )
					pNew->sDlgEntry += ' ';
			}
		}
	}

	return rList.Count();
}


// linken Rand ueber Objectleiste einstellen (aenhlich dem Stufen von
// Numerierungen)
sal_Bool SwEditShell::IsMoveLeftMargin( sal_Bool bRight, sal_Bool bModulus ) const
{
	sal_Bool bRet = sal_True;

	const SvxTabStopItem& rTabItem = (SvxTabStopItem&)GetDoc()->
								GetDefault( RES_PARATR_TABSTOP );
	sal_uInt16 nDefDist = static_cast<sal_uInt16>(rTabItem.Count() ? rTabItem[0].GetTabPos() : 1134);
	if( !nDefDist )
		return sal_False;

	FOREACHPAM_START(this)

		sal_uLong nSttNd = PCURCRSR->GetMark()->nNode.GetIndex(),
			  nEndNd = PCURCRSR->GetPoint()->nNode.GetIndex();

		if( nSttNd > nEndNd )
		{
			sal_uLong nTmp = nSttNd; nSttNd = nEndNd; nEndNd = nTmp;
		}

		SwCntntNode* pCNd;
		for( sal_uLong n = nSttNd; bRet && n <= nEndNd; ++n )
			if( 0 != ( pCNd = GetDoc()->GetNodes()[ n ]->GetTxtNode() ))
			{
				const SvxLRSpaceItem& rLS = (SvxLRSpaceItem&)
											pCNd->GetAttr( RES_LR_SPACE );
				if( bRight )
				{
					long nNext = rLS.GetTxtLeft() + nDefDist;
					if( bModulus )
						nNext = ( nNext / nDefDist ) * nDefDist;
					SwFrm* pFrm = pCNd->getLayoutFrm( GetLayout() );
                    if ( pFrm )
                    {
                        const sal_uInt16 nFrmWidth = static_cast<sal_uInt16>( pFrm->IsVertical() ?
                                                 pFrm->Frm().Height() :
                                                 pFrm->Frm().Width() );
                        bRet = nFrmWidth > ( nNext + MM50 );
                    }
                    else
                        bRet = sal_False;
				}
			}

		if( !bRet )
			break;

	FOREACHPAM_END()
	return bRet;
}

void SwEditShell::MoveLeftMargin( sal_Bool bRight, sal_Bool bModulus )
{
	StartAllAction();
	StartUndo( UNDO_START );

	SwPaM* pCrsr = GetCrsr();
	if( pCrsr->GetNext() != pCrsr )			// Mehrfachselektion ?
	{
		SwPamRanges aRangeArr( *pCrsr );
		SwPaM aPam( *pCrsr->GetPoint() );
		for( sal_uInt16 n = 0; n < aRangeArr.Count(); ++n )
			GetDoc()->MoveLeftMargin( aRangeArr.SetPam( n, aPam ),
										bRight, bModulus );
	}
	else
		GetDoc()->MoveLeftMargin( *pCrsr, bRight, bModulus );

	EndUndo( UNDO_END );
	EndAllAction();
}


inline sal_uInt16 lcl_SetScriptFlags( sal_uInt16 nType )
{
	sal_uInt16 nRet;
   	switch( nType )
	{
	case ::com::sun::star::i18n::ScriptType::LATIN:		nRet = SCRIPTTYPE_LATIN;	break;
	case ::com::sun::star::i18n::ScriptType::ASIAN:		nRet = SCRIPTTYPE_ASIAN;	break;
	case ::com::sun::star::i18n::ScriptType::COMPLEX:	nRet = SCRIPTTYPE_COMPLEX;	break;
	default: nRet = 0;
	}
	return nRet;
}

sal_Bool lcl_IsNoEndTxtAttrAtPos( const SwTxtNode& rTNd, xub_StrLen nPos,
                            sal_uInt16 &rScrpt, sal_Bool bInSelection, sal_Bool bNum )
{
	sal_Bool bRet = sal_False;
	const String& rTxt = rTNd.GetTxt();
    String sExp;

    // consider numbering
    if ( bNum )
    {
        bRet = sal_False;

        // --> OD 2008-03-19 #refactorlists#
        if ( rTNd.IsInList() )
        {
            ASSERT( rTNd.GetNumRule(),
                    "<lcl_IsNoEndTxtAttrAtPos(..)> - no list style found at text node. Serious defect -> please inform OD." );
            const SwNumRule* pNumRule = rTNd.GetNumRule();
			//Modified for i119959,2012.6.12
			//Under this scenario,this pointer is null,but on win,it doesn't crash immediately
			//it exits with exception,and associated memory will have problem which leads to crash problem in 
			//other place in dev env...
			if ( pNumRule )
			{
			//End
            const SwNumFmt &rNumFmt = pNumRule->Get( static_cast<sal_uInt16>(rTNd.GetActualListLevel()) );
            if( SVX_NUM_BITMAP != rNumFmt.GetNumberingType() )
            {
                if ( SVX_NUM_CHAR_SPECIAL == rNumFmt.GetNumberingType() )
                    sExp = rNumFmt.GetBulletChar();
                else
                    sExp = rTNd.GetNumString();
				}
			//Modified for i119959,2012.6.12
			//Under this scenario,this pointer is null,but on win,it doesn't crash immediately
			//it exits with exception,and associated memory will have problem which leads to crash problem in 
			//other place in dev env...
			}
			//End
        }
    }

    // and fields
    if ( CH_TXTATR_BREAKWORD == rTxt.GetChar( nPos ) )
    {
        const SwTxtAttr* const pAttr = rTNd.GetTxtAttrForCharAt( nPos );
        if (pAttr)
        {
            bRet = sal_True; // all other than fields can be
                         // defined as weak-script ?
            if ( RES_TXTATR_FIELD == pAttr->Which() )
            {
                const SwField* const pFld = pAttr->GetFld().GetFld();
                if (pFld)
                {
                    sExp += pFld->ExpandField(true);
                }
            }
        }
    }

    xub_StrLen nEnd = sExp.Len();
    if ( nEnd )
    {
        xub_StrLen n;
        if( bInSelection )
        {
            sal_uInt16 nScript;
            for( n = 0; n < nEnd; n = (xub_StrLen)
                    pBreakIt->GetBreakIter()->endOfScript( sExp, n, nScript ))
            {
                nScript = pBreakIt->GetBreakIter()->getScriptType( sExp, n );
                rScrpt |= lcl_SetScriptFlags( nScript );
            }
        }
        else
            rScrpt |= lcl_SetScriptFlags( pBreakIt->GetBreakIter()->
                                        getScriptType( sExp, nEnd-1 ));
    }

	return bRet;
}


// returns the scripttpye of the selection
sal_uInt16 SwEditShell::GetScriptType() const
{
	sal_uInt16 nRet = 0;
	//if( pBreakIt->GetBreakIter().is() )
	{
		FOREACHPAM_START(this)

			const SwPosition *pStt = PCURCRSR->Start(),
							 *pEnd = pStt == PCURCRSR->GetMark()
									? PCURCRSR->GetPoint()
									: PCURCRSR->GetMark();
			if( pStt == pEnd || *pStt == *pEnd )
			{
				const SwTxtNode* pTNd = pStt->nNode.GetNode().GetTxtNode();
				if( pTNd )
				{
                    // try to get SwScriptInfo
                    const SwScriptInfo* pScriptInfo = SwScriptInfo::GetScriptInfo( *pTNd );

					xub_StrLen nPos = pStt->nContent.GetIndex();
					//Task 90448: we need the scripttype of the previous
					//				position, if no selection exist!
					if( nPos )
					{
						SwIndex aIdx( pStt->nContent );
						if( pTNd->GoPrevious( &aIdx, CRSR_SKIP_CHARS ) )
							nPos = aIdx.GetIndex();
					}

                    sal_uInt16 nScript;

                    if ( pTNd->GetTxt().Len() )
                    {
                        nScript = pScriptInfo ?
                                  pScriptInfo->ScriptType( nPos ) :
                                  pBreakIt->GetBreakIter()->getScriptType( pTNd->GetTxt(), nPos );
                    }
                    else
                        nScript = GetI18NScriptTypeOfLanguage( (sal_uInt16)GetAppLanguage() );

                    if( !lcl_IsNoEndTxtAttrAtPos( *pTNd, nPos, nRet, sal_False, sal_False ))
                        nRet |= lcl_SetScriptFlags( nScript );
				}
			}
			else if ( pBreakIt->GetBreakIter().is() )
			{
				sal_uLong nEndIdx = pEnd->nNode.GetIndex();
				SwNodeIndex aIdx( pStt->nNode );
				for( ; aIdx.GetIndex() <= nEndIdx; aIdx++ )
					if( aIdx.GetNode().IsTxtNode() )
					{
						const SwTxtNode* pTNd = aIdx.GetNode().GetTxtNode();
						const String& rTxt = pTNd->GetTxt();

                        // try to get SwScriptInfo
                        const SwScriptInfo* pScriptInfo = SwScriptInfo::GetScriptInfo( *pTNd );

						xub_StrLen nChg = aIdx == pStt->nNode
												? pStt->nContent.GetIndex()
												: 0,
									nEndPos = aIdx == nEndIdx
												? pEnd->nContent.GetIndex()
                                                : rTxt.Len();

						ASSERT( nEndPos <= rTxt.Len(), "Index outside the range - endless loop!" );
						if( nEndPos > rTxt.Len() )
							nEndPos = rTxt.Len();

						sal_uInt16 nScript;
						while( nChg < nEndPos )
						{
                            nScript = pScriptInfo ?
                                      pScriptInfo->ScriptType( nChg ) :
                                      pBreakIt->GetBreakIter()->getScriptType(
																rTxt, nChg );

                            if( !lcl_IsNoEndTxtAttrAtPos( *pTNd, nChg, nRet, sal_True,
                                                          0 == nChg && rTxt.Len() == nEndPos ) )
								nRet |= lcl_SetScriptFlags( nScript );

							if( (SCRIPTTYPE_LATIN | SCRIPTTYPE_ASIAN |
								SCRIPTTYPE_COMPLEX) == nRet )
								break;

							xub_StrLen nFldPos = nChg+1;

                            nChg = pScriptInfo ?
                                   pScriptInfo->NextScriptChg( nChg ) :
                                   (xub_StrLen)pBreakIt->GetBreakIter()->endOfScript(
													rTxt, nChg, nScript );

                            nFldPos = rTxt.Search(
											CH_TXTATR_BREAKWORD, nFldPos );
							if( nFldPos < nChg )
								nChg = nFldPos;
						}
						if( (SCRIPTTYPE_LATIN | SCRIPTTYPE_ASIAN |
								SCRIPTTYPE_COMPLEX) == nRet )
							break;
					}
			}
			if( (SCRIPTTYPE_LATIN | SCRIPTTYPE_ASIAN |
								SCRIPTTYPE_COMPLEX) == nRet )
				break;

		FOREACHPAM_END()
	}
	if( !nRet )
        nRet = SvtLanguageOptions::GetScriptTypeOfLanguage( LANGUAGE_SYSTEM );
	return nRet;
}


sal_uInt16 SwEditShell::GetCurLang() const
{
	const SwPaM* pCrsr = GetCrsr();
	const SwPosition& rPos = *pCrsr->GetPoint();
	const SwTxtNode* pTNd = rPos.nNode.GetNode().GetTxtNode();
	sal_uInt16 nLang;
	if( pTNd )
	{
		//JP 24.9.2001: if exist no selection, then get the language before
		//				the current character!
		xub_StrLen nPos = rPos.nContent.GetIndex();
		if( nPos && !pCrsr->HasMark() )
			--nPos;
		nLang = pTNd->GetLang( nPos );
	}
	else
		nLang = LANGUAGE_DONTKNOW;
	return nLang;
}

sal_uInt16 SwEditShell::GetScalingOfSelectedText() const
{
	const SwPaM* pCrsr = GetCrsr();
	const SwPosition* pStt = pCrsr->Start();
	const SwTxtNode* pTNd = pStt->nNode.GetNode().GetTxtNode();
	ASSERT( pTNd, "no textnode available" );

	sal_uInt16 nScaleWidth;
	if( pTNd )
	{
		xub_StrLen nStt = pStt->nContent.GetIndex(), nEnd;
		const SwPosition* pEnd = pStt == pCrsr->GetPoint()
										? pCrsr->GetMark()
										: pCrsr->GetPoint();
		if( pStt->nNode == pEnd->nNode )
			nEnd = pEnd->nContent.GetIndex();
		else
			nEnd = pTNd->GetTxt().Len();
		nScaleWidth = pTNd->GetScalingOfSelectedText( nStt, nEnd );
	}
	else
		nScaleWidth = 100;		        // default are no scaling -> 100%
	return nScaleWidth;
}