/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 * 
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sw.hxx"

#include <hintids.hxx>		    // contains RES_.. IDs
#include <frame.hxx>
#include <hints.hxx>
#include <swcache.hxx>          // mba: get rid of that dependency
#include <swfntcch.hxx>         // mba: get rid of that dependency

static SwClientIter* pClientIters = 0;

TYPEINIT0(SwClient);

/*************************************************************************/
SwClient::SwClient(SwModify *pToRegisterIn)
	: pLeft( 0 ), pRight( 0 ), pRegisteredIn( 0 ), mbIsAllowedToBeRemovedInModifyCall(false)
{
	if(pToRegisterIn)
        // connect to SwModify
		pToRegisterIn->Add(this);
}

/*************************************************************************/
void SwClient::CheckRegistration( const SfxPoolItem* pOld, const SfxPoolItem * )
{
    // this method only handles notification about dying SwModify objects
	if( (!pOld || pOld->Which() != RES_OBJECTDYING) )
		return;

	const SwPtrMsgPoolItem *pDead = static_cast<const SwPtrMsgPoolItem*>(pOld);
	if(pDead && pDead->pObject == pRegisteredIn)
	{
        // I've got a notification from the object I know
		SwModify *pAbove = const_cast<SwModify*>(pRegisteredIn->GetRegisteredIn());
		if(pAbove)
		{
            // if the dying object itself was listening at an SwModify, I take over
            // adding myself to pAbove will automatically remove me from my current pRegisteredIn
			pAbove->Add(this);
			return;
		}

        // destroy connection
		pRegisteredIn->Remove(this);
	}
}

void SwClient::Modify( const SfxPoolItem *pOldValue, const SfxPoolItem *pNewValue )
{
    CheckRegistration( pOldValue, pNewValue );
}

void SwClient::SwClientNotify( const SwModify&, const SfxHint& )
{

}

//*************************************************************************
SwClient::~SwClient()
{
    DBG_ASSERT( !pRegisteredIn || pRegisteredIn->GetDepends(),"SwModify still known, but Client already disconnected!" );
	if( pRegisteredIn && pRegisteredIn->GetDepends() )
        // still connected
		pRegisteredIn->Remove( this );
}


sal_Bool SwClient::GetInfo( SfxPoolItem& ) const
{
	return sal_True;		// und weiter
}


/*************************************************************************/
SwModify::SwModify() 
    : SwClient(0), pRoot(0) 
{
    bModifyLocked = sal_False;
    bLockClientList = sal_False;
	bInDocDTOR = sal_False;
	bInCache = sal_False;
	bInSwFntCache = sal_False;
}

SwModify::SwModify( SwModify *pToRegisterIn )
	: SwClient(pToRegisterIn), pRoot( 0 )
{
    bModifyLocked = sal_False;
    bLockClientList = sal_False;
	bInDocDTOR = sal_False;
	bInCache = sal_False;
	bInSwFntCache = sal_False;
}

/*************************************************************************/
SwModify::~SwModify()
{
	ASSERT( !IsModifyLocked(), "Modify destroyed but locked." );

	if ( IsInCache() )
		SwFrm::GetCache().Delete( this );

	if ( IsInSwFntCache() )
		pSwFontCache->Delete( this );

	if( pRoot )
	{
        // there are depending objects
		if( IsInDocDTOR() )
		{
			// if document gets destroyed anyway, just tell clients to forget me
            // so that they don't try to get removed from my list later when they also get destroyed
			SwClientIter aIter( *this );
			SwClient* p = aIter.GoStart();
			while ( p )
            {
				p->pRegisteredIn = 0;
                p = ++aIter;
            }
		}
		else
		{
			// notify all clients that they shall remove themselves
			SwPtrMsgPoolItem aDyObject( RES_OBJECTDYING, this );
			NotifyClients( &aDyObject, &aDyObject );

            // remove all clients that have not done themselves
            // mba: possibly a hotfix for forgotten base class calls?!
			while( pRoot )
				pRoot->CheckRegistration(&aDyObject, &aDyObject);
		}
	}
}

/*************************************************************************/
void SwModify::Modify( const SfxPoolItem *pOldValue, const SfxPoolItem *pNewValue )
{
    NotifyClients( pOldValue, pNewValue );
}

void SwModify::NotifyClients( const SfxPoolItem *pOldValue, const SfxPoolItem *pNewValue )
{
	if (IsInCache() || IsInSwFntCache())
	{
		const sal_uInt16 nWhich = pOldValue ? pOldValue->Which() :
										pNewValue ? pNewValue->Which() : 0;
		CheckCaching( nWhich );
	}

  	if (!pRoot || IsModifyLocked())
		return;

	LockModify();

    // mba: WTF?!
	if( !pOldValue )
		bLockClientList = sal_True;
	else
    {
		// following Modifies shouldn't call an ASSERT
		switch( pOldValue->Which() )
		{
		case RES_OBJECTDYING:
 		case RES_REMOVE_UNO_OBJECT:
			bLockClientList = ((SwPtrMsgPoolItem *)pOldValue)->pObject != this;
			break;

		case RES_FOOTNOTE_DELETED:
		case RES_REFMARK_DELETED:
		case RES_TOXMARK_DELETED:
		case RES_FIELD_DELETED:
			bLockClientList = sal_False;
			break;
		default:
			bLockClientList = sal_True;
		}
    }

    ModifyBroadcast( pOldValue, pNewValue );
	bLockClientList = sal_False;
	UnlockModify();
}

sal_Bool SwModify::GetInfo( SfxPoolItem& rInfo ) const
{
	sal_Bool bRet = sal_True;		// bedeutet weiter zum naechsten

	if( pRoot )
	{
		SwClientIter aIter( *(SwModify*)this );

		SwClient* pLast = aIter.GoStart();
		if( pLast )
			while( 0 != ( bRet = pLast->GetInfo( rInfo )) &&
					0 != ( pLast = ++aIter ) )
				;
	}

	return bRet;
}

/*************************************************************************/
void SwModify::Add(SwClient *pDepend)
{
	ASSERT( !bLockClientList, "Client inserted while in Modify" );

	if(pDepend->pRegisteredIn != this )
	{
#ifdef DBG_UTIL
		SwClientIter* pTmp = pClientIters;
		while( pTmp )
		{
			ASSERT( &pTmp->GetModify() != pRoot, "Client added to active ClientIter" );
			pTmp = pTmp->pNxtIter;
		}
#endif
		// deregister new client in case it is already registered elsewhere
		if( pDepend->pRegisteredIn != 0 )
			pDepend->pRegisteredIn->Remove( pDepend );

		if( !pRoot )
		{
            // first client added
			pRoot = pDepend;
			pRoot->pLeft = 0;
			pRoot->pRight = 0;
		}
		else
		{
			// append client
			pDepend->pRight = pRoot->pRight;
			pRoot->pRight = pDepend;
			pDepend->pLeft = pRoot;
			if( pDepend->pRight )
				pDepend->pRight->pLeft = pDepend;
		}

        // connect client to me
		pDepend->pRegisteredIn = this;
	}
}

/*************************************************************************/

SwClient* SwModify::Remove(SwClient * pDepend)
{
	if ( bInDocDTOR )
        return 0;

    ASSERT( !bLockClientList || pDepend->mbIsAllowedToBeRemovedInModifyCall, "SwClient shall be removed in Modify call!" );

	if( pDepend->pRegisteredIn == this )
	{
        // SwClient is my listener
        // remove it from my list
		SwClient* pR = pDepend->pRight;
		SwClient* pL = pDepend->pLeft;
		if( pRoot == pDepend )
			pRoot = pL ? pL : pR;

		if( pL )
			pL->pRight = pR;
		if( pR )
			pR->pLeft = pL;

		// update ClientIters 
		SwClientIter* pTmp = pClientIters;
		while( pTmp )
		{
			if( pTmp->pAct == pDepend || pTmp->pDelNext == pDepend )
                // if object being removed is the current or next object in an iterator, advance this iterator
				pTmp->pDelNext = pR;
			pTmp = pTmp->pNxtIter;
		}

		pDepend->pLeft = 0;
		pDepend->pRight = 0;
	}
	else 
    {
		ASSERT( false, "SwModify::Remove(): pDepend nicht gefunden" );
    }

    // disconnect client from me
	pDepend->pRegisteredIn = 0;
	return pDepend;
}

int SwModify::GetClientCount() const
{
    int nRet=0;
    SwClientIter aIter( *this );
    SwClient *pLast = aIter.GoStart();
    if( pLast )
        do
        {
            ++nRet;
        } while( 0 != ( pLast = ++aIter ));
    return nRet;
}

void SwModify::CheckCaching( const sal_uInt16 nWhich )
{
    if (isCHRATR(nWhich))
    {
        SetInSwFntCache( sal_False );
    }
	else
		switch ( nWhich )
		{
		case RES_OBJECTDYING:
		case RES_FMT_CHG:
		case RES_ATTRSET_CHG:
			SetInSwFntCache( sal_False );

		case RES_UL_SPACE:
		case RES_LR_SPACE:
		case RES_BOX:
		case RES_SHADOW:
		case RES_FRM_SIZE:
		case RES_KEEP:
		case RES_BREAK:
			if ( IsInCache() )
			{
				SwFrm::GetCache().Delete( this );
				SetInCache( sal_False );
			}
			break;
		}
}

void SwModify::CallSwClientNotify( const SfxHint& rHint ) const
{
    SwClientIter aIter(*this);
    SwClient * pClient = aIter.GoStart();
    while (pClient)
    {
        pClient->SwClientNotify( *this, rHint );
        pClient = ++aIter;
    }
}

void SwModify::ModifyBroadcast( const SfxPoolItem *pOldValue, const SfxPoolItem *pNewValue, TypeId nType )
{
    SwClientIter aIter(*this);
    SwClient * pClient = aIter.First( nType );
    while (pClient)
    {
        pClient->Modify( pOldValue, pNewValue );
        pClient = aIter.Next();
    }
}

// ----------
// SwDepend
// ----------

/*************************************************************************/

SwDepend::SwDepend(SwClient *pTellHim, SwModify *pDepend)
	: SwClient(pDepend)
{
	pToTell  = pTellHim;
}

/*************************************************************************/

void SwDepend::Modify( const SfxPoolItem* pOldValue, const SfxPoolItem *pNewValue )
{
	if(pNewValue && pNewValue->Which() == RES_OBJECTDYING)
		CheckRegistration(pOldValue,pNewValue);
	else if(pToTell)
		pToTell->ModifyNotification(pOldValue, pNewValue);
}

void SwDepend::SwClientNotify( const SwModify& rMod, const SfxHint& rHint )
{
    if ( pToTell )
        pToTell->SwClientNotifyCall( rMod, rHint );
}

sal_Bool SwDepend::GetInfo( SfxPoolItem& rInfo ) const
{
	return pToTell ? pToTell->GetInfo( rInfo ) : sal_True;
}

/********************************************************************/

SwClientIter::SwClientIter( const SwModify& rModify )
	: rRoot( rModify )
{
	pNxtIter = 0;
	if( pClientIters )
	{
        // append to list of ClientIters
		SwClientIter* pTmp = pClientIters;
		while( pTmp->pNxtIter )
			pTmp = pTmp->pNxtIter;
		pTmp->pNxtIter = this;
	}
	else
		pClientIters = this;

	pAct = const_cast<SwClient*>(rRoot.GetDepends());
	pDelNext = pAct;
}



SwClientIter::~SwClientIter()
{
	if( pClientIters )
	{
        // reorganize list of ClientIters
		if( pClientIters == this )
			pClientIters = pNxtIter;
		else
		{
			SwClientIter* pTmp = pClientIters;
			while( pTmp->pNxtIter != this )
				if( 0 == ( pTmp = pTmp->pNxtIter ) )
				{
					ASSERT( this, "wo ist mein Pointer" );
					return ;
				}
			pTmp->pNxtIter = pNxtIter;
		}
	}
}


SwClient* SwClientIter::operator++()
{
	if( pDelNext == pAct )
	{
		pAct = pAct->pRight;
		pDelNext = pAct;
	}
	else
		pAct = pDelNext;
	return pAct;
}

SwClient* SwClientIter::GoStart()
{
	pAct = const_cast<SwClient*>(rRoot.GetDepends());
	if( pAct )
		while( pAct->pLeft )
			pAct = pAct->pLeft;
	pDelNext = pAct;
	return pAct;
}

SwClient* SwClientIter::GoEnd()	
{
	pAct = pDelNext;
	if( !pAct )
    	pAct = const_cast<SwClient*>(rRoot.GetDepends());
	if( pAct )
		while( pAct->pRight )
			pAct = pAct->pRight;
	pDelNext = pAct;
	return pAct;
}

SwClient* SwClientIter::First( TypeId nType )
{
	aSrchId = nType;
	GoStart();
	if( pAct )
		do {
			if( pAct->IsA( aSrchId ) )
				break;

			if( pDelNext == pAct )
			{
				pAct = pAct->pRight;
				pDelNext = pAct;
			}
			else
				pAct = pDelNext;

		} while( pAct );
	return pAct;
}

SwClient* SwClientIter::Next()
{
	do {
		if( pDelNext == pAct )
		{
			pAct = pAct->pRight;
			pDelNext = pAct;
		}
		else
			pAct = pDelNext;

		if( pAct && pAct->IsA( aSrchId ) )
			break;
	} while( pAct );
	return pAct;
}

SwClient* SwClientIter::Last( TypeId nType )
{
	aSrchId = nType;
	GoEnd();
	if( pAct )
		do {
			if( pAct->IsA( aSrchId ) )
				break;

	        if( pDelNext == pAct )
		        pAct = pAct->pLeft;
	        else
		        pAct = pDelNext->pLeft;
	        pDelNext = pAct;

		} while( pAct );
	return pAct;
}

SwClient* SwClientIter::Previous()
{
	do {
	    if( pDelNext == pAct )
		    pAct = pAct->pLeft;
	    else
		    pAct = pDelNext->pLeft;
	    pDelNext = pAct;

		if( pAct && pAct->IsA( aSrchId ) )
			break;
	} while( pAct );
	return pAct;
}

