/**************************************************************
 * 
 * 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 <pam.hxx>          // GetSpaces
#include <txtcfg.hxx>
#include <frminf.hxx>		// SwTxtFrminfo
#include <itrtxt.hxx>       // SwTxtMargin

/*************************************************************************
 *					 SwTxtMargin::GetTxtStart()
 *************************************************************************/

xub_StrLen SwTxtMargin::GetTxtStart() const
{
	const XubString &rTxt = GetInfo().GetTxt();
    const xub_StrLen nTmpPos = nStart;
    const xub_StrLen nEnd = nTmpPos + pCurr->GetLen();
	xub_StrLen i;

    for( i = nTmpPos; i < nEnd; ++i )
	{
		const xub_Unicode aChar = rTxt.GetChar( i );
		if( CH_TAB != aChar && ' ' != aChar )
			return i;
	}
	return i;
}

/*************************************************************************
 *					 SwTxtMargin::GetTxtEnd()
 *************************************************************************/

xub_StrLen SwTxtMargin::GetTxtEnd() const
{
	const XubString &rTxt = GetInfo().GetTxt();
    const xub_StrLen nTmpPos = nStart;
    const xub_StrLen nEnd = nTmpPos + pCurr->GetLen();
	long i;
    for( i = nEnd - 1; i >= nTmpPos; --i )
	{
        xub_Unicode aChar = rTxt.GetChar( static_cast<xub_StrLen>(i) );
		if( CH_TAB != aChar && CH_BREAK != aChar && ' ' != aChar )
            return static_cast<xub_StrLen>(i + 1);
	}
    return static_cast<xub_StrLen>(i + 1);
}

/*************************************************************************
 *					 SwTxtFrmInfo::IsOneLine()
 *************************************************************************/

// Passt der Absatz in eine Zeile?
sal_Bool SwTxtFrmInfo::IsOneLine() const
{
	const SwLineLayout *pLay = pFrm->GetPara();
	if( !pLay )
		return sal_False;
	else
	{
		// 6575: bei Follows natuerlich sal_False
		if( pFrm->GetFollow() )
			return sal_False;
		pLay = pLay->GetNext();
		while( pLay )
		{
			if( pLay->GetLen() )
				return sal_False;
			pLay = pLay->GetNext();
		}
		return sal_True;
	}
}

/*************************************************************************
 *					 SwTxtFrmInfo::IsFilled()
 *************************************************************************/

// Ist die Zeile zu X% gefuellt?
sal_Bool SwTxtFrmInfo::IsFilled( const sal_uInt8 nPercent ) const
{
	const SwLineLayout *pLay = pFrm->GetPara();
	if( !pLay )
		return sal_False;
	else
	{
		long nWidth = pFrm->Prt().Width();
		nWidth *= nPercent;
		nWidth /= 100;
		return KSHORT(nWidth) <= pLay->Width();
	}
}

/*************************************************************************
 *					 SwTxtFrmInfo::GetLineStart()
 *************************************************************************/

// Wo beginnt der Text (ohne whitespaces)? ( Dokument global )
SwTwips SwTxtFrmInfo::GetLineStart( const SwTxtCursor &rLine ) const
{
    xub_StrLen nTxtStart = rLine.GetTxtStart();
	SwTwips nStart;
	if( rLine.GetStart() == nTxtStart )
		nStart = rLine.GetLineStart();
	else
	{
		SwRect aRect;
		if( ((SwTxtCursor&)rLine).GetCharRect( &aRect, nTxtStart ) )
			nStart = aRect.Left();
		else
			nStart = rLine.GetLineStart();
	}
	return nStart;
}


/*************************************************************************
 *					 SwTxtFrmInfo::GetLineStart()
 *************************************************************************/

// Wo beginnt der Text (ohne whitespaces)? (rel. im Frame)
SwTwips SwTxtFrmInfo::GetLineStart() const
{
	SwTxtSizeInfo aInf( (SwTxtFrm*)pFrm );
	SwTxtCursor aLine( (SwTxtFrm*)pFrm, &aInf );
	return GetLineStart( aLine ) - pFrm->Frm().Left() -	pFrm->Prt().Left();
}

// errechne die Position des Zeichens und gebe die Mittelposition zurueck
SwTwips SwTxtFrmInfo::GetCharPos( xub_StrLen nChar, sal_Bool bCenter ) const
{
    SWRECTFN( pFrm )
    SwFrmSwapper aSwapper( pFrm, sal_True );

    SwTxtSizeInfo aInf( (SwTxtFrm*)pFrm );
	SwTxtCursor aLine( (SwTxtFrm*)pFrm, &aInf );

	SwTwips nStt, nNext;
	SwRect aRect;
	if( ((SwTxtCursor&)aLine).GetCharRect( &aRect, nChar ) )
    {
        if ( bVert )
            pFrm->SwitchHorizontalToVertical( aRect );

        nStt = (aRect.*fnRect->fnGetLeft)();
    }
	else
		nStt = aLine.GetLineStart();

	if( !bCenter )
        return nStt - (pFrm->Frm().*fnRect->fnGetLeft)();

	if( ((SwTxtCursor&)aLine).GetCharRect( &aRect, nChar+1 ) )
    {
        if ( bVert )
            pFrm->SwitchHorizontalToVertical( aRect );

        nNext = (aRect.*fnRect->fnGetLeft)();
    }
	else
		nNext = aLine.GetLineStart();

    return (( nNext + nStt ) / 2 ) - (pFrm->Frm().*fnRect->fnGetLeft)();
}

/*************************************************************************
 *					 SwTxtFrmInfo::GetSpaces()
 *************************************************************************/

SwPaM *AddPam( SwPaM *pPam, const SwTxtFrm* pTxtFrm,
				const xub_StrLen nPos, const xub_StrLen nLen )
{
	if( nLen )
	{
		// Es koennte auch der erste sein.
		if( pPam->HasMark() )
		{
			// liegt die neue Position genau hinter der aktuellen, dann
			// erweiter den Pam einfach
			if( nPos == pPam->GetPoint()->nContent.GetIndex() )
			{
				pPam->GetPoint()->nContent += nLen;
				return pPam;
			}
			pPam = new SwPaM( *pPam );
		}

		SwIndex &rContent = pPam->GetPoint()->nContent;
		rContent.Assign( (SwTxtNode*)pTxtFrm->GetTxtNode(), nPos );
		pPam->SetMark();
		rContent += nLen;
	}
	return pPam;
}

// Sammelt die whitespaces am Zeilenbeginn und -ende im Pam
void SwTxtFrmInfo::GetSpaces( SwPaM &rPam, sal_Bool bWithLineBreak ) const
{
	SwTxtSizeInfo aInf( (SwTxtFrm*)pFrm );
	SwTxtMargin aLine( (SwTxtFrm*)pFrm, &aInf );
	SwPaM *pPam = &rPam;
	sal_Bool bFirstLine = sal_True;
	do {

		if( aLine.GetCurr()->GetLen() )
		{
			xub_StrLen nPos = aLine.GetTxtStart();
			// Bug 49649: von der ersten Line die Blanks/Tabs NICHT
			//				mit selektieren
			if( !bFirstLine && nPos > aLine.GetStart() )
				pPam = AddPam( pPam, pFrm, aLine.GetStart(),
								nPos - aLine.GetStart() );

			// Bug 49649: von der letzten Line die Blanks/Tabs NICHT
			//				mit selektieren
			if( aLine.GetNext() )
			{
				nPos = aLine.GetTxtEnd();

				if( nPos < aLine.GetEnd() )
				{
					MSHORT nOff = !bWithLineBreak && CH_BREAK ==
								aLine.GetInfo().GetChar( aLine.GetEnd() - 1 )
								? 1 : 0;
					pPam = AddPam( pPam, pFrm, nPos, aLine.GetEnd() - nPos - nOff );
				}
			}
		}
		bFirstLine = sal_False;
	}
	while( aLine.Next() );
}

/*************************************************************************
 *					 SwTxtFrmInfo::IsBullet()
 *************************************************************************/

// Ist an der Textposition ein Bullet/Symbol etc?
// Fonts: CharSet, SYMBOL und DONTKNOW
sal_Bool SwTxtFrmInfo::IsBullet( xub_StrLen nTxtStart ) const
{
	SwTxtSizeInfo aInf( (SwTxtFrm*)pFrm );
	SwTxtMargin aLine( (SwTxtFrm*)pFrm, &aInf );
	aInf.SetIdx( nTxtStart );
	return aLine.IsSymbol( nTxtStart );
}

/*************************************************************************
 *					 SwTxtFrmInfo::GetFirstIndent()
 *************************************************************************/

// Ermittelt Erstzeileneinzug
// Voraussetzung fuer pos. oder neg. EZE ist, dass alle
// Zeilen ausser der ersten Zeile den selben linken Rand haben.
// Wir wollen nicht so knauserig sein und arbeiten mit einer Toleranz
// von TOLERANCE Twips.

#define TOLERANCE 20

SwTwips SwTxtFrmInfo::GetFirstIndent() const
{
	SwTxtSizeInfo aInf( (SwTxtFrm*)pFrm );
	SwTxtCursor aLine( (SwTxtFrm*)pFrm, &aInf );
	const SwTwips nFirst = GetLineStart( aLine );
	if( !aLine.Next() )
		return 0;

	SwTwips nLeft = GetLineStart( aLine );
	while( aLine.Next() )
	{
		if( aLine.GetCurr()->GetLen() )
		{
			const SwTwips nCurrLeft = GetLineStart( aLine );
			if( nLeft + TOLERANCE < nCurrLeft ||
				nLeft - TOLERANCE > nCurrLeft )
				return 0;
		}
	}

	// Vorerst wird nur +1, -1 und 0 returnt.
	if( nLeft == nFirst )
		return 0;
	else
		if( nLeft > nFirst )
			return -1;
		else
			return +1;
}

/*************************************************************************
 *					 SwTxtFrmInfo::GetBigIndent()
 *************************************************************************/

KSHORT SwTxtFrmInfo::GetBigIndent( xub_StrLen& rFndPos,
									const SwTxtFrm *pNextFrm ) const
{
	SwTxtSizeInfo aInf( (SwTxtFrm*)pFrm );
	SwTxtCursor aLine( (SwTxtFrm*)pFrm, &aInf );
	SwTwips nNextIndent = 0;

	if( pNextFrm )
	{
		// ich bin einzeilig
		SwTxtSizeInfo aNxtInf( (SwTxtFrm*)pNextFrm );
		SwTxtCursor aNxtLine( (SwTxtFrm*)pNextFrm, &aNxtInf );
		nNextIndent = GetLineStart( aNxtLine );
	}
	else
	{
		// ich bin mehrzeilig
		if( aLine.Next() )
		{
			nNextIndent = GetLineStart( aLine );
			aLine.Prev();
		}
	}

	if( nNextIndent <= GetLineStart( aLine ) )
		return 0;

	const Point aPoint( nNextIndent, aLine.Y() );
	rFndPos = aLine.GetCrsrOfst( 0, aPoint, sal_False );
	if( 1 >= rFndPos )
		return 0;

	// steht vor einem "nicht Space"
	const XubString& rTxt = aInf.GetTxt();
	xub_Unicode aChar = rTxt.GetChar( rFndPos );
	if( CH_TAB == aChar || CH_BREAK == aChar || ' ' == aChar ||
		(( CH_TXTATR_BREAKWORD == aChar || CH_TXTATR_INWORD == aChar ) &&
			aInf.HasHint( rFndPos ) ) )
		return 0;

	// und hinter einem "Space"
	aChar = rTxt.GetChar( rFndPos - 1 );
	if( CH_TAB != aChar && CH_BREAK != aChar &&
		( ( CH_TXTATR_BREAKWORD != aChar && CH_TXTATR_INWORD != aChar ) ||
			!aInf.HasHint( rFndPos - 1 ) ) &&
		// mehr als 2 Blanks !!
		( ' ' != aChar || ' ' != rTxt.GetChar( rFndPos - 2 ) ) )
		return 0;

	SwRect aRect;
	return aLine.GetCharRect( &aRect, rFndPos )
			? KSHORT( aRect.Left() - pFrm->Frm().Left() - pFrm->Prt().Left())
			: 0;
}



