/**************************************************************
 * 
 * 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_cppuhelper.hxx"

#include <cppuhelper/interfacecontainer.hxx>
#include <cppuhelper/queryinterface.hxx>
#include <cppuhelper/propshlp.hxx>

#include <osl/diagnose.h>
#include <osl/mutex.hxx>

#include <hash_map>

#include <com/sun/star/lang/XEventListener.hpp>


using namespace osl;
using namespace com::sun::star::uno;
using namespace com::sun::star::lang;

namespace cppu
{

//===================================================================
//===================================================================
//===================================================================
/**
 * Reallocate the sequence.
 */
static void realloc( Sequence< Reference< XInterface > > & rSeq, sal_Int32 nNewLen )
	SAL_THROW( () )
{
	rSeq.realloc( nNewLen );
}

/**
 * Remove an element from an interface sequence.
 */
static void sequenceRemoveElementAt( Sequence< Reference< XInterface > > & rSeq, sal_Int32 index )
	SAL_THROW( () )
{
	sal_Int32 nNewLen = rSeq.getLength() - 1;

	Sequence< Reference< XInterface > > aDestSeq( rSeq.getLength() - 1 );
	// getArray on a const sequence is faster
	const Reference< XInterface > * pSource = ((const Sequence< Reference< XInterface > > &)rSeq).getConstArray();
	Reference< XInterface > * pDest = aDestSeq.getArray();
	sal_Int32 i = 0;
	for( ; i < index; i++ )
		pDest[i] = pSource[i];
	for( sal_Int32 j = i ; j < nNewLen; j++ )
		pDest[j] = pSource[j+1];
	rSeq = aDestSeq;
}


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

#ifdef _MSC_VER
#pragma warning( disable: 4786 )
#endif

//===================================================================
//===================================================================
//===================================================================
OInterfaceIteratorHelper::OInterfaceIteratorHelper( OInterfaceContainerHelper & rCont_ )
	SAL_THROW( () )
	: rCont( rCont_ )
{
	MutexGuard aGuard( rCont.rMutex );
	if( rCont.bInUse )
		// worst case, two iterators at the same time
		rCont.copyAndResetInUse();
	bIsList = rCont_.bIsList;
	aData = rCont_.aData;
	if( bIsList )
	{
		rCont.bInUse = sal_True;
		nRemain = aData.pAsSequence->getLength();
	}
	else if( aData.pAsInterface )
	{
		aData.pAsInterface->acquire();
		nRemain = 1;
	}
	else
		nRemain = 0;
}

OInterfaceIteratorHelper::~OInterfaceIteratorHelper() SAL_THROW( () )
{
	sal_Bool bShared;
	{
	MutexGuard aGuard( rCont.rMutex );
	// bResetInUse protect the iterator against recursion
	bShared = aData.pAsSequence == rCont.aData.pAsSequence && rCont.bIsList;
	if( bShared )
	{
		OSL_ENSURE( rCont.bInUse, "OInterfaceContainerHelper must be in use" );
		rCont.bInUse = sal_False;
	}
	}

	if( !bShared )
	{
		if( bIsList )
			// Sequence owned by the iterator
			delete aData.pAsSequence;
		else if( aData.pAsInterface )
			// Interface is acquired by the iterator
			aData.pAsInterface->release();
	}
}

XInterface * OInterfaceIteratorHelper::next() SAL_THROW( () )
{
	if( nRemain )
	{
		nRemain--;
		if( bIsList )
			// typecase to const,so the getArray method is faster
			return aData.pAsSequence->getConstArray()[nRemain].get();
		else if( aData.pAsInterface )
			return aData.pAsInterface;
	}
	// exception
	return 0;
}

void OInterfaceIteratorHelper::remove() SAL_THROW( () )
{
	if( bIsList )
	{
		OSL_ASSERT( nRemain >= 0 &&
					nRemain < aData.pAsSequence->getLength() );
        XInterface * p = aData.pAsSequence->getConstArray()[nRemain].get();
		rCont.removeInterface( * reinterpret_cast< const Reference< XInterface > * >( &p ) );
	}
	else
	{
		OSL_ASSERT( 0 == nRemain );
		rCont.removeInterface( * reinterpret_cast< const Reference< XInterface > * >(&aData.pAsInterface));
	}
}

//===================================================================
//===================================================================
//===================================================================


OInterfaceContainerHelper::OInterfaceContainerHelper( Mutex & rMutex_ ) SAL_THROW( () )
	: rMutex( rMutex_ )
	, bInUse( sal_False )
	, bIsList( sal_False )
{
}

OInterfaceContainerHelper::~OInterfaceContainerHelper() SAL_THROW( () )
{
	OSL_ENSURE( !bInUse, "~OInterfaceContainerHelper but is in use" );
	if( bIsList )
		delete aData.pAsSequence;
	else if( aData.pAsInterface )
		aData.pAsInterface->release();
}

sal_Int32 OInterfaceContainerHelper::getLength() const SAL_THROW( () )
{
	MutexGuard aGuard( rMutex );
	if( bIsList )
		return aData.pAsSequence->getLength();
	else if( aData.pAsInterface )
		return 1;
	return 0;
}

Sequence< Reference<XInterface> > OInterfaceContainerHelper::getElements() const SAL_THROW( () )
{
	MutexGuard aGuard( rMutex );
	if( bIsList )
		return *aData.pAsSequence;
	else if( aData.pAsInterface )
	{
		Reference<XInterface> x( aData.pAsInterface );
		return Sequence< Reference< XInterface > >( &x, 1 );
	}
	return Sequence< Reference< XInterface > >();
}

void OInterfaceContainerHelper::copyAndResetInUse() SAL_THROW( () )
{
	OSL_ENSURE( bInUse, "OInterfaceContainerHelper not in use" );
	if( bInUse )
	{
		// this should be the worst case. If a iterator is active
		// and a new Listener is added.
		if( bIsList )
			aData.pAsSequence = new Sequence< Reference< XInterface > >( *aData.pAsSequence );
		else if( aData.pAsInterface )
			aData.pAsInterface->acquire();

		bInUse = sal_False;
	}
}

sal_Int32 OInterfaceContainerHelper::addInterface( const Reference<XInterface> & rListener ) SAL_THROW( () )
{
	OSL_ASSERT( rListener.is() );
	MutexGuard aGuard( rMutex );
	if( bInUse )
		copyAndResetInUse();

	if( bIsList )
	{
		sal_Int32 nLen = aData.pAsSequence->getLength();
		realloc( *aData.pAsSequence, nLen +1 );
		aData.pAsSequence->getArray()[ nLen ] = rListener;
		return nLen +1;
	}
	else if( aData.pAsInterface )
	{
		Sequence< Reference< XInterface > > * pSeq = new Sequence< Reference< XInterface > >( 2 );
		Reference<XInterface> * pArray = pSeq->getArray();
		pArray[0] = aData.pAsInterface;
		pArray[1] = rListener;
		aData.pAsInterface->release();
		aData.pAsSequence = pSeq;
		bIsList = sal_True;
		return 2;
	}
	else
	{
		aData.pAsInterface = rListener.get();
		if( rListener.is() )
			rListener->acquire();
		return 1;
	}
}

sal_Int32 OInterfaceContainerHelper::removeInterface( const Reference<XInterface> & rListener ) SAL_THROW( () )
{
	OSL_ASSERT( rListener.is() );
	MutexGuard aGuard( rMutex );
	if( bInUse )
		copyAndResetInUse();

	if( bIsList )
	{
		const Reference<XInterface> * pL = aData.pAsSequence->getConstArray();
		sal_Int32 nLen = aData.pAsSequence->getLength();
		sal_Int32 i;
		for( i = 0; i < nLen; i++ )
		{
			// It is not valid to compare the Pointer direkt, but is is is much
			// more faster.
			if( pL[i].get() == rListener.get() )
			{
				sequenceRemoveElementAt( *aData.pAsSequence, i );
				break;
			}
		}

		if( i == nLen )
		{
			// interface not found, use the correct compare method
			for( i = 0; i < nLen; i++ )
			{
				if( pL[i] == rListener )
				{
					sequenceRemoveElementAt(*aData.pAsSequence, i );
					break;
				}
			}
		}

		if( aData.pAsSequence->getLength() == 1 )
		{
			XInterface * p = aData.pAsSequence->getConstArray()[0].get();
			p->acquire();
			delete aData.pAsSequence;
			aData.pAsInterface = p;
			bIsList = sal_False;
			return 1;
		}
		else
			return aData.pAsSequence->getLength();
	}
	else if( aData.pAsInterface && Reference<XInterface>( aData.pAsInterface ) == rListener )
	{
		aData.pAsInterface->release();
		aData.pAsInterface = 0;
	}
	return aData.pAsInterface ? 1 : 0;
}

void OInterfaceContainerHelper::disposeAndClear( const EventObject & rEvt ) SAL_THROW( () )
{
	ClearableMutexGuard aGuard( rMutex );
	OInterfaceIteratorHelper aIt( *this );
	// Container freigeben, falls im disposing neue Einträge kommen
	OSL_ENSURE( !bIsList || bInUse, "OInterfaceContainerHelper not in use" );
	if( !bIsList && aData.pAsInterface )
		aData.pAsInterface->release();
	// set the member to null, the iterator delete the values
	aData.pAsInterface = NULL;
	bIsList = sal_False;
	bInUse = sal_False;
	aGuard.clear();
	while( aIt.hasMoreElements() )
	{
		try
		{
			Reference<XEventListener > xLst( aIt.next(), UNO_QUERY );
			if( xLst.is() )
				xLst->disposing( rEvt );
		}
		catch ( RuntimeException & )
		{
			// be robust, if e.g. a remote bridge has disposed already.
			// there is no way, to delegate the error to the caller :o(.
		}
	}
}


void OInterfaceContainerHelper::clear() SAL_THROW( () )
{
	ClearableMutexGuard aGuard( rMutex );
	OInterfaceIteratorHelper aIt( *this );
	// Container freigeben, falls im disposing neue Einträge kommen
	OSL_ENSURE( !bIsList || bInUse, "OInterfaceContainerHelper not in use" );
	if( !bIsList && aData.pAsInterface )
		aData.pAsInterface->release();
	// set the member to null, the iterator delete the values
	aData.pAsInterface = 0;
	bIsList = sal_False;
	bInUse = sal_False;
	// release mutex before aIt destructor call
	aGuard.clear();
}

//##################################################################################################
//##################################################################################################
//##################################################################################################

// specialized class for type

typedef ::std::vector< std::pair < Type , void* > > t_type2ptr;

OMultiTypeInterfaceContainerHelper::OMultiTypeInterfaceContainerHelper( Mutex & rMutex_ )
    SAL_THROW( () )
    : rMutex( rMutex_ )
{
	m_pMap = new t_type2ptr();
}
OMultiTypeInterfaceContainerHelper::~OMultiTypeInterfaceContainerHelper()
    SAL_THROW( () )
{
    t_type2ptr * pMap = (t_type2ptr *)m_pMap;
	t_type2ptr::iterator iter = pMap->begin();
	t_type2ptr::iterator end = pMap->end();

	while( iter != end )
	{
		delete (OInterfaceContainerHelper*)(*iter).second;
		(*iter).second = 0;
		++iter;
	}
	delete pMap;
}
Sequence< Type > OMultiTypeInterfaceContainerHelper::getContainedTypes() const
    SAL_THROW( () )
{
    t_type2ptr * pMap = (t_type2ptr *)m_pMap;
	t_type2ptr::size_type nSize;

	::osl::MutexGuard aGuard( rMutex );
	nSize = pMap->size();
	if( nSize )
	{
		::com::sun::star::uno::Sequence< Type > aInterfaceTypes( nSize );
		Type * pArray = aInterfaceTypes.getArray();

		t_type2ptr::iterator iter = pMap->begin();
		t_type2ptr::iterator end = pMap->end();

		sal_Int32 i = 0;
		while( iter != end )
		{
			// are interfaces added to this container?
			if( ((OInterfaceContainerHelper*)(*iter).second)->getLength() )
				// yes, put the type in the array
				pArray[i++] = (*iter).first;
			++iter;
		}
		if( (t_type2ptr::size_type)i != nSize ) {
			// may be empty container, reduce the sequence to the right size
			aInterfaceTypes = ::com::sun::star::uno::Sequence< Type >( pArray, i );
		}
		return aInterfaceTypes;
	}
	return ::com::sun::star::uno::Sequence< Type >();
}

static t_type2ptr::iterator findType(t_type2ptr *pMap, const Type & rKey )
{
	t_type2ptr::iterator iter = pMap->begin();
	t_type2ptr::iterator end = pMap->end();

	while( iter != end )
    {
        if (iter->first == rKey)
            break;
        iter++;
    }
    return iter;
}

OInterfaceContainerHelper * OMultiTypeInterfaceContainerHelper::getContainer( const Type & rKey ) const
    SAL_THROW( () )
{
	::osl::MutexGuard aGuard( rMutex );

    t_type2ptr * pMap = (t_type2ptr *)m_pMap;
 	t_type2ptr::iterator iter = findType( pMap, rKey );
	if( iter != pMap->end() )
			return (OInterfaceContainerHelper*) (*iter).second;
	return 0;
}
sal_Int32 OMultiTypeInterfaceContainerHelper::addInterface(
    const Type & rKey, const Reference< XInterface > & rListener )
    SAL_THROW( () )
{
	::osl::MutexGuard aGuard( rMutex );
    t_type2ptr * pMap = (t_type2ptr *)m_pMap;
	t_type2ptr::iterator iter = findType( pMap, rKey );
	if( iter == pMap->end() )
	{
		OInterfaceContainerHelper * pLC = new OInterfaceContainerHelper( rMutex );
        pMap->push_back(std::pair<Type, void*>(rKey, pLC));
		return pLC->addInterface( rListener );
	}
	else
		return ((OInterfaceContainerHelper*)(*iter).second)->addInterface( rListener );
}
sal_Int32 OMultiTypeInterfaceContainerHelper::removeInterface(
    const Type & rKey, const Reference< XInterface > & rListener )
    SAL_THROW( () )
{
	::osl::MutexGuard aGuard( rMutex );

	// search container with id nUik
    t_type2ptr * pMap = (t_type2ptr *)m_pMap;
	t_type2ptr::iterator iter = findType( pMap, rKey );
		// container found?
	if( iter != pMap->end() )
        return ((OInterfaceContainerHelper*)(*iter).second)->removeInterface( rListener );

	// no container with this id. Always return 0
	return 0;
}
void OMultiTypeInterfaceContainerHelper::disposeAndClear( const EventObject & rEvt )
    SAL_THROW( () )
{
	t_type2ptr::size_type nSize = 0;
	OInterfaceContainerHelper ** ppListenerContainers = NULL;
	{
		::osl::MutexGuard aGuard( rMutex );
        t_type2ptr * pMap = (t_type2ptr *)m_pMap;
		nSize = pMap->size();
		if( nSize )
		{
			typedef OInterfaceContainerHelper* ppp;
			ppListenerContainers = new ppp[nSize];
			//ppListenerContainers = new (ListenerContainer*)[nSize];

			t_type2ptr::iterator iter = pMap->begin();
			t_type2ptr::iterator end = pMap->end();

			t_type2ptr::size_type i = 0;
			while( iter != end )
			{
				ppListenerContainers[i++] = (OInterfaceContainerHelper*)(*iter).second;
				++iter;
			}
		}
	}

	// create a copy, because do not fire event in a guarded section
	for( t_type2ptr::size_type i = 0;
			i < nSize; i++ )
	{
		if( ppListenerContainers[i] )
			ppListenerContainers[i]->disposeAndClear( rEvt );
	}

	delete [] ppListenerContainers;
}
void OMultiTypeInterfaceContainerHelper::clear()
    SAL_THROW( () )
{
	::osl::MutexGuard aGuard( rMutex );
    t_type2ptr * pMap = (t_type2ptr *)m_pMap;
	t_type2ptr::iterator iter = pMap->begin();
	t_type2ptr::iterator end = pMap->end();

	while( iter != end )
	{
		((OInterfaceContainerHelper*)(*iter).second)->clear();
		++iter;
	}
}


//##################################################################################################
//##################################################################################################
//##################################################################################################

// specialized class for long

typedef ::std::vector< std::pair < sal_Int32 , void* > > t_long2ptr;

static t_long2ptr::iterator findLong(t_long2ptr *pMap, sal_Int32 nKey )
{
	t_long2ptr::iterator iter = pMap->begin();
	t_long2ptr::iterator end = pMap->end();

	while( iter != end )
    {
        if (iter->first == nKey)
            break;
        iter++;
    }
    return iter;
}

OMultiTypeInterfaceContainerHelperInt32::OMultiTypeInterfaceContainerHelperInt32( Mutex & rMutex_ )
    SAL_THROW( () )
    : m_pMap( NULL )
    , rMutex( rMutex_ )
{
    // delay pMap allocation until necessary.
}
OMultiTypeInterfaceContainerHelperInt32::~OMultiTypeInterfaceContainerHelperInt32()
    SAL_THROW( () )
{
    if (!m_pMap)
        return;

    t_long2ptr * pMap = (t_long2ptr *)m_pMap;
	t_long2ptr::iterator iter = pMap->begin();
	t_long2ptr::iterator end = pMap->end();

	while( iter != end )
	{
		delete (OInterfaceContainerHelper*)(*iter).second;
		(*iter).second = 0;
		++iter;
	}
	delete pMap;
}
Sequence< sal_Int32 > OMultiTypeInterfaceContainerHelperInt32::getContainedTypes() const
    SAL_THROW( () )
{
    t_long2ptr * pMap = (t_long2ptr *)m_pMap;
	t_long2ptr::size_type nSize;

	::osl::MutexGuard aGuard( rMutex );
	nSize = pMap ? pMap->size() : 0;
	if( nSize )
	{
		::com::sun::star::uno::Sequence< sal_Int32 > aInterfaceTypes( nSize );
		sal_Int32 * pArray = aInterfaceTypes.getArray();

		t_long2ptr::iterator iter = pMap->begin();
		t_long2ptr::iterator end = pMap->end();
        
		sal_Int32 i = 0;
		while( iter != end )
		{
			// are interfaces added to this container?
			if( ((OInterfaceContainerHelper*)(*iter).second)->getLength() )
				// yes, put the type in the array
				pArray[i++] = (*iter).first;
			++iter;
		}
		if( (t_long2ptr::size_type)i != nSize ) {
			// may be empty container, reduce the sequence to the right size
			aInterfaceTypes = ::com::sun::star::uno::Sequence< sal_Int32 >( pArray, i );
		}
		return aInterfaceTypes;
	}
	return ::com::sun::star::uno::Sequence< sal_Int32 >();
}
OInterfaceContainerHelper * OMultiTypeInterfaceContainerHelperInt32::getContainer( const sal_Int32 & rKey ) const
    SAL_THROW( () )
{
	::osl::MutexGuard aGuard( rMutex );

    if (!m_pMap)
        return 0;
    t_long2ptr * pMap = (t_long2ptr *)m_pMap;
 	t_long2ptr::iterator iter = findLong( pMap, rKey );
	if( iter != pMap->end() )
			return (OInterfaceContainerHelper*) (*iter).second;
	return 0;
}
sal_Int32 OMultiTypeInterfaceContainerHelperInt32::addInterface(
    const sal_Int32 & rKey, const Reference< XInterface > & rListener )
    SAL_THROW( () )
{
	::osl::MutexGuard aGuard( rMutex );
    if (!m_pMap)
    	m_pMap = new t_long2ptr();
    t_long2ptr * pMap = (t_long2ptr *)m_pMap;
	t_long2ptr::iterator iter = findLong( pMap, rKey );
 	if( iter == pMap->end() )
	{
		OInterfaceContainerHelper * pLC = new OInterfaceContainerHelper( rMutex );
        pMap->push_back(std::pair< sal_Int32, void* >(rKey, pLC));
		return pLC->addInterface( rListener );
	}
	else
		return ((OInterfaceContainerHelper*)(*iter).second)->addInterface( rListener );
}
sal_Int32 OMultiTypeInterfaceContainerHelperInt32::removeInterface(
    const sal_Int32 & rKey, const Reference< XInterface > & rListener )
    SAL_THROW( () )
{
	::osl::MutexGuard aGuard( rMutex );

    if (!m_pMap)
        return 0;
	// search container with id nUik
    t_long2ptr * pMap = (t_long2ptr *)m_pMap;
	t_long2ptr::iterator iter = findLong( pMap, rKey );
		// container found?
	if( iter != pMap->end() )
        return ((OInterfaceContainerHelper*)(*iter).second)->removeInterface( rListener );

	// no container with this id. Always return 0
	return 0;
}
void OMultiTypeInterfaceContainerHelperInt32::disposeAndClear( const EventObject & rEvt )
    SAL_THROW( () )
{
	t_long2ptr::size_type nSize = 0;
	OInterfaceContainerHelper ** ppListenerContainers = NULL;
	{
		::osl::MutexGuard aGuard( rMutex );
        if (!m_pMap)
            return;

        t_long2ptr * pMap = (t_long2ptr *)m_pMap;
		nSize = pMap->size();
		if( nSize )
		{
			typedef OInterfaceContainerHelper* ppp;
			ppListenerContainers = new ppp[nSize];
			//ppListenerContainers = new (ListenerContainer*)[nSize];

			t_long2ptr::iterator iter = pMap->begin();
			t_long2ptr::iterator end = pMap->end();

			t_long2ptr::size_type i = 0;
			while( iter != end )
			{
				ppListenerContainers[i++] = (OInterfaceContainerHelper*)(*iter).second;
				++iter;
			}
		}
	}

	// create a copy, because do not fire event in a guarded section
	for( t_long2ptr::size_type i = 0;
			i < nSize; i++ )
	{
		if( ppListenerContainers[i] )
			ppListenerContainers[i]->disposeAndClear( rEvt );
	}

	delete [] ppListenerContainers;
}
void OMultiTypeInterfaceContainerHelperInt32::clear()
    SAL_THROW( () )
{
	::osl::MutexGuard aGuard( rMutex );
    if (!m_pMap)
        return;
    t_long2ptr * pMap = (t_long2ptr *)m_pMap;
	t_long2ptr::iterator iter = pMap->begin();
	t_long2ptr::iterator end = pMap->end();

	while( iter != end )
	{
		((OInterfaceContainerHelper*)(*iter).second)->clear();
		++iter;
	}
}

}

