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

#include <stdio.h>
#include <math.h>
#include <string>
#include <hash_map>
#include <map>

#include <osl/diagnose.h>
#include <osl/mutex.hxx>
#include <osl/module.h>
#include <osl/process.h>
#include <osl/thread.h>
#include <osl/conditn.hxx>
#include <osl/time.h>

#ifdef SAL_W32
#include <windows.h>
#else
#include <sys/times.h>
#include <unistd.h>
#endif

#include <rtl/string.hxx>
#include <rtl/strbuf.hxx>
#include <rtl/ustrbuf.hxx>

#include <uno/environment.hxx>
#include <uno/mapping.hxx>

#include <cppuhelper/factory.hxx>
#include <cppuhelper/implbase2.hxx>

#include <com/sun/star/lang/XServiceInfo.hpp>
#include <com/sun/star/lang/XComponent.hpp>
#include <com/sun/star/lang/XMain.hpp>
#include <com/sun/star/lang/XInitialization.hpp>
#include <com/sun/star/loader/XImplementationLoader.hpp>
#include <com/sun/star/registry/XRegistryKey.hpp>
#include <com/sun/star/bridge/XUnoUrlResolver.hpp>
#include <com/sun/star/container/XSet.hpp>
#include <com/sun/star/test/performance/XPerformanceTest.hpp>

#define NLOOP 200000000

using namespace rtl;
using namespace osl;
using namespace cppu;
using namespace com::sun::star::uno;
using namespace com::sun::star::lang;
using namespace com::sun::star::loader;
using namespace com::sun::star::registry;
using namespace com::sun::star::bridge;
using namespace com::sun::star::container;
using namespace com::sun::star::test::performance;

#define SERVICENAME		"com.sun.star.test.performance.PerformanceTest"
#define IMPLNAME		"com.sun.star.comp.performance.PerformanceTest"

namespace benchmark_test
{

static inline sal_uInt32 getSystemTicks()
{
#ifdef SAL_W32
	return (sal_uInt32)GetTickCount();
#else // only UNX supported for now
	static sal_uInt32	nImplTicksPerSecond = 0;
	static double		dImplTicksPerSecond;
	static double		dImplTicksULONGMAX;

	struct tms			aTms;
	sal_uInt32 nTicks = (sal_uInt32)times( &aTms );

	if ( !nImplTicksPerSecond )
	{
		nImplTicksPerSecond = sysconf(_SC_CLK_TCK);
		dImplTicksPerSecond = nImplTicksPerSecond;
		dImplTicksULONGMAX	= (double)(sal_uInt32)ULONG_MAX;
	}

	double fTicks = nTicks;
	fTicks *= 1000;
	fTicks /= dImplTicksPerSecond;
	fTicks = fmod (fTicks, dImplTicksULONGMAX);

	return (sal_uInt32)fTicks;
#endif
}

//--------------------------------------------------------------------------------------------------
static void out( const sal_Char * pText, FILE * stream = stderr,
				 sal_Int32 nStart = -1, sal_Char cFillchar = ' ' )
{
	static sal_Int32 s_nPos = 0;

	sal_Char ar[2] = { cFillchar, 0 };
	while (s_nPos < nStart)
	{
		::fprintf( stream, ar );
		++s_nPos;
	}

	::fprintf( stream, pText );

	for ( const sal_Char * p = pText; *p; ++p )
	{
		if (*p == '\n')
			s_nPos = 0;
		else
			++s_nPos;
	}
}
//--------------------------------------------------------------------------------------------------
static inline void out( const OUString & rText, FILE * stream = stderr,
						sal_Int32 nStart = -1, sal_Char cFillchar = ' ' )
{
	OString aText( OUStringToOString( rText, RTL_TEXTENCODING_ASCII_US ) );
	out( aText.getStr(), stream, nStart, cFillchar );
}
//--------------------------------------------------------------------------------------------------
static inline void out( double fVal, FILE * stream = stderr,
						sal_Int32 nStart = -1, sal_Char cFillchar = ' ' )
{
	sal_Char ar[128];
    ::snprintf( ar, sizeof(ar), (fVal < 0.000001 ? "%g" : "%f"), fVal );
	out( ar, stream, nStart, cFillchar );
}
//--------------------------------------------------------------------------------------------------
static inline void out( sal_Int64 nVal, FILE * stream = stderr,
						sal_Int32 nStart = -1, sal_Char cFillchar = ' ' )
{
	sal_Char ar[128];
    ::snprintf( ar, sizeof(ar), "%ld", nVal );
	out( ar, stream, nStart, cFillchar );
}

//==================================================================================================
Reference< XSingleServiceFactory > loadLibComponentFactory(
	const OUString & rLibName, const OUString & rImplName,
	const Reference< XMultiServiceFactory > & xSF, const Reference< XRegistryKey > & xKey )
{
	Reference< XSingleServiceFactory > xRet;

	OUStringBuffer aLibNameBuf( 32 );
#ifdef SAL_UNX
	aLibNameBuf.appendAscii( RTL_CONSTASCII_STRINGPARAM("lib") );
	aLibNameBuf.append( rLibName );
	aLibNameBuf.appendAscii( RTL_CONSTASCII_STRINGPARAM(".so") );
#else
	aLibNameBuf.append( rLibName );
	aLibNameBuf.appendAscii( RTL_CONSTASCII_STRINGPARAM(".dll") );
#endif
	OUString aLibName( aLibNameBuf.makeStringAndClear() );
	oslModule lib = osl_loadModule( aLibName.pData, SAL_LOADMODULE_LAZY | SAL_LOADMODULE_GLOBAL );

	if (lib)
	{
		void * pSym;

		// ========================= LATEST VERSION =========================
		OUString aGetEnvName( RTL_CONSTASCII_USTRINGPARAM(COMPONENT_GETENV) );
		if (pSym = osl_getSymbol( lib, aGetEnvName.pData ))
		{
			uno_Environment * pCurrentEnv = 0;
			uno_Environment * pEnv = 0;
			const sal_Char * pEnvTypeName = 0;
			(*((component_getImplementationEnvironmentFunc)pSym))( &pEnvTypeName, &pEnv );

			sal_Bool bNeedsMapping =
				(pEnv || 0 != rtl_str_compare( pEnvTypeName, CPPU_CURRENT_LANGUAGE_BINDING_NAME ));

			OUString aEnvTypeName( OUString::createFromAscii( pEnvTypeName ) );

			if (bNeedsMapping)
			{
				if (! pEnv)
					uno_getEnvironment( &pEnv, aEnvTypeName.pData, 0 );
				if (pEnv)
				{
					OUString aCppEnvTypeName( RTL_CONSTASCII_USTRINGPARAM(CPPU_CURRENT_LANGUAGE_BINDING_NAME) );
					uno_getEnvironment( &pCurrentEnv, aCppEnvTypeName.pData, 0 );
					if (pCurrentEnv)
						bNeedsMapping = (pEnv != pCurrentEnv);
				}
			}

			OUString aGetFactoryName( RTL_CONSTASCII_USTRINGPARAM(COMPONENT_GETFACTORY) );
			if (pSym = osl_getSymbol( lib, aGetFactoryName.pData ))
			{
				OString aImplName( OUStringToOString( rImplName, RTL_TEXTENCODING_ASCII_US ) );

				if (bNeedsMapping)
				{
					if (pEnv && pCurrentEnv)
					{
						Mapping aCurrent2Env( pCurrentEnv, pEnv );
						Mapping aEnv2Current( pEnv, pCurrentEnv );

						if (aCurrent2Env.is() && aEnv2Current.is())
						{
							void * pSMgr = aCurrent2Env.mapInterface(
								xSF.get(), ::getCppuType( (const Reference< XMultiServiceFactory > *)0 ) );
							void * pKey = aCurrent2Env.mapInterface(
								xKey.get(), ::getCppuType( (const Reference< XRegistryKey > *)0 ) );

							void * pSSF = (*((component_getFactoryFunc)pSym))(
								aImplName.getStr(), pSMgr, pKey );

							if (pKey)
								(*pEnv->pExtEnv->releaseInterface)( pEnv->pExtEnv, pKey );
							if (pSMgr)
								(*pEnv->pExtEnv->releaseInterface)( pEnv->pExtEnv, pSMgr );

							if (pSSF)
							{
								aEnv2Current.mapInterface(
									reinterpret_cast< void ** >( &xRet ),
									pSSF, ::getCppuType( (const Reference< XSingleServiceFactory > *)0 ) );
								(*pEnv->pExtEnv->releaseInterface)( pEnv->pExtEnv, pSSF );
							}
						}
					}
				}
				else
				{
					XSingleServiceFactory * pRet = (XSingleServiceFactory *)
						(*((component_getFactoryFunc)pSym))(
							aImplName.getStr(), xSF.get(), xKey.get() );
					if (pRet)
					{
						xRet = pRet;
						pRet->release();
					}
				}
			}

			if (pEnv)
				(*pEnv->release)( pEnv );
			if (pCurrentEnv)
				(*pCurrentEnv->release)( pCurrentEnv );
		}

		// ========================= PREVIOUS VERSION =========================
		else
		{
			OUString aGetFactoryName( RTL_CONSTASCII_USTRINGPARAM(CREATE_COMPONENT_FACTORY_FUNCTION) );
			if (pSym = osl_getSymbol( lib, aGetFactoryName.pData ))
			{
				OUString aCppEnvTypeName( RTL_CONSTASCII_USTRINGPARAM(CPPU_CURRENT_LANGUAGE_BINDING_NAME) );
				OUString aUnoEnvTypeName( RTL_CONSTASCII_USTRINGPARAM(UNO_LB_UNO) );
				Mapping aUno2Cpp( aUnoEnvTypeName, aCppEnvTypeName );
				Mapping aCpp2Uno( aCppEnvTypeName, aUnoEnvTypeName );
				OSL_ENSURE( aUno2Cpp.is() && aCpp2Uno.is(), "### cannot get uno mappings!" );

				if (aUno2Cpp.is() && aCpp2Uno.is())
				{
					uno_Interface * pUComponentFactory = 0;

					uno_Interface * pUSFactory = (uno_Interface *)aCpp2Uno.mapInterface(
						xSF.get(), ::getCppuType( (const Reference< XMultiServiceFactory > *)0 ) );
					uno_Interface * pUKey = (uno_Interface *)aCpp2Uno.mapInterface(
						xKey.get(), ::getCppuType( (const Reference< XRegistryKey > *)0 ) );

					pUComponentFactory = (*((CreateComponentFactoryFunc)pSym))(
						rImplName.getStr(), pUSFactory, pUKey );

					if (pUKey)
						(*pUKey->release)( pUKey );
					if (pUSFactory)
						(*pUSFactory->release)( pUSFactory );

					if (pUComponentFactory)
					{
						XSingleServiceFactory * pXFactory =
							(XSingleServiceFactory *)aUno2Cpp.mapInterface(
								pUComponentFactory, ::getCppuType( (const Reference< XSingleServiceFactory > *)0 ) );
						(*pUComponentFactory->release)( pUComponentFactory );

						if (pXFactory)
						{
							xRet = pXFactory;
							pXFactory->release();
						}
					}
				}
			}
		}

		if (! xRet.is())
			osl_unloadModule( lib );
	}

	return xRet;
}
//--------------------------------------------------------------------------------------------------
template< class T >
static void createInstance( Reference< T > & rxOut,
							const Reference< XMultiServiceFactory > & xMgr,
							const OUString & rServiceName )
	throw (RuntimeException)
{
	Reference< XInterface > x( xMgr->createInstance( rServiceName ), UNO_QUERY );

	if (! x.is())
	{
		static sal_Bool s_bSet = sal_False;
		if (! s_bSet)
		{
			MutexGuard aGuard( Mutex::getGlobalMutex() );
			if (! s_bSet)
			{
				Reference< XSet > xSet( xMgr, UNO_QUERY );
				if (xSet.is())
				{
					// acceptor
					xSet->insert( makeAny( loadLibComponentFactory(
						OUString( RTL_CONSTASCII_USTRINGPARAM("acceptor") ),
						OUString( RTL_CONSTASCII_USTRINGPARAM("com.sun.star.comp.stoc.Acceptor") ),
						xMgr, Reference< XRegistryKey >() ) ) );
					// connector
					xSet->insert( makeAny( loadLibComponentFactory(
						OUString( RTL_CONSTASCII_USTRINGPARAM("connectr") ),
						OUString( RTL_CONSTASCII_USTRINGPARAM("com.sun.star.comp.stoc.Connector") ),
						xMgr, Reference< XRegistryKey >() ) ) );
					// iiop bridge
					xSet->insert( makeAny( loadLibComponentFactory(
						OUString( RTL_CONSTASCII_USTRINGPARAM("remotebridge") ),
						OUString( RTL_CONSTASCII_USTRINGPARAM("com.sun.star.bridge.Bridge.various") ),
						xMgr, Reference< XRegistryKey >() ) ) );
					// bridge factory
					xSet->insert( makeAny( loadLibComponentFactory(
						OUString( RTL_CONSTASCII_USTRINGPARAM("brdgfctr") ),
						OUString( RTL_CONSTASCII_USTRINGPARAM("com.sun.star.comp.stoc.BridgeFactory") ),
						xMgr, Reference< XRegistryKey >() ) ) );
					// uno url resolver
					xSet->insert( makeAny( loadLibComponentFactory(
						OUString( RTL_CONSTASCII_USTRINGPARAM("uuresolver") ),
						OUString( RTL_CONSTASCII_USTRINGPARAM("com.sun.star.comp.bridge.UnoUrlResolver") ),
						xMgr, Reference< XRegistryKey >() ) ) );
					// java loader
//  					xSet->insert( makeAny( loadLibComponentFactory(
//  						OUString( RTL_CONSTASCII_USTRINGPARAM("javaloader") ),
//  						OUString( RTL_CONSTASCII_USTRINGPARAM("com.sun.star.comp.stoc.JavaComponentLoader") ),
//  						xMgr, Reference< XRegistryKey >() ) ) );
				}
				s_bSet = sal_True;
			}
		}
		x = xMgr->createInstance( rServiceName );
	}

	if (! x.is())
	{
		OUStringBuffer buf( 64 );
		buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("cannot get service instance \"") );
		buf.append( rServiceName );
		buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("\"!") );
		throw RuntimeException( buf.makeStringAndClear(), Reference< XInterface >() );
	}

	rxOut = Reference< T >::query( x );
	if (! rxOut.is())
	{
		OUStringBuffer buf( 64 );
		buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("service instance \"") );
		buf.append( rServiceName );
		buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("\" does not support demanded interface \"") );
		const Type & rType = ::getCppuType( (const Reference< T > *)0 );
		buf.append( rType.getTypeName() );
		buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("\"!") );
		throw RuntimeException( buf.makeStringAndClear(), Reference< XInterface >() );
	}
}

//--------------------------------------------------------------------------------------------------
inline static Sequence< OUString > getSupportedServiceNames()
{
	OUString aName( RTL_CONSTASCII_USTRINGPARAM(SERVICENAME) );
	return Sequence< OUString >( &aName, 1 );
}

//==================================================================================================
class TestImpl : public WeakImplHelper2< XServiceInfo, XMain >
{
	Reference< XMultiServiceFactory > _xSMgr;

	Reference< XInterface > _xDirect;
	Reference< XInterface > getDirect() throw (Exception);
	Reference< XInterface > resolveObject( const OUString & rUnoUrl ) throw (Exception);

public:
	TestImpl( const Reference< XMultiServiceFactory > & xSMgr );
	virtual ~TestImpl();

	// XServiceInfo
	virtual OUString SAL_CALL getImplementationName() throw (RuntimeException);
	virtual sal_Bool SAL_CALL supportsService( const OUString & rServiceName ) throw (RuntimeException);
	virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() throw (RuntimeException);

	// XMain
    virtual sal_Int32 SAL_CALL run( const Sequence< OUString > & rArgs ) throw (RuntimeException);
};

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

//__________________________________________________________________________________________________
TestImpl::TestImpl( const Reference< XMultiServiceFactory > & xSMgr )
	: _xSMgr( xSMgr )
{
}
//__________________________________________________________________________________________________
TestImpl::~TestImpl()
{
}

//==================================================================================================
static Reference< XInterface > SAL_CALL TestImpl_create( const Reference< XMultiServiceFactory > & xSMgr )
{
	return Reference< XInterface >( *new TestImpl( xSMgr ) );
}

// XServiceInfo
//__________________________________________________________________________________________________
OUString TestImpl::getImplementationName()
	throw (RuntimeException)
{
	return OUString( RTL_CONSTASCII_USTRINGPARAM(IMPLNAME) );
}
//__________________________________________________________________________________________________
sal_Bool TestImpl::supportsService( const OUString & rServiceName )
	throw (RuntimeException)
{
	const Sequence< OUString > & rSNL = getSupportedServiceNames();
	const OUString * pArray = rSNL.getConstArray();
	for ( sal_Int32 nPos = rSNL.getLength(); nPos--; )
	{
		if (pArray[nPos] == rServiceName)
			return sal_True;
	}
	return sal_False;
}
//__________________________________________________________________________________________________
Sequence< OUString > TestImpl::getSupportedServiceNames()
	throw (RuntimeException)
{
	return benchmark_test::getSupportedServiceNames();
}

//__________________________________________________________________________________________________
Reference< XInterface > TestImpl::getDirect()
	throw (Exception)
{
	if (! _xDirect.is())
	{
		MutexGuard aGuard( Mutex::getGlobalMutex() );
		if (! _xDirect.is())
		{
			Reference< XSingleServiceFactory > xFac( loadLibComponentFactory(
				OUString( RTL_CONSTASCII_USTRINGPARAM("perfobj") ),
				OUString( RTL_CONSTASCII_USTRINGPARAM("com.sun.star.comp.performance.PerformanceTestObject") ),
				_xSMgr, Reference< XRegistryKey >() ) );
			if (! xFac.is())
				throw RuntimeException( OUString( RTL_CONSTASCII_USTRINGPARAM("no test object available!") ), Reference< XInterface >() );
			_xDirect = xFac->createInstance();
		}
	}
	return _xDirect;
}
//--------------------------------------------------------------------------------------------------
Reference< XInterface > TestImpl::resolveObject( const OUString & rUnoUrl )
	throw (Exception)
{
	Reference< XUnoUrlResolver > xResolver;
	createInstance(
		xResolver, _xSMgr,
		OUString( RTL_CONSTASCII_USTRINGPARAM("com.sun.star.bridge.UnoUrlResolver") ) );

	Reference< XInterface > xResolvedObject( xResolver->resolve( rUnoUrl ) );

	if (! xResolvedObject.is())
	{
		OUStringBuffer buf( 32 );
		buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("cannot resolve object \"") );
		buf.append( rUnoUrl );
		buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("\"!") );
		throw RuntimeException( buf.makeStringAndClear(), Reference< XInterface >() );
	}

	return xResolvedObject;
}

//==================================================================================================
class TimeEntry
{
	sal_Int64			nLoop;
	sal_uInt32			nTicks;

public:
	TimeEntry()
		{}
	TimeEntry( sal_Int64 nLoop_, sal_uInt32 nTicks_ )
		: nLoop( nLoop_ )
		, nTicks( nTicks_ )
		{}

	inline double secPerCall() const
		{ return (((double)nTicks) / (nLoop * 1000)); }

	double ratio( const TimeEntry & rEntry ) const;
};
//__________________________________________________________________________________________________
double TimeEntry::ratio( const TimeEntry & rEntry ) const
{
	double f = rEntry.nTicks * nLoop;
	if (f == 0.0)
	{
		return 0.0;
	}
	else
	{
		return (((double)(nTicks * rEntry.nLoop)) / f);
	}
}

//==================================================================================================
typedef std::map< std::string, TimeEntry > t_TimeEntryMap;

//==================================================================================================
struct TimingSheet
{
	t_TimeEntryMap		_entries;
	void insert( const sal_Char * pText, sal_Int64 nLoop, sal_uInt32 nTicks );
};
//__________________________________________________________________________________________________
void TimingSheet::insert( const sal_Char * pText, sal_Int64 nLoop, sal_uInt32 nTicks )
{
	_entries[ pText ] = TimeEntry( nLoop, nTicks );
}

//==================================================================================================
typedef std::hash_map< std::string, TimingSheet > t_TimingSheetMap;

//--------------------------------------------------------------------------------------------------
static void benchmark(
	TimingSheet & rSheet, const Reference< XInterface > & xInstance, sal_Int64 nLoop )
	throw (Exception)
{
	Reference< XPerformanceTest > xBench( xInstance, UNO_QUERY );
	if (! xBench.is())
		throw RuntimeException( OUString( RTL_CONSTASCII_USTRINGPARAM("illegal test object!") ), Reference< XInterface >() );

	sal_Int64 i;
	sal_uInt32 tStart, tEnd;

	const Type & rKnownType = ::getCppuType( (const Reference< XPerformanceTest > *)0 );
	const Type & rUnKnownType = ::getCppuType( (const Reference< XSet > *)0 );

	ComplexTypes aDummyStruct;

	//------------------------------------
	// oneway calls
	i = nLoop;
	tStart = getSystemTicks();
	while (i--)
		xBench->async();
	sal_uInt32 tEndSend = getSystemTicks();
	xBench->sync();
	tEnd = getSystemTicks();
	rSheet.insert( "1a: sending simple oneway calls (no params, no return)", nLoop, tEndSend - tStart );
	rSheet.insert( "1b: simple oneway calls (no params, no return)", nLoop, tEnd - tStart );
	// synchron calls
	i = nLoop;
	tStart = getSystemTicks();
	while (i--)
		xBench->sync();
	xBench->sync();
	tEnd = getSystemTicks();
	rSheet.insert( "1c: simple synchron calls (no params no return)", nLoop+1, tEnd - tStart );

	// acquire
	i = nLoop;
	tStart = getSystemTicks();
	while (i--)
		xBench->acquire();
	tEnd = getSystemTicks();
	rSheet.insert( "2a: interface acquire() calls", nLoop, tEnd - tStart );
	// release
	i = nLoop;
	tStart = getSystemTicks();
	while (i--)
		xBench->release();
	tEnd = getSystemTicks();
	rSheet.insert( "2b: interface release() calls", nLoop, tEnd - tStart );

	// queryInterface() for known type
	i = nLoop;
	tStart = getSystemTicks();
	while (i--)
		xBench->queryInterface( rKnownType );
	tEnd = getSystemTicks();
	rSheet.insert( "2c: interface query for implemented type", nLoop, tEnd - tStart );
	// queryInterface() for unknown type
	i = nLoop;
	tStart = getSystemTicks();
	while (i--)
		xBench->queryInterface( rUnKnownType );
	tEnd = getSystemTicks();
	rSheet.insert( "2d: interface query for unknown type", nLoop, tEnd - tStart );

	// create and forget objects
	Reference< XPerformanceTest > xBench2( xBench );
	i = nLoop;
	tStart = getSystemTicks();
	while (i--)
		xBench2 = xBench2->createObject();
	tEnd = getSystemTicks();
	rSheet.insert( "3a: create and release test objects", nLoop, tEnd - tStart );

	// hold new objects
	Sequence< Reference< XInterface > > aSeq( nLoop / 100 );
	Reference< XInterface > * pSeq = aSeq.getArray();
	xBench2 = xBench;
	i = aSeq.getLength();
	tStart = getSystemTicks();
	while (i--)
		pSeq[i] = xBench2 = xBench2->createObject();
	tEnd = getSystemTicks();
	rSheet.insert( "3b: create and hold test objects", nLoop, tEnd - tStart );

	// structs
	i = nLoop;
	tStart = getSystemTicks();
	while (i--)
		xBench->complex_in( aDummyStruct );
	tEnd = getSystemTicks();
	rSheet.insert( "4a: complex_in() calls (in struct; return struct)", nLoop, tEnd - tStart );
	i = nLoop;
	tStart = getSystemTicks();
	while (i--)
		xBench->complex_inout( aDummyStruct );
	tEnd = getSystemTicks();
	rSheet.insert( "4b: complex_inout() calls (inout struct; return struct)", nLoop, tEnd - tStart );

	i = nLoop;
	tStart = getSystemTicks();
	while (i--)
		xBench->complex_oneway( aDummyStruct );
	tEnd = getSystemTicks();
	rSheet.insert( "4c: complex_oneway() oneway calls (in struct)", nLoop, tEnd - tStart );
	i = nLoop;
	tStart = getSystemTicks();
	while (i--)
		xBench->complex_noreturn( aDummyStruct );
	tEnd = getSystemTicks();
	rSheet.insert( "4d: complex_noreturn() calls (in struct)", nLoop, tEnd - tStart );

	// attributes, get() methods
	i = nLoop;
	tStart = getSystemTicks();
	while (i--)
		xBench->getLong();
	tEnd = getSystemTicks();
	rSheet.insert( "5a: getLong() call", nLoop, tEnd - tStart );
	i = nLoop;
	tStart = getSystemTicks();
	while (i--)
		xBench->getLong_attr();
	tEnd = getSystemTicks();
	rSheet.insert( "5b: get long attribute", nLoop, tEnd - tStart );

	i = nLoop;
	tStart = getSystemTicks();
	while (i--)
		xBench->setLong( 0 );
	tEnd = getSystemTicks();
	rSheet.insert( "5c: setLong() call", nLoop, tEnd - tStart );
	i = nLoop;
	tStart = getSystemTicks();
	while (i--)
		xBench->setLong_attr( 0 );
	tEnd = getSystemTicks();
	rSheet.insert( "5d: set long attribute", nLoop, tEnd - tStart );

	i = nLoop;
	tStart = getSystemTicks();
	while (i--)
		xBench->getHyper();
	tEnd = getSystemTicks();
	rSheet.insert( "5e: getHyper() call", nLoop, tEnd - tStart );
	i = nLoop;
	tStart = getSystemTicks();
	while (i--)
		xBench->getHyper_attr();
	tEnd = getSystemTicks();
	rSheet.insert( "5f: get hyper attribute", nLoop, tEnd - tStart );

	i = nLoop;
	tStart = getSystemTicks();
	while (i--)
		xBench->setHyper( 0 );
	tEnd = getSystemTicks();
	rSheet.insert( "5g: setHyper() call", nLoop, tEnd - tStart );
	i = nLoop;
	tStart = getSystemTicks();
	while (i--)
		xBench->setHyper_attr( 0 );
	tEnd = getSystemTicks();
	rSheet.insert( "5h: set hyper attribute", nLoop, tEnd - tStart );

	i = nLoop;
	tStart = getSystemTicks();
	while (i--)
		xBench->getFloat();
	tEnd = getSystemTicks();
	rSheet.insert( "5i: getFloat() call", nLoop, tEnd - tStart );
	i = nLoop;
	tStart = getSystemTicks();
	while (i--)
		xBench->getFloat_attr();
	tEnd = getSystemTicks();
	rSheet.insert( "5j: get float attribute",nLoop,  tEnd - tStart );

	i = nLoop;
	tStart = getSystemTicks();
	while (i--)
		xBench->setFloat( 0.0 );
	tEnd = getSystemTicks();
	rSheet.insert( "5k: setFloat() call", nLoop, tEnd - tStart );
	i = nLoop;
	tStart = getSystemTicks();
	while (i--)
		xBench->setFloat_attr( 0.0 );
	tEnd = getSystemTicks();
	rSheet.insert( "5l: set float attribute", nLoop, tEnd - tStart );

	i = nLoop;
	tStart = getSystemTicks();
	while (i--)
		xBench->getDouble();
	tEnd = getSystemTicks();
	rSheet.insert( "5m: getDouble() call", nLoop, tEnd - tStart );
	i = nLoop;
	tStart = getSystemTicks();
	while (i--)
		xBench->getDouble_attr();
	tEnd = getSystemTicks();
	rSheet.insert( "5n: get double attribute", nLoop, tEnd - tStart );
	i = nLoop;
	tStart = getSystemTicks();
	while (i--)
		xBench->setDouble( 0.0 );
	tEnd = getSystemTicks();
	rSheet.insert( "5o: setDouble() call", nLoop, tEnd - tStart );
	i = nLoop;
	tStart = getSystemTicks();
	while (i--)
		xBench->setDouble_attr( 0.0 );
	tEnd = getSystemTicks();
	rSheet.insert( "5p: set double attribute", nLoop, tEnd - tStart );

	i = nLoop;
	tStart = getSystemTicks();
	while (i--)
		xBench->getString();
	tEnd = getSystemTicks();
	rSheet.insert( "6a: getString() call (empty)", nLoop, tEnd - tStart );
	i = nLoop;
	tStart = getSystemTicks();
	while (i--)
		xBench->getString_attr();
	tEnd = getSystemTicks();
	rSheet.insert( "6b: get empty string attribute", nLoop, tEnd - tStart );

	i = nLoop;
	OUString aDummyString;
	tStart = getSystemTicks();
	while (i--)
		xBench->setString( aDummyString );
	tEnd = getSystemTicks();
	rSheet.insert( "6c: setString() call (emtpy)", nLoop, tEnd - tStart );
	i = nLoop;
	tStart = getSystemTicks();
	while (i--)
		xBench->setString_attr( aDummyString );
	tEnd = getSystemTicks();
	rSheet.insert( "6d: set empty string attribute", nLoop, tEnd - tStart );

	i = nLoop;
	tStart = getSystemTicks();
	while (i--)
		xBench->getInterface();
	tEnd = getSystemTicks();
	rSheet.insert( "7a: getInterface() call (null)", nLoop, tEnd - tStart );
	i = nLoop;
	tStart = getSystemTicks();
	while (i--)
		xBench->getInterface_attr();
	tEnd = getSystemTicks();
	rSheet.insert( "7b: get interface attribute", nLoop, tEnd - tStart );

	i = nLoop;
	Reference< XInterface > aDummyInterface;
	tStart = getSystemTicks();
	while (i--)
		xBench->setInterface( aDummyInterface );
	tEnd = getSystemTicks();
	rSheet.insert( "7c: setInterface() call (null)", nLoop, tEnd - tStart );
	i = nLoop;
	tStart = getSystemTicks();
	while (i--)
		xBench->setInterface_attr( Reference< XInterface >() );
	tEnd = getSystemTicks();
	rSheet.insert( "7d: set interface attribute", nLoop, tEnd - tStart );

	i = nLoop;
	tStart = getSystemTicks();
	while (i--)
		xBench->getAny();
	tEnd = getSystemTicks();
	rSheet.insert( "8a: getAny() call (empty)", nLoop, tEnd - tStart );
	i = nLoop;
	tStart = getSystemTicks();
	while (i--)
		xBench->getAny_attr();
	tEnd = getSystemTicks();
	rSheet.insert( "8b: get empty any attribute", nLoop, tEnd - tStart );

	i = nLoop;
	Any aDummyAny;
	tStart = getSystemTicks();
	while (i--)
		xBench->setAny( aDummyAny );
	tEnd = getSystemTicks();
	rSheet.insert( "8c: setAny() call (empty)", nLoop, tEnd - tStart );
	i = nLoop;
	tStart = getSystemTicks();
	while (i--)
		xBench->setAny_attr( aDummyAny );
	tEnd = getSystemTicks();
	rSheet.insert( "8d: set empty any attribute", nLoop, tEnd - tStart );

	i = nLoop;
	tStart = getSystemTicks();
	while (i--)
		xBench->getSequence();
	tEnd = getSystemTicks();
	rSheet.insert( "9a: getSequence() call (empty)", nLoop, tEnd - tStart );
	i = nLoop;
	tStart = getSystemTicks();
	while (i--)
		xBench->getSequence_attr();
	tEnd = getSystemTicks();
	rSheet.insert( "9b: get empty sequence attribute", nLoop, tEnd - tStart );
	i = nLoop;
	Sequence< Reference< XInterface > > aDummySeq;
	tStart = getSystemTicks();
	while (i--)
		xBench->setSequence( aDummySeq );
	tEnd = getSystemTicks();
	rSheet.insert( "9c: setSequence() call (empty)", nLoop, tEnd - tStart );
	i = nLoop;
	tStart = getSystemTicks();
	while (i--)
		xBench->setSequence_attr( aDummySeq );
	tEnd = getSystemTicks();
	rSheet.insert( "9d: set empty sequence attribute", nLoop, tEnd - tStart );

	i = nLoop;
	tStart = getSystemTicks();
	while (i--)
		xBench->getStruct();
	tEnd = getSystemTicks();
	rSheet.insert( "Aa: getStruct() call", nLoop, tEnd - tStart );
	i = nLoop;
	tStart = getSystemTicks();
	while (i--)
		xBench->getStruct_attr();
	tEnd = getSystemTicks();
	rSheet.insert( "Ab: get struct attribute", nLoop, tEnd - tStart );

	i = nLoop;
	tStart = getSystemTicks();
	while (i--)
		xBench->setStruct( aDummyStruct );
	tEnd = getSystemTicks();
	rSheet.insert( "Ac: setStruct() call", nLoop, tEnd - tStart );
	i = nLoop;
	tStart = getSystemTicks();
	while (i--)
		xBench->setStruct_attr( aDummyStruct );
	tEnd = getSystemTicks();
	rSheet.insert( "Ad: set struct attribute", nLoop, tEnd - tStart );

	// load
//  	i = nLoop;
//  	tStart = getSystemTicks();
//  	while (i--)
//  		xBench->setSequence( aSeq );
//  	tEnd = getSystemTicks();
//  	rSheet.insert( "transfer of exisiting objects", nLoop, tEnd - tStart );

	// exceptions
	i = nLoop;
	tStart = getSystemTicks();
	while (i--)
	{
		try
		{
			xBench->raiseRuntimeException();
		}
		catch (RuntimeException &)
		{
		}
	}
	tEnd = getSystemTicks();
	rSheet.insert( "Ba: raising RuntimeException", nLoop, tEnd - tStart );

	//------------------------------------
}

//--------------------------------------------------------------------------------------------------
static OUString extractParam( const Sequence< OUString > & rArgs, const OUString & rParam )
{
	const OUString * pArgs = rArgs.getConstArray();
	for ( sal_Int32 nPos = rArgs.getLength(); nPos--; )
	{
		if (pArgs[nPos].indexOf( rParam ) == 0 &&
			pArgs[nPos].getLength() > (rParam.getLength()+1))
		{
			return pArgs[nPos].copy( rParam.getLength() +1 ); // XXX=bla
		}
	}
	return OUString();
}

const sal_Int32 nMagicNumberDirect = 34000;

//XMain
//__________________________________________________________________________________________________
sal_Int32 TestImpl::run( const Sequence< OUString > & rArgs )
	throw (RuntimeException)
{
	// defaults
	FILE * stream = stderr;
	sal_Int64 nLoop = NLOOP;
	OUString aArg( RTL_CONSTASCII_USTRINGPARAM("dms") );

	try
	{
		OUString aLoopStr( extractParam( rArgs, OUString( RTL_CONSTASCII_USTRINGPARAM("loop") ) ) );
		if (aLoopStr.getLength())
		{
			sal_Int64 n = aLoopStr.toInt64();
			if (n > 0)
				nLoop = n;
		}

		OUString aDurationStr( extractParam( rArgs , OUString( RTL_CONSTASCII_USTRINGPARAM("duration" ) ) ) );
		if( aDurationStr.getLength() )
		{
			sal_Int64 n = aDurationStr.toInt64();
			if( n >0 )
				nLoop = nMagicNumberDirect * n;
		}

		OUString aLogStr( extractParam( rArgs, OUString( RTL_CONSTASCII_USTRINGPARAM("log") ) ) );
		if (aLogStr.getLength())
		{
			if (aLogStr.compareToAscii( "stderr" ) == 0)
			{
				stream = stderr;
			}
			else if (aLogStr.compareToAscii( "stdout" ) == 0)
			{
				stream = stdout;
			}
			else
			{
				OString aFileName( OUStringToOString( aLogStr, RTL_TEXTENCODING_ASCII_US ) );
				stream = ::fopen( aFileName.getStr(), "w" );
				if (! stream)
				{
					OUStringBuffer buf( 32 );
					buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("cannot open file for writing: \"") );
					buf.append( aLogStr );
					buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("\"!") );
					throw RuntimeException( buf.makeStringAndClear(), Reference< XInterface >() );
				}
			}
		}

		OUString aArgStr( extractParam( rArgs, OUString( RTL_CONSTASCII_USTRINGPARAM("opt") ) ) );
		if (aArgStr.getLength())
		{
			aArg = aArgStr;
		}

		if (! rArgs.getLength())
			out( "\n> no options given, using defaults" );

		// params
		out( "\n> opt=" );
		out( aArg );
		out( " log=" );
		if (stream == stderr)
			out( "stderr" );
		else if (stream == stderr)
			out( "stdout loop=" );
		else
			out( aLogStr );
		out( " loop=" );
		out( nLoop );
		out( "\n" );
		t_TimingSheetMap aSheets;
		TimingSheet aDirect;

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

		if (aArg.indexOf( 'd' ) >= 0)
		{
			// in process direct test
			sal_uInt32 nStart = getSystemTicks();
			benchmark( aDirect, getDirect(), nLoop );
			sal_uInt32 nEnd = getSystemTicks();
			fprintf( stderr, "Duration (direct in process): %g s\n", (nEnd - nStart)/1000.  );
		}

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

		if (aArg.indexOf( 'm' ) >= 0)
		{
			// in process uno dispatch
			Environment aCppEnv, aAnoCppEnv;
			OUString aCurrentLanguageBindingName( RTL_CONSTASCII_USTRINGPARAM(CPPU_CURRENT_LANGUAGE_BINDING_NAME) );
			uno_getEnvironment( reinterpret_cast< uno_Environment ** >( &aCppEnv ),
								aCurrentLanguageBindingName.pData, 0 );
			// anonymous
			uno_createEnvironment( reinterpret_cast< uno_Environment ** >( &aAnoCppEnv ),
								   aCurrentLanguageBindingName.pData, 0 );

			// pseudo mapping uno<->uno: does nothing!
			Mapping aMapping( aCppEnv.get(), aAnoCppEnv.get(), OUString( RTL_CONSTASCII_USTRINGPARAM("pseudo") ) );
			if (! aMapping.is())
				throw RuntimeException( OUString( RTL_CONSTASCII_USTRINGPARAM("no pseudo mapping available!") ), Reference< XInterface >() );

			Reference< XInterface > xMapped;
			Reference< XInterface > xDirect( getDirect() );
			aMapping.mapInterface( reinterpret_cast< void ** >( &xMapped ), xDirect.get(),
								   ::getCppuType( &xDirect ) );
			if (! xMapped.is())
				throw RuntimeException( OUString( RTL_CONSTASCII_USTRINGPARAM("mapping object failed!") ), Reference< XInterface >() );

			sal_uInt32 nStart = getSystemTicks();
			benchmark( aSheets[ "mapped in process" ], xMapped, nLoop / 100 );
			sal_uInt32 nEnd = getSystemTicks();
			fprintf( stderr, "Duration (mapped in process): %g s\n", (nStart - nEnd)/1000. );
		}

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

		if (aArg.indexOf( 's' ) >= 0)
		{
			// start server process
			oslSecurity hSecurity = osl_getCurrentSecurity();
			if (! hSecurity)
				throw RuntimeException( OUString( RTL_CONSTASCII_USTRINGPARAM("cannot get current security handle!") ), Reference< XInterface >() );

			OUString aArgs[] = {
				OUString( RTL_CONSTASCII_USTRINGPARAM("-c") ),
				OUString( RTL_CONSTASCII_USTRINGPARAM("com.sun.star.comp.performance.PerformanceTestObject") ),
				OUString( RTL_CONSTASCII_USTRINGPARAM("-l") ),
#ifdef SAL_UNX
				OUString( RTL_CONSTASCII_USTRINGPARAM("libperfobj.so") ),
#else
				OUString( RTL_CONSTASCII_USTRINGPARAM("perfobj.dll") ),
#endif
				OUString( RTL_CONSTASCII_USTRINGPARAM("-r") ),
				OUString( RTL_CONSTASCII_USTRINGPARAM("applicat.rdb") ),
				OUString( RTL_CONSTASCII_USTRINGPARAM("-u") ),
				OUString( RTL_CONSTASCII_USTRINGPARAM("uno:socket,host=localhost,port=6000;iiop;TestRemoteObject") ),
				OUString( RTL_CONSTASCII_USTRINGPARAM("--singleaccept") )
			};
			rtl_uString * pArgs[] = {
				aArgs[0].pData,
				aArgs[1].pData,
				aArgs[2].pData,
				aArgs[3].pData,
				aArgs[4].pData,
				aArgs[5].pData,
				aArgs[6].pData,
				aArgs[7].pData,
				aArgs[8].pData,
				aArgs[9].pData,
			};

			out( "\n> executing: \"uno" );
			for ( sal_Int32 nPos = 0; nPos < (sizeof(aArgs) / sizeof(OUString)); ++nPos )
			{
				out( " " );
				out( aArgs[nPos] );
			}
			out( "\" ..." );

			oslProcess hProcess = 0;
			OUString aUnoExe( RTL_CONSTASCII_USTRINGPARAM("uno") );
			OUString aWorkingDir( RTL_CONSTASCII_USTRINGPARAM(".") );
			osl_executeProcess(
				aUnoExe.pData, pArgs, sizeof(aArgs) / sizeof(OUString),
				osl_Process_SEARCHPATH | osl_Process_DETACHED | osl_Process_NORMAL,
				hSecurity, aWorkingDir.pData, 0, 0, &hProcess );

			osl_freeSecurityHandle( hSecurity );
			if (! hProcess)
				throw RuntimeException( OUString( RTL_CONSTASCII_USTRINGPARAM("cannot start server process!") ), Reference< XInterface >() );
			osl_freeProcessHandle( hProcess );

			// wait three seconds
			TimeValue threeSeconds;
			threeSeconds.Seconds = 3;
			osl_waitThread( &threeSeconds );

			// connect and resolve outer process object
			Reference< XInterface > xResolvedObject( resolveObject( OUString(
				RTL_CONSTASCII_USTRINGPARAM("uno:socket,host=localhost,port=6000;iiop;TestRemoteObject") ) ) );

			benchmark( aSheets[ "remote same host" ], xResolvedObject, nLoop / 300 );
		}

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

		if (aArg.indexOf( 'r' ) >= 0)
		{
			// remote
			OUString aUnoUrl( extractParam( rArgs, OUString( RTL_CONSTASCII_USTRINGPARAM("url") ) ) );
			if (! aUnoUrl.getLength())
				throw RuntimeException( OUString( RTL_CONSTASCII_USTRINGPARAM("performance test r(emote) needs additional uno url!") ), Reference< XInterface >() );

			// connect and resolve outer process object
			Reference< XInterface > xResolvedObject( resolveObject( aUnoUrl ) );

			sal_Int32 t1 = getSystemTicks();
			OString o = OUStringToOString( aUnoUrl, RTL_TEXTENCODING_ASCII_US );
			benchmark( aSheets[ o.getStr() ], xResolvedObject, nLoop / 900 );
			sal_Int32 t2 = getSystemTicks();
			fprintf( stderr, "Duration (%s): %g s\n", o.getStr(),(t2 - t1)/1000. );
		}

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

		if (aArg.indexOf( 'j' ) >= 0)
		{
			// java
  			benchmark( aSheets[ "java in process" ],
					   _xSMgr->createInstance(OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.comp.benchmark.JavaTestObject"))),
					   nLoop / 1000 );
		}

		//------------------------------------------------------------------------------------------
		// dump out tables

		out( "\nTimes( ratio to direct in process )", stream );
#if OSL_DEBUG_LEVEL > 1
        out ("; compiled with OSL_DEBUG_LEVEL > 1", stream );
#endif
		out( ":", stream );

		sal_Int32 nPos = 60;
		out( "[direct in process]", stream, nPos );
		t_TimingSheetMap::const_iterator iSheets( aSheets.begin() );
		for ( ; iSheets != aSheets.end(); ++iSheets )
		{
			nPos += 40;
			out( "[", stream, nPos );
			out( (*iSheets).first.c_str(), stream );
			out( "]", stream );
		}
		for ( t_TimeEntryMap::const_iterator iTopics( aDirect._entries.begin() );
			  iTopics != aDirect._entries.end(); ++iTopics )
		{
			const std::string & rTopic = (*iTopics).first;

			out( "\n", stream );
			out( rTopic.c_str(), stream );

			out( ":", stream, 58, '.' );

			sal_Int32 nPos = 60;

			double secs = (*iTopics).second.secPerCall();
			if (secs > 0.0)
			{
				out( secs * 1000, stream, nPos );
				out( "ms", stream );
			}
			else
			{
				out( "NA", stream, nPos );
			}

			iSheets = aSheets.begin();
			for ( ; iSheets != aSheets.end(); ++iSheets )
			{
				const t_TimeEntryMap::const_iterator iFind( (*iSheets).second._entries.find( rTopic ) );
				OSL_ENSURE( iFind != (*iSheets).second._entries.end(), "####" );

				nPos += 40;

				double secs = (*iFind).second.secPerCall();
				if (secs != 0.0)
				{
					out( secs * 1000, stream, nPos );
					out( "ms", stream );

					out( " (", stream );
					double ratio = (*iFind).second.ratio( (*iTopics).second );
					if (ratio != 0.0)
					{
						out( ratio, stream );
						out( " x)", stream );
					}
					else
					{
						out( "NA)", stream );
					}
				}
				else
				{
					out( "NA", stream, nPos );
				}
			}
		}
	}
	catch (Exception & rExc)
	{
		if (stream != stderr && stream != stdout)
			::fclose( stream );
		throw RuntimeException( rExc.Message, rExc.Context );
	}

	if (stream != stderr && stream != stdout)
		::fclose( stream );

	out( "\n> done.\n" );
	return 0;
}

}


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


extern "C"
{
//==================================================================================================
void SAL_CALL component_getImplementationEnvironment(
	const sal_Char ** ppEnvTypeName, uno_Environment ** ppEnv )
{
	*ppEnvTypeName = CPPU_CURRENT_LANGUAGE_BINDING_NAME;
}
//==================================================================================================
sal_Bool SAL_CALL component_writeInfo(
	void * pServiceManager, void * pRegistryKey )
{
	if (pRegistryKey)
	{
		try
		{
			Reference< XRegistryKey > xNewKey(
				reinterpret_cast< XRegistryKey * >( pRegistryKey )->createKey(
					OUString( RTL_CONSTASCII_USTRINGPARAM("/" IMPLNAME "/UNO/SERVICES") ) ) );
			xNewKey->createKey( OUString( RTL_CONSTASCII_USTRINGPARAM(SERVICENAME) ) );

			return sal_True;
		}
		catch (InvalidRegistryException &)
		{
			OSL_ENSURE( sal_False, "### InvalidRegistryException!" );
		}
	}
	return sal_False;
}
//==================================================================================================
void * SAL_CALL component_getFactory(
	const sal_Char * pImplName, void * pServiceManager, void * pRegistryKey )
{
	void * pRet = 0;

	if (pServiceManager && rtl_str_compare( pImplName, IMPLNAME ) == 0)
	{
		Reference< XSingleServiceFactory > xFactory( createSingleFactory(
			reinterpret_cast< XMultiServiceFactory * >( pServiceManager ),
			OUString( RTL_CONSTASCII_USTRINGPARAM(IMPLNAME) ),
			benchmark_test::TestImpl_create,
			benchmark_test::getSupportedServiceNames() ) );

		if (xFactory.is())
		{
			xFactory->acquire();
			pRet = xFactory.get();
		}
	}

	return pRet;
}
}
