/*************************************************************************
 *
 * 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_chart2.hxx"
#include "LifeTime.hxx"
#include "macros.hxx"
#include <osl/diagnose.h>

#include <com/sun/star/util/XModifyListener.hpp>
#include <com/sun/star/util/XCloseListener.hpp>

using namespace ::com::sun::star;

namespace apphelper
{
//--------------------------

LifeTimeManager::LifeTimeManager( lang::XComponent* pComponent, sal_Bool bLongLastingCallsCancelable )
	: m_aListenerContainer( m_aAccessMutex )
	, m_pComponent(pComponent)
	, m_bLongLastingCallsCancelable(bLongLastingCallsCancelable)
{
	impl_init();
}

void LifeTimeManager::impl_init()
{
	m_bDisposed = sal_False;
	m_bInDispose = sal_False;
	m_nAccessCount = 0;
	m_nLongLastingCallCount = 0;
	m_aNoAccessCountCondition.set();
	m_aNoLongLastingCallCountCondition.set();
}

LifeTimeManager::~LifeTimeManager()
{
}

bool LifeTimeManager::impl_isDisposed( bool bAssert )
{
	if( m_bDisposed || m_bInDispose )
	{
        if( bAssert )
        {
            OSL_ENSURE( sal_False, "This component is already disposed " );
            (void)(bAssert);
        }
		return sal_True;
	}
	return sal_False;
}
			sal_Bool LifeTimeManager
::impl_canStartApiCall()
{
	if( impl_isDisposed() )
		return sal_False; //behave passive if already disposed

	//mutex is acquired
	return sal_True;
}

	void LifeTimeManager
::impl_registerApiCall(sal_Bool bLongLastingCall)
{
	//only allowed if not disposed
	//do not acquire the mutex here because it will be acquired already
	m_nAccessCount++;
	if(m_nAccessCount==1)
		//@todo? is it ok to wake some threads here while we have acquired the mutex?
		m_aNoAccessCountCondition.reset();

	if(bLongLastingCall)
		m_nLongLastingCallCount++;
	if(m_nLongLastingCallCount==1)
		m_aNoLongLastingCallCountCondition.reset();
}
	void LifeTimeManager
::impl_unregisterApiCall(sal_Bool bLongLastingCall)
{
	//Mutex needs to be acquired exactly ones
	//mutex may be released inbetween in special case of impl_apiCallCountReachedNull()

	OSL_ENSURE( m_nAccessCount>0, "access count mismatch" );
	m_nAccessCount--;
	if(bLongLastingCall)
		m_nLongLastingCallCount--;
	if( m_nLongLastingCallCount==0 )
	{
		m_aNoLongLastingCallCountCondition.set();
	}
	if( m_nAccessCount== 0)
	{
		m_aNoAccessCountCondition.set();
		impl_apiCallCountReachedNull();

	}
}

		sal_Bool LifeTimeManager
::dispose() throw(uno::RuntimeException)
{
	//hold no mutex
	{
		osl::Guard< osl::Mutex > aGuard( m_aAccessMutex );

		if( m_bDisposed || m_bInDispose )
		{
			OSL_TRACE( "This component is already disposed " );
			return sal_False; //behave passive if already disposed
		}

		m_bInDispose = true;
		//adding any listener is not allowed anymore
		//new calls will not be accepted
		//still running calls have the freedom to finish their work without crash
	}
	//no mutex is acquired

	//--do the disposing of listeners after calling this method
	{
		uno::Reference< lang::XComponent > xComponent =
			uno::Reference< lang::XComponent >(m_pComponent);;
		if(xComponent.is())
		{
            // notify XCLoseListeners
            lang::EventObject aEvent( xComponent );
            m_aListenerContainer.disposeAndClear( aEvent );
		}
	}

	//no mutex is acquired
	{
		osl::ClearableGuard< osl::Mutex > aGuard( m_aAccessMutex );
		OSL_ENSURE( !m_bDisposed, "dispose was called already" );
		m_bDisposed = sal_True;
		aGuard.clear();
	}
	//no mutex is acquired

	//wait until all still running calls have finished
	//the accessCount cannot grow anymore, because all calls will return after checking m_bDisposed
	m_aNoAccessCountCondition.wait();

	//we are the only ones working on our data now

	return sal_True;
	//--release all resources and references after calling this method successful
}

//-----------------------------------------------------------------

CloseableLifeTimeManager::CloseableLifeTimeManager( ::com::sun::star::util::XCloseable* pCloseable
		, ::com::sun::star::lang::XComponent* pComponent
		, sal_Bool bLongLastingCallsCancelable )
		: LifeTimeManager( pComponent, bLongLastingCallsCancelable )
		, m_pCloseable(pCloseable)
{
	impl_init();
}

CloseableLifeTimeManager::~CloseableLifeTimeManager()
{
}

bool CloseableLifeTimeManager::impl_isDisposedOrClosed( bool bAssert )
{
	if( impl_isDisposed( bAssert ) )
		return sal_True;

	if( m_bClosed )
	{
        if( bAssert )
        {
            OSL_ENSURE( sal_False, "This object is already closed" );
            (void)(bAssert);//avoid warnings
        }
		return sal_True;
	}
	return sal_False;
}

		sal_Bool CloseableLifeTimeManager
::g_close_startTryClose(sal_Bool bDeliverOwnership)
	throw ( uno::Exception )
{
	//no mutex is allowed to be acquired
	{
		osl::ResettableGuard< osl::Mutex > aGuard( m_aAccessMutex );
        if( impl_isDisposedOrClosed(false) )
            return sal_False;

		//Mutex needs to be acquired exactly ones; will be released inbetween
		if( !impl_canStartApiCall() )
			return sal_False;
		//mutex is acquired

		//not closed already -> we try to close again
		m_bInTryClose = sal_True;
		m_aEndTryClosingCondition.reset();

		impl_registerApiCall(sal_False);
	}

	//------------------------------------------------
	//no mutex is acquired

	//only remove listener calls will be worked on until end of tryclose
	//all other new calls will wait till end of try close // @todo? is that really ok

	//?? still running calls have the freedom to finish their work without crash

	try
	{
		uno::Reference< util::XCloseable > xCloseable =
			uno::Reference< util::XCloseable >(m_pCloseable);;
		if(xCloseable.is())
		{
			//--call queryClosing on all registered close listeners
			::cppu::OInterfaceContainerHelper* pIC = m_aListenerContainer.getContainer(
						::getCppuType((const uno::Reference< util::XCloseListener >*)0) );;
			if( pIC )
			{
				//lang::EventObject aEvent( static_cast< util::XCloseable*>(xCloseable) );
				lang::EventObject aEvent( xCloseable );
				::cppu::OInterfaceIteratorHelper aIt( *pIC );
				while( aIt.hasMoreElements() )
                {
                    uno::Reference< util::XCloseListener > xCloseListener( aIt.next(), uno::UNO_QUERY );
                    if(xCloseListener.is())
					    xCloseListener->queryClosing( aEvent, bDeliverOwnership );
                }
			}
		}
	}
	catch( uno::Exception& ex )
	{
		//no mutex is acquired
		g_close_endTryClose(bDeliverOwnership, sal_False);
        (void)(ex);
        throw;
	}
	return sal_True;
}

	void CloseableLifeTimeManager
::g_close_endTryClose(sal_Bool bDeliverOwnership, sal_Bool /* bMyVeto */ )
{
	//this method is called, if the try to close was not successfull
	osl::Guard< osl::Mutex > aGuard( m_aAccessMutex );
	impl_setOwnership( bDeliverOwnership, sal_False );

	m_bInTryClose = sal_False;
	m_aEndTryClosingCondition.set();

	//Mutex needs to be acquired exactly ones
	//mutex may be released inbetween in special case of impl_apiCallCountReachedNull()
	impl_unregisterApiCall(sal_False);
}

	sal_Bool CloseableLifeTimeManager
::g_close_isNeedToCancelLongLastingCalls( sal_Bool bDeliverOwnership, util::CloseVetoException& ex )
	throw ( util::CloseVetoException )
{
	//this method is called when no closelistener has had a veto during queryclosing
	//the method returns false, if nothing stands against closing anymore
	//it returns true, if some longlasting calls are running, which might be cancelled
	//it throws the given exception, if long calls are running but not cancelable

	osl::Guard< osl::Mutex > aGuard( m_aAccessMutex );
	//this count cannot grow after try of close has started, because we wait in all those methods for end of try closing
	if( !m_nLongLastingCallCount )
		return sal_False;

  	if(m_bLongLastingCallsCancelable)
		return sal_True;

	impl_setOwnership( bDeliverOwnership, sal_True );

	m_bInTryClose = sal_False;
	m_aEndTryClosingCondition.set();

	//Mutex needs to be acquired exactly ones
	//mutex may be released inbetween in special case of impl_apiCallCountReachedNull()
	impl_unregisterApiCall(sal_False);

	throw ex;
}

	void CloseableLifeTimeManager
::g_close_endTryClose_doClose()
{
	//this method is called, if the try to close was successfull
	osl::ResettableGuard< osl::Mutex > aGuard( m_aAccessMutex );

	m_bInTryClose		= sal_False;
	m_aEndTryClosingCondition.set();

	//Mutex needs to be acquired exactly ones
	//mutex may be released inbetween in special case of impl_apiCallCountReachedNull()
	impl_unregisterApiCall(sal_False);
	impl_doClose();
}

	void CloseableLifeTimeManager
::impl_setOwnership( sal_Bool bDeliverOwnership, sal_Bool bMyVeto )
{
	m_bOwnership			= bDeliverOwnership && bMyVeto;
	m_bOwnershipIsWellKnown	= sal_True;
}
	sal_Bool CloseableLifeTimeManager
::impl_shouldCloseAtNextChance()
{
	return m_bOwnership;
}

	void CloseableLifeTimeManager
::impl_apiCallCountReachedNull()
{
	//Mutex needs to be acquired exactly ones
	//mutex will be released inbetween in impl_doClose()
	if( m_pCloseable && impl_shouldCloseAtNextChance() )
		impl_doClose();
}

	void CloseableLifeTimeManager
::impl_doClose()
{
	//Mutex needs to be acquired exactly ones before calling impl_doClose()

	if(m_bClosed)
		return; //behave as passive as possible, if disposed or closed already
	if( m_bDisposed || m_bInDispose )
		return; //behave as passive as possible, if disposed or closed already

	//--------
	m_bClosed = sal_True;

	NegativeGuard< osl::Mutex > aNegativeGuard( m_aAccessMutex );
	//mutex is not acquired, mutex will be reacquired at the end of this method automatically

	uno::Reference< util::XCloseable > xCloseable=NULL;
	try
	{
		xCloseable = uno::Reference< util::XCloseable >(m_pCloseable);;
		if(xCloseable.is())
		{
			//--call notifyClosing on all registered close listeners
			::cppu::OInterfaceContainerHelper* pIC = m_aListenerContainer.getContainer(
						::getCppuType((const uno::Reference< util::XCloseListener >*)0) );;
			if( pIC )
			{
				//lang::EventObject aEvent( static_cast< util::XCloseable*>(xCloseable) );
				lang::EventObject aEvent( xCloseable );
				::cppu::OInterfaceIteratorHelper aIt( *pIC );
				while( aIt.hasMoreElements() )
                {
                    uno::Reference< util::XCloseListener > xListener( aIt.next(), uno::UNO_QUERY );
                    if( xListener.is() )
                        xListener->notifyClosing( aEvent );
                }
			}
		}
	}
	catch( uno::Exception& ex )
	{
		ASSERT_EXCEPTION( ex );
	}

	if(xCloseable.is())
	{
		uno::Reference< lang::XComponent > xComponent =
			uno::Reference< lang::XComponent >( xCloseable, uno::UNO_QUERY );
		if(xComponent.is())
		{
			OSL_ENSURE( m_bClosed, "a not closed component will be disposed " );
			xComponent->dispose();
		}
	}
	//mutex will be reacquired in destructor of aNegativeGuard
}

	sal_Bool CloseableLifeTimeManager
::g_addCloseListener( const uno::Reference<	util::XCloseListener > & xListener )
	throw(uno::RuntimeException)
{
	osl::Guard< osl::Mutex > aGuard( m_aAccessMutex );
	//Mutex needs to be acquired exactly ones; will be released inbetween
	if( !impl_canStartApiCall() )
		return sal_False;
	//mutex is acquired

	m_aListenerContainer.addInterface( ::getCppuType((const uno::Reference< util::XCloseListener >*)0),xListener );
	m_bOwnership = sal_False;
	return sal_True;
}

	sal_Bool CloseableLifeTimeManager
::impl_canStartApiCall()
{
	//Mutex needs to be acquired exactly ones before calling this method
	//the mutex will be released inbetween and reacquired

	if( impl_isDisposed() )
		return sal_False; //behave passive if already disposed
	if( m_bClosed )
		return sal_False; //behave passive if closing is already done

	//during try-close most calls need to wait for the decision
	while( m_bInTryClose )
	{
		//if someone tries to close this object at the moment
		//we need to wait for his end because the result of the preceding call
		//is relevant for our behaviour here

		m_aAccessMutex.release();
		m_aEndTryClosingCondition.wait(); //@todo??? this may block??? try closing
		m_aAccessMutex.acquire();
		if( m_bDisposed || m_bInDispose || m_bClosed )
			return sal_False; //return if closed already
	}
	//mutex is acquired
	return sal_True;
}

//--------------------------

	sal_Bool LifeTimeGuard
::startApiCall(sal_Bool bLongLastingCall)
{
	//Mutex needs to be acquired exactly ones; will be released inbetween
	//mutex is requiered due to constructor of LifeTimeGuard

	OSL_ENSURE( !m_bCallRegistered, "this method is only allowed ones" );
	if(m_bCallRegistered)
		return sal_False;

	//Mutex needs to be acquired exactly ones; will be released inbetween
	if( !m_rManager.impl_canStartApiCall() )
		return sal_False;
	//mutex is acquired

	m_bCallRegistered = sal_True;
	m_bLongLastingCallRegistered = bLongLastingCall;
	m_rManager.impl_registerApiCall(bLongLastingCall);
	return sal_True;
}

LifeTimeGuard::~LifeTimeGuard()
{
	try
	{
		//do acquire the mutex if it was cleared before
        osl::MutexGuard g(m_rManager.m_aAccessMutex);
		if(m_bCallRegistered)
		{
			//Mutex needs to be acquired exactly ones
			//mutex may be released inbetween in special case of impl_apiCallCountReachedNull()
			m_rManager.impl_unregisterApiCall(m_bLongLastingCallRegistered);
		}
	}
	catch( uno::Exception& ex )
	{
		//@todo ? allow a uno::RuntimeException from dispose to travel through??
        ex.Context.is(); //to avoid compilation warnings
	}
}

/*
the XCloseable::close method has to be implemented in the following way:
::close
{
	//hold no mutex

	if( !m_aLifeTimeManager.g_close_startTryClose( bDeliverOwnership ) )
		return;
	//no mutex is acquired

	// At the end of this method may we must dispose ourself ...
    // and may nobody from outside hold a reference to us ...
    // then it's a good idea to do that by ourself.
    uno::Reference< uno::XInterface > xSelfHold( static_cast< ::cppu::OWeakObject* >(this) );

	//the listeners have had no veto
	//check wether we self can close
	{
		util::CloseVetoException aVetoException = util::CloseVetoException(
						::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
						"the model itself could not be closed" ) )
						, static_cast< ::cppu::OWeakObject* >(this));

		if( m_aLifeTimeManager.g_close_isNeedToCancelLongLastingCalls( bDeliverOwnership, aVetoException ) )
		{
			////you can empty this block, if you never start longlasting calls or
			////if your longlasting calls are per default not cancelable (check how you have constructed your LifeTimeManager)

			sal_Bool bLongLastingCallsAreCanceled = sal_False;
			try
			{
				//try to cancel running longlasting calls
				//// @todo
			}
			catch( uno::Exception& ex )
			{
				//// @todo
				//do not throw anything here!! (without endTryClose)
			}
			//if not successful canceled
			if(!bLongLastingCallsAreCanceled)
			{
				m_aLifeTimeManager.g_close_endTryClose( bDeliverOwnership, sal_True );
                throw aVetoException;
			}
		}

	}
	m_aLifeTimeManager.g_close_endTryClose_doClose();
}
*/

}//end namespace apphelper
