/**************************************************************
 * 
 * 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_comphelper.hxx"
#include <comphelper/accessibleeventnotifier.hxx>
#include <osl/diagnose.h>
#include <rtl/instance.hxx>
#include <comphelper/guarding.hxx>

using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::accessibility;
using namespace ::comphelper;

//=====================================================================
//= AccessibleEventNotifier
//=====================================================================
//---------------------------------------------------------------------
namespace 
{
    struct lclMutex 
        : public rtl::Static< ::osl::Mutex, lclMutex > {};
    struct Clients 
        : public rtl::Static< AccessibleEventNotifier::ClientMap, Clients > {}; 
}

//.........................................................................
namespace comphelper
{
//.........................................................................

	//---------------------------------------------------------------------
	AccessibleEventNotifier::TClientId AccessibleEventNotifier::generateId()
	{
		TClientId nBiggestUsedId = 0;
		TClientId nFreeId = 0;

		// look through all registered clients until we find a "gap" in the ids

		// Note that the following relies on the fact the elements in the map are traveled with
		// ascending keys (aka client ids)
        AccessibleEventNotifier::ClientMap &rClients = Clients::get();
		for	(	ClientMap::const_iterator aLookup = rClients.begin();
				aLookup != rClients.end();
				++aLookup
			)
		{
			TClientId nCurrent = aLookup->first;
			OSL_ENSURE( nCurrent > nBiggestUsedId, "AccessibleEventNotifier::generateId: map is expected to be sorted ascending!" );

			if ( nCurrent - nBiggestUsedId > 1 )
			{	// found a "gap"
				nFreeId = nBiggestUsedId + 1;
				break;
			}

			nBiggestUsedId = nCurrent;
		}

		if ( !nFreeId )
			nFreeId = nBiggestUsedId + 1;

		OSL_ENSURE( rClients.end() == rClients.find( nFreeId ),
			"AccessibleEventNotifier::generateId: algorithm broken!" );

		return nFreeId;
	}

	//---------------------------------------------------------------------
	AccessibleEventNotifier::TClientId AccessibleEventNotifier::registerClient( )
	{
		::osl::MutexGuard aGuard( lclMutex::get() );

		// generate a new client id
		TClientId nNewClientId = generateId( );

		// the event listeners for the new client
		EventListeners* pNewListeners = new EventListeners( lclMutex::get() );
			// note that we're using our own mutex here, so the listener containers for all
			// our clients share this same mutex.
			// this is a reminiscense to the days where the notifier was asynchronous. Today this is
			// completely nonsense, and potentially slowing down the Office me thinks ...

		// add the client
		Clients::get().insert( ClientMap::value_type( nNewClientId, pNewListeners ) );

		// outta here
		return nNewClientId;
	}

	//---------------------------------------------------------------------
	sal_Bool AccessibleEventNotifier::implLookupClient( const TClientId _nClient, ClientMap::iterator& _rPos )
	{
		// look up this client
        AccessibleEventNotifier::ClientMap &rClients = Clients::get();
		_rPos = rClients.find( _nClient );
		OSL_ENSURE( rClients.end() != _rPos, "AccessibleEventNotifier::implLookupClient: invalid client id (did you register your client?)!" );

		return ( rClients.end() != _rPos );
	}

	//---------------------------------------------------------------------
	void AccessibleEventNotifier::revokeClient( const TClientId _nClient )
	{
		::osl::MutexGuard aGuard( lclMutex::get() );

		ClientMap::iterator aClientPos;
		if ( !implLookupClient( _nClient, aClientPos ) )
			// already asserted in implLookupClient
			return;

		// remove it from the clients map
		delete aClientPos->second;
		Clients::get().erase( aClientPos );
	}

	//---------------------------------------------------------------------
	void AccessibleEventNotifier::revokeClientNotifyDisposing( const TClientId _nClient,
			const Reference< XInterface >& _rxEventSource ) SAL_THROW( ( ) )
	{
		::osl::MutexGuard aGuard( lclMutex::get() );

		ClientMap::iterator aClientPos;
		if ( !implLookupClient( _nClient, aClientPos ) )
			// already asserted in implLookupClient
			return;

		// notify the "disposing" event for this client
		EventObject aDisposalEvent;
		aDisposalEvent.Source = _rxEventSource;

		// notify the listeners
		EventListeners* pListeners = aClientPos->second;

		// we do not need the entry in the clients map anymore
		// (do this before actually notifying, because some client implementations have re-entrance
		// problems and call into revokeClient while we are notifying from hereing)
		Clients::get().erase( aClientPos );

		// now really do the notification
		pListeners->disposeAndClear( aDisposalEvent );
		delete pListeners;

	}

	//---------------------------------------------------------------------
	sal_Int32 AccessibleEventNotifier::addEventListener(
		const TClientId _nClient, const Reference< XAccessibleEventListener >& _rxListener ) SAL_THROW( ( ) )
	{
		::osl::MutexGuard aGuard( lclMutex::get() );

		ClientMap::iterator aClientPos;
		if ( !implLookupClient( _nClient, aClientPos ) )
			// already asserted in implLookupClient
			return 0;

		if ( _rxListener.is() )
			aClientPos->second->addInterface( _rxListener );

		return aClientPos->second->getLength();
	}

	//---------------------------------------------------------------------
	sal_Int32 AccessibleEventNotifier::removeEventListener(
		const TClientId _nClient, const Reference< XAccessibleEventListener >& _rxListener ) SAL_THROW( ( ) )
	{
		::osl::MutexGuard aGuard( lclMutex::get() );

		ClientMap::iterator aClientPos;
		if ( !implLookupClient( _nClient, aClientPos ) )
			// already asserted in implLookupClient
			return 0;

		if ( _rxListener.is() )
			aClientPos->second->removeInterface( _rxListener );

		return aClientPos->second->getLength();
	}

	//---------------------------------------------------------------------
	Sequence< Reference< XInterface > > AccessibleEventNotifier::getEventListeners( const TClientId _nClient ) SAL_THROW( ( ) )
	{
		Sequence< Reference< XInterface > > aListeners;

		::osl::MutexGuard aGuard( lclMutex::get() );

		ClientMap::iterator aClientPos;
		if ( implLookupClient( _nClient, aClientPos ) )
			aListeners = aClientPos->second->getElements();

		return aListeners;
	}

	//---------------------------------------------------------------------
	void AccessibleEventNotifier::addEvent( const TClientId _nClient, const AccessibleEventObject& _rEvent ) SAL_THROW( ( ) )
	{
		Sequence< Reference< XInterface > > aListeners;

		// --- <mutex lock> -------------------------------
		{
			::osl::MutexGuard aGuard( lclMutex::get() );

			ClientMap::iterator aClientPos;
			if ( !implLookupClient( _nClient, aClientPos ) )
				// already asserted in implLookupClient
				return;

			// since we're synchronous, again, we want to notify immediately
			aListeners = aClientPos->second->getElements();
		}
		// --- </mutex lock> ------------------------------

			// default handling: loop through all listeners, and notify them
		const Reference< XInterface >* pListeners = aListeners.getConstArray();
		const Reference< XInterface >* pListenersEnd = pListeners + aListeners.getLength();
		while ( pListeners != pListenersEnd )
		{
			try
			{
				static_cast< XAccessibleEventListener* >( pListeners->get() )->notifyEvent( _rEvent );
			}
			catch( const Exception& )
			{
				// no assertion, because a broken access remote bridge or something like this
				// can cause this exception
			}
			++pListeners;
		}
	}

//.........................................................................
}	// namespace comphelper
//.........................................................................

