/**************************************************************
 * 
 * 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 "swrect.hxx"
#include "paratr.hxx" 	// pTabStop, ADJ*
#include "viewopt.hxx"	// SwViewOptions
#include "errhdl.hxx" 	// ASSERT
#include <SwPortionHandler.hxx>

#include "txtcfg.hxx"
#include "porglue.hxx"
#include "inftxt.hxx"
#include "porlay.hxx" 	// SwParaPortion, SetFull
#include "porfly.hxx" 	// SwParaPortion, SetFull

/*************************************************************************
 *						class SwGluePortion
 *************************************************************************/

SwGluePortion::SwGluePortion( const KSHORT nInitFixWidth )
	: nFixWidth( nInitFixWidth )
{
	PrtWidth( nFixWidth );
	SetWhichPor( POR_GLUE );
}

/*************************************************************************
 *				  virtual SwGluePortion::GetCrsrOfst()
 *************************************************************************/

xub_StrLen SwGluePortion::GetCrsrOfst( const KSHORT nOfst ) const
{
	if( !GetLen() || nOfst > GetLen() || !Width() )
		return SwLinePortion::GetCrsrOfst( nOfst );
	else
		return nOfst / (Width() / GetLen());
}

/*************************************************************************
 *				  virtual SwGluePortion::GetTxtSize()
 *************************************************************************/

SwPosSize SwGluePortion::GetTxtSize( const SwTxtSizeInfo &rInf ) const
{
	if( 1 >= GetLen() || rInf.GetLen() > GetLen() || !Width() || !GetLen() )
		return SwPosSize(*this);
	else
		return SwPosSize( (Width() / GetLen()) * rInf.GetLen(), Height() );
}

/*************************************************************************
 *				virtual SwGluePortion::GetExpTxt()
 *************************************************************************/

sal_Bool SwGluePortion::GetExpTxt( const SwTxtSizeInfo &rInf, XubString &rTxt ) const
{
	if( GetLen() && rInf.OnWin() &&
		rInf.GetOpt().IsBlank() && rInf.IsNoSymbol() )
	{
		rTxt.Fill( GetLen(), CH_BULLET );
		return sal_True;
	}
	return sal_False;
}

/*************************************************************************
 *				  virtual SwGluePortion::Paint()
 *************************************************************************/

void SwGluePortion::Paint( const SwTxtPaintInfo &rInf ) const
{
	if( !GetLen() )
		return;

	if( rInf.GetFont()->IsPaintBlank() )
	{
		XubString aTxt;
		aTxt.Fill( GetFixWidth() / GetLen(), ' ' );
		SwTxtPaintInfo aInf( rInf, aTxt );
		aInf.DrawText( *this, aTxt.Len(), sal_True );
	}

	if( rInf.OnWin() && rInf.GetOpt().IsBlank() && rInf.IsNoSymbol() )
	{
#ifdef DBG_UTIL
		const xub_Unicode cChar = rInf.GetChar( rInf.GetIdx() );
		ASSERT( CH_BLANK  == cChar || CH_BULLET == cChar,
				"SwGluePortion::Paint: blank expected" );
#endif
		if( 1 == GetLen() )
		{
			String aBullet( CH_BULLET );
			SwPosSize aBulletSize( rInf.GetTxtSize( aBullet ) );
			Point aPos( rInf.GetPos() );
			aPos.X() += (Width()/2) - (aBulletSize.Width()/2);
			SwTxtPaintInfo aInf( rInf, aBullet );
			aInf.SetPos( aPos );
			SwTxtPortion aBulletPor;
			aBulletPor.Width( aBulletSize.Width() );
			aBulletPor.Height( aBulletSize.Height() );
			aBulletPor.SetAscent( GetAscent() );
			aInf.DrawText( aBulletPor, aBullet.Len(), sal_True );
		}
		else
		{
            SwTxtSlot aSlot( &rInf, this, true, false );
			rInf.DrawText( *this, rInf.GetLen(), sal_True );
		}
	}
}

/*************************************************************************
 *						SwGluePortion::MoveGlue()
 *************************************************************************/

void SwGluePortion::MoveGlue( SwGluePortion *pTarget, const short nPrtGlue )
{
	short nPrt = Min( nPrtGlue, GetPrtGlue() );
	if( 0 < nPrt )
	{
		pTarget->AddPrtWidth( nPrt );
		SubPrtWidth( nPrt );
	}
}

/*************************************************************************
 *				  void SwGluePortion::Join()
 *************************************************************************/

void SwGluePortion::Join( SwGluePortion *pVictim )
{
	// Die GluePortion wird ausgesogen und weggespuelt ...
	AddPrtWidth( pVictim->PrtWidth() );
	SetLen( pVictim->GetLen() + GetLen() );
	if( Height() < pVictim->Height() )
		Height( pVictim->Height() );

	AdjFixWidth();
	Cut( pVictim );
	delete pVictim;
}

/*************************************************************************
 *				  class SwFixPortion
 *************************************************************************/

// Wir erwarten ein framelokales SwRect !
SwFixPortion::SwFixPortion( const SwRect &rRect )
	   :SwGluePortion( KSHORT(rRect.Width()) ), nFix( KSHORT(rRect.Left()) )
{
	Height( KSHORT(rRect.Height()) );
	SetWhichPor( POR_FIX );
}

SwFixPortion::SwFixPortion(const KSHORT nFixedWidth, const KSHORT nFixedPos)
       : SwGluePortion(nFixedWidth), nFix(nFixedPos)
{
	SetWhichPor( POR_FIX );
}

/*************************************************************************
 *				  class SwMarginPortion
 *************************************************************************/

SwMarginPortion::SwMarginPortion( const KSHORT nFixedWidth )
    :SwGluePortion( nFixedWidth )
{
	SetWhichPor( POR_MARGIN );
}

/*************************************************************************
 *				  SwMarginPortion::AdjustRight()
 *
 * In der umschliessenden Schleife werden alle Portions durchsucht,
 * dabei werden erst die am Ende liegenden GluePortions verarbeitet.
 * Das Ende wird nach jeder Schleife nach vorne verlegt, bis keine
 * GluePortions mehr vorhanden sind.
 * Es werden immer GluePortion-Paare betrachtet (pLeft und pRight),
 * wobei Textportions zwischen pLeft und pRight hinter pRight verschoben
 * werden, wenn pRight genuegend Glue besitzt. Bei jeder Verschiebung
 * wandert ein Teil des Glues von pRight nach pLeft.
 * Im naechsten Schleifendurchlauf ist pLeft das pRight und das Spiel
 * beginnt von vorne.
 *************************************************************************/

void SwMarginPortion::AdjustRight( const SwLineLayout *pCurr )
{
	SwGluePortion *pRight = 0;
	sal_Bool bNoMove = 0 != pCurr->GetpKanaComp();
	while( pRight != this )
	{

		// 1) Wir suchen den linken Glue
		SwLinePortion *pPos = (SwLinePortion*)this;
		SwGluePortion *pLeft = 0;
		while( pPos )
		{
			DBG_LOOP;
			if( pPos->InFixMargGrp() )
				pLeft = (SwGluePortion*)pPos;
			pPos = pPos->GetPortion();
			if( pPos == pRight)
				pPos = 0;
		}

		// Zwei nebeneinander liegende FlyPortions verschmelzen
		if( pRight && pLeft->GetPortion() == pRight )
		{
			pRight->MoveAllGlue( pLeft );
			pRight = 0;
		}
		KSHORT nRightGlue = pRight && 0 < pRight->GetPrtGlue()
						  ? KSHORT(pRight->GetPrtGlue()) : 0;
		// 2) linken und rechten Glue ausgleichen
		//	  Bei Tabs haengen wir nix um ...
		if( pLeft && nRightGlue && !pRight->InTabGrp() )
		{
			// pPrev ist die Portion, die unmittelbar vor pRight liegt.
			SwLinePortion *pPrev = pRight->FindPrevPortion( pLeft );

			if ( pRight->IsFlyPortion() && pRight->GetLen() )
			{
				SwFlyPortion *pFly = (SwFlyPortion *)pRight;
				if ( pFly->GetBlankWidth() < nRightGlue )
				{
					// Hier entsteht eine neue TxtPortion, die dass zuvor
					// vom Fly verschluckte Blank reaktiviert.
					nRightGlue = nRightGlue - pFly->GetBlankWidth();
					pFly->SubPrtWidth( pFly->GetBlankWidth() );
					pFly->SetLen( 0 );
					SwTxtPortion *pNewPor = new SwTxtPortion;
					pNewPor->SetLen( 1 );
					pNewPor->Height( pFly->Height() );
					pNewPor->Width( pFly->GetBlankWidth() );
					pFly->Insert( pNewPor );
				}
				else
					pPrev = pLeft;
			}
			while( pPrev != pLeft )
			{
				DBG_LOOP;

				if( bNoMove || pPrev->PrtWidth() >= nRightGlue ||
					pPrev->InHyphGrp() || pPrev->IsKernPortion() )
				{
					// Die Portion, die vor pRight liegt kann nicht
					// verschoben werden, weil kein Glue mehr vorhanden ist.
					// Wir fuehren die Abbruchbedingung herbei:
					pPrev = pLeft;
				}
				else
				{
					nRightGlue = nRightGlue - pPrev->PrtWidth();
					// pPrev wird hinter pRight verschoben.
					// Dazu wird der Gluewert zwischen pRight und pLeft
					// ausgeglichen.
					pRight->MoveGlue( pLeft, short( pPrev->PrtWidth() ) );
					// Jetzt wird die Verkettung gerichtet.
					SwLinePortion *pPrevPrev = pPrev->FindPrevPortion( pLeft );
					pPrevPrev->SetPortion( pRight );
					pPrev->SetPortion( pRight->GetPortion() );
					pRight->SetPortion( pPrev );
					if ( pPrev->GetPortion() && pPrev->InTxtGrp()
						 && pPrev->GetPortion()->IsHolePortion() )
					{
						SwHolePortion *pHolePor =
							(SwHolePortion*)pPrev->GetPortion();
						if ( !pHolePor->GetPortion() ||
							 !pHolePor->GetPortion()->InFixMargGrp() )
						{
							pPrev->AddPrtWidth( pHolePor->GetBlankWidth() );
							pPrev->SetLen( pPrev->GetLen() + 1 );
							pPrev->SetPortion( pHolePor->GetPortion() );
							delete pHolePor;
						}
					}
					pPrev = pPrevPrev;
				}
			}
		}
		// Wenn es keinen linken Glue mehr gibt, wird die Abbruchbedingung
		// herbeigefuehrt.
		pRight = pLeft ? pLeft : (SwGluePortion*)this;
	}
}



