/**************************************************************
 * 
 * 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_stoc.hxx"
#include <cppuhelper/queryinterface.hxx>
#ifndef _CPPUHELPER_IMPLEMENTATIONENTRY_HXX_
#include <cppuhelper/implementationentry.hxx>
#endif

#include <com/sun/star/lang/XComponent.hpp>
#include <com/sun/star/reflection/XTypeDescription.hpp>
#include "com/sun/star/uno/RuntimeException.hpp"

using namespace com::sun::star;
using namespace com::sun::star::lang;
using namespace com::sun::star::registry;
using namespace cppu;
using namespace osl;
using namespace rtl;

#include "base.hxx"


namespace stoc_corefl
{

static const sal_Int32 CACHE_SIZE = 256;

#define SERVICENAME "com.sun.star.reflection.CoreReflection"
#define IMPLNAME	"com.sun.star.comp.stoc.CoreReflection"

// can be static, as every client of the core reflection keeps a reference to the
// core reflection, so refcounting can be done here.
static rtl_StandardModuleCount g_moduleCount = MODULE_COUNT_INIT;

static Sequence< OUString > core_getSupportedServiceNames()
{
	static Sequence < OUString > *pNames = 0;
	if( ! pNames )
	{
		MutexGuard guard( Mutex::getGlobalMutex() );
		if( !pNames )
		{
			static Sequence< OUString > seqNames(1);
			seqNames.getArray()[0] = OUString( RTL_CONSTASCII_USTRINGPARAM(SERVICENAME) );
			pNames = &seqNames;
		}
	}
	return *pNames;
}

static OUString core_getImplementationName()
{
	static OUString *pImplName = 0;
	if( ! pImplName )
	{
		MutexGuard guard( Mutex::getGlobalMutex() );
		if( ! pImplName )
		{
			static OUString implName( RTL_CONSTASCII_USTRINGPARAM( IMPLNAME ) );
			pImplName = &implName;
		}
	}
	return *pImplName;
}
//__________________________________________________________________________________________________
IdlReflectionServiceImpl::IdlReflectionServiceImpl(
    const Reference< XComponentContext > & xContext )
	: OComponentHelper( _aComponentMutex )
	, _xMgr( xContext->getServiceManager(), UNO_QUERY )
	, _aElements( CACHE_SIZE )
{
	g_moduleCount.modCnt.acquire( &g_moduleCount.modCnt );
    xContext->getValueByName( OUString( RTL_CONSTASCII_USTRINGPARAM(
        "/singletons/com.sun.star.reflection.theTypeDescriptionManager") ) ) >>= _xTDMgr;
	OSL_ENSURE( _xTDMgr.is(), "### cannot get singleton \"TypeDescriptionManager\" from context!" );
}
//__________________________________________________________________________________________________
IdlReflectionServiceImpl::~IdlReflectionServiceImpl()
{
	TRACE( "> IdlReflectionServiceImpl dtor <\n" );
	g_moduleCount.modCnt.release( &g_moduleCount.modCnt );
}

// XInterface
//__________________________________________________________________________________________________
Any IdlReflectionServiceImpl::queryInterface( const Type & rType )
	throw(::com::sun::star::uno::RuntimeException)
{
	Any aRet( ::cppu::queryInterface(
		rType,
		static_cast< XIdlReflection * >( this ),
		static_cast< XHierarchicalNameAccess * >( this ),
		static_cast< XServiceInfo * >( this ) ) );

	return (aRet.hasValue() ? aRet : OComponentHelper::queryInterface( rType ));
}
//__________________________________________________________________________________________________
void IdlReflectionServiceImpl::acquire() throw()
{
	OComponentHelper::acquire();
}
//__________________________________________________________________________________________________
void IdlReflectionServiceImpl::release() throw()
{
	OComponentHelper::release();
}

// XTypeProvider
//__________________________________________________________________________________________________
Sequence< Type > IdlReflectionServiceImpl::getTypes()
	throw (::com::sun::star::uno::RuntimeException)
{
	static OTypeCollection * s_pTypes = 0;
	if (! s_pTypes)
	{
		MutexGuard aGuard( _aComponentMutex );
		if (! s_pTypes)
		{
			static OTypeCollection s_aTypes(
				::getCppuType( (const Reference< XIdlReflection > *)0 ),
				::getCppuType( (const Reference< XHierarchicalNameAccess > *)0 ),
				::getCppuType( (const Reference< XServiceInfo > *)0 ),
				OComponentHelper::getTypes() );
			s_pTypes = &s_aTypes;
		}
	}
	return s_pTypes->getTypes();
}
//__________________________________________________________________________________________________
Sequence< sal_Int8 > IdlReflectionServiceImpl::getImplementationId()
	throw (::com::sun::star::uno::RuntimeException)
{
	static OImplementationId * s_pId = 0;
	if (! s_pId)
	{
		MutexGuard aGuard( _aComponentMutex );
		if (! s_pId)
		{
			static OImplementationId s_aId;
			s_pId = &s_aId;
		}
	}
	return s_pId->getImplementationId();
}

// XComponent
//__________________________________________________________________________________________________
void IdlReflectionServiceImpl::dispose()
	throw(::com::sun::star::uno::RuntimeException)
{
	TRACE( "> disposing corereflection... <" );
	OComponentHelper::dispose();

	MutexGuard aGuard( _aComponentMutex );
	_aElements.clear();
#ifdef TEST_LIST_CLASSES
	OSL_ENSURE( g_aClassNames.size() == 0, "### idl classes still alive!" );
	ClassNameList::const_iterator iPos( g_aClassNames.begin() );
	while (iPos != g_aClassNames.end())
	{
		OUString aName( *iPos );
		++iPos;
	}
#endif
}

// XServiceInfo
//__________________________________________________________________________________________________
OUString IdlReflectionServiceImpl::getImplementationName()
	throw(::com::sun::star::uno::RuntimeException)
{
	return core_getImplementationName();
}
//__________________________________________________________________________________________________
sal_Bool IdlReflectionServiceImpl::supportsService( const OUString & rServiceName )
	throw(::com::sun::star::uno::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 > IdlReflectionServiceImpl::getSupportedServiceNames()
	throw(::com::sun::star::uno::RuntimeException)
{
	return core_getSupportedServiceNames();
}

// XIdlReflection
//__________________________________________________________________________________________________
Reference< XIdlClass > IdlReflectionServiceImpl::getType( const Any & rObj )
	throw(::com::sun::star::uno::RuntimeException)
{
	return (rObj.hasValue() ? forType( rObj.getValueTypeRef() ) : Reference< XIdlClass >());
}

//__________________________________________________________________________________________________
inline Reference< XIdlClass > IdlReflectionServiceImpl::constructClass(
	typelib_TypeDescription * pTypeDescr )
{
	OSL_ENSURE( pTypeDescr->eTypeClass != typelib_TypeClass_TYPEDEF, "### unexpected typedef!" );

	switch (pTypeDescr->eTypeClass)
	{
	case typelib_TypeClass_VOID:
	case typelib_TypeClass_CHAR:
	case typelib_TypeClass_BOOLEAN:
	case typelib_TypeClass_BYTE:
	case typelib_TypeClass_SHORT:
	case typelib_TypeClass_UNSIGNED_SHORT:
	case typelib_TypeClass_LONG:
	case typelib_TypeClass_UNSIGNED_LONG:
	case typelib_TypeClass_HYPER:
	case typelib_TypeClass_UNSIGNED_HYPER:
	case typelib_TypeClass_FLOAT:
	case typelib_TypeClass_DOUBLE:
	case typelib_TypeClass_STRING:
	case typelib_TypeClass_ANY:
		return new IdlClassImpl( this, pTypeDescr->pTypeName, pTypeDescr->eTypeClass, pTypeDescr );

	case TypeClass_ENUM:
		return new EnumIdlClassImpl( this, pTypeDescr->pTypeName, pTypeDescr->eTypeClass, pTypeDescr );

	case typelib_TypeClass_STRUCT:
	case typelib_TypeClass_UNION:
	case typelib_TypeClass_EXCEPTION:
		return new CompoundIdlClassImpl( this, pTypeDescr->pTypeName, pTypeDescr->eTypeClass, pTypeDescr );

	case typelib_TypeClass_ARRAY:
	case typelib_TypeClass_SEQUENCE:
		return new ArrayIdlClassImpl( this, pTypeDescr->pTypeName, pTypeDescr->eTypeClass, pTypeDescr );

	case typelib_TypeClass_INTERFACE:
		return new InterfaceIdlClassImpl( this, pTypeDescr->pTypeName, pTypeDescr->eTypeClass, pTypeDescr );

	case typelib_TypeClass_TYPE:
		return new IdlClassImpl( this, pTypeDescr->pTypeName, pTypeDescr->eTypeClass, pTypeDescr );

	default:
#if OSL_DEBUG_LEVEL > 1
		OSL_TRACE( "### corereflection type unsupported: " );
		OString aName( OUStringToOString( pTypeDescr->pTypeName, RTL_TEXTENCODING_ASCII_US ) );
		OSL_TRACE( aName.getStr() );
		OSL_TRACE( "\n" );
#endif
        return Reference< XIdlClass >();
	}
}
//__________________________________________________________________________________________________
Reference< XIdlClass > IdlReflectionServiceImpl::forName( const OUString & rTypeName )
	throw(::com::sun::star::uno::RuntimeException)
{
	Reference< XIdlClass > xRet;
	Any aAny( _aElements.getValue( rTypeName ) );

	if (aAny.hasValue())
	{
		if (aAny.getValueTypeClass() == TypeClass_INTERFACE)
			xRet = *(const Reference< XIdlClass > *)aAny.getValue();
	}
	else
	{
		// try to get _type_ by name
		typelib_TypeDescription * pTD = 0;
		typelib_typedescription_getByName( &pTD, rTypeName.pData );
		if (pTD)
		{
			if ((xRet = constructClass( pTD )).is())
				_aElements.setValue( rTypeName, makeAny( xRet ) ); // update
			typelib_typedescription_release( pTD );
		}
	}

	return xRet;
}

// XHierarchicalNameAccess
//__________________________________________________________________________________________________
Any IdlReflectionServiceImpl::getByHierarchicalName( const OUString & rName )
	throw(::com::sun::star::container::NoSuchElementException, ::com::sun::star::uno::RuntimeException)
{
	Any aRet( _aElements.getValue( rName ) );
	if (! aRet.hasValue())
	{
		// first look for constants exclusivly!
		aRet = _xTDMgr->getByHierarchicalName( rName );
		if (aRet.getValueTypeClass() == TypeClass_INTERFACE) // if no constant,
															 // i.e. XTypeDescription for a type
		{
			// type retrieved from tdmgr
			OSL_ASSERT( (*(Reference< XInterface > *)aRet.getValue())->queryInterface(
				::getCppuType( (const Reference< XTypeDescription > *)0 ) ).hasValue() );

			// if you are interested in a type then CALL forName()!!!
			// this way is NOT recommended for types, because this method looks for constants first

			// if td manager found some type, it will be in the cache (hopefully.. we just got it)
			// so the second retrieving via c typelib callback chain should succeed...

			// try to get _type_ by name
			typelib_TypeDescription * pTD = 0;
			typelib_typedescription_getByName( &pTD, rName.pData );

			aRet.clear(); // kick XTypeDescription interface

			if (pTD)
			{
				Reference< XIdlClass > xIdlClass( constructClass( pTD ) );
				aRet.setValue( &xIdlClass, ::getCppuType( (const Reference< XIdlClass > *)0 ) );
				typelib_typedescription_release( pTD );
			}
		}
		// else is constant

		// update
		if (aRet.hasValue())
			_aElements.setValue( rName, aRet );
		else
		{
			throw NoSuchElementException( rName, Reference< XInterface >() );
		}
	}
	return aRet;
}
//__________________________________________________________________________________________________
sal_Bool IdlReflectionServiceImpl::hasByHierarchicalName( const OUString & rName )
	throw(::com::sun::star::uno::RuntimeException)
{
	try
	{
		return getByHierarchicalName( rName ).hasValue();
	}
	catch (NoSuchElementException &)
	{
	}
	return sal_False;
}

//__________________________________________________________________________________________________
Reference< XIdlClass > IdlReflectionServiceImpl::forType( typelib_TypeDescription * pTypeDescr )
	throw(::com::sun::star::uno::RuntimeException)
{
	Reference< XIdlClass > xRet;
	OUString aName( pTypeDescr->pTypeName );
	Any aAny( _aElements.getValue( aName ) );

	if (aAny.hasValue())
	{
		if (aAny.getValueTypeClass() == TypeClass_INTERFACE)
			xRet = *(const Reference< XIdlClass > *)aAny.getValue();
	}
	else
	{
		if (pTypeDescr && (xRet = constructClass( pTypeDescr )).is())
			_aElements.setValue( aName, makeAny( xRet ) ); // update
	}

	return xRet;
}
//__________________________________________________________________________________________________
Reference< XIdlClass > IdlReflectionServiceImpl::forType( typelib_TypeDescriptionReference * pRef )
	throw(::com::sun::star::uno::RuntimeException)
{
	typelib_TypeDescription * pTD = 0;
	TYPELIB_DANGER_GET( &pTD, pRef );
	if (pTD)
	{
		Reference< XIdlClass > xRet = forType( pTD );
		TYPELIB_DANGER_RELEASE( pTD );
		return xRet;
	}
	throw RuntimeException(
		OUString( RTL_CONSTASCII_USTRINGPARAM("IdlReflectionServiceImpl::forType() failed!") ),
		(XWeak *)(OWeakObject *)this );
}

//__________________________________________________________________________________________________
const Mapping & IdlReflectionServiceImpl::getCpp2Uno()
	throw(::com::sun::star::uno::RuntimeException)
{
	if (! _aCpp2Uno.is())
	{
		MutexGuard aGuard( getMutexAccess() );
		if (! _aCpp2Uno.is())
		{
			_aCpp2Uno = Mapping(
                OUString( RTL_CONSTASCII_USTRINGPARAM(CPPU_CURRENT_LANGUAGE_BINDING_NAME) ),
                OUString( RTL_CONSTASCII_USTRINGPARAM(UNO_LB_UNO) ) );
			OSL_ENSURE( _aCpp2Uno.is(), "### cannot get c++ to uno mapping!" );
			if (! _aCpp2Uno.is())
			{
				throw RuntimeException(
					OUString( RTL_CONSTASCII_USTRINGPARAM("cannot get c++ to uno mapping!") ),
					(XWeak *)(OWeakObject *)this );
			}
		}
	}
	return _aCpp2Uno;
}
//__________________________________________________________________________________________________
const Mapping & IdlReflectionServiceImpl::getUno2Cpp()
	throw(::com::sun::star::uno::RuntimeException)
{
	if (! _aUno2Cpp.is())
	{
		MutexGuard aGuard( getMutexAccess() );
		if (! _aUno2Cpp.is())
		{
			_aUno2Cpp = Mapping(
                OUString( RTL_CONSTASCII_USTRINGPARAM(UNO_LB_UNO) ),
                OUString( RTL_CONSTASCII_USTRINGPARAM(CPPU_CURRENT_LANGUAGE_BINDING_NAME) ) );
			OSL_ENSURE( _aUno2Cpp.is(), "### cannot get uno to c++ mapping!" );
			if (! _aUno2Cpp.is())
			{
				throw RuntimeException(
					OUString( RTL_CONSTASCII_USTRINGPARAM("cannot get uno to c++ mapping!") ),
					(XWeak *)(OWeakObject *)this );
			}
		}
	}
	return _aUno2Cpp;
}
//__________________________________________________________________________________________________
uno_Interface * IdlReflectionServiceImpl::mapToUno(
    const Any & rObj, typelib_InterfaceTypeDescription * pTo )
	throw(::com::sun::star::uno::RuntimeException)
{
	Reference< XInterface > xObj;
	if (extract( rObj, pTo, xObj, this ))
		return (uno_Interface *)getCpp2Uno().mapInterface( xObj.get(), pTo );

	throw RuntimeException(
		OUString( RTL_CONSTASCII_USTRINGPARAM("illegal object given!") ),
		(XWeak *)(OWeakObject *)this );
}

//==================================================================================================
Reference< XInterface > SAL_CALL IdlReflectionServiceImpl_create(
	const Reference< XComponentContext > & xContext )
	throw(::com::sun::star::uno::Exception)
{
	return Reference< XInterface >( (XWeak *)(OWeakObject *)new IdlReflectionServiceImpl( xContext ) );
}

}


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

using namespace stoc_corefl;

static struct ImplementationEntry g_entries[] =
{
	{
		IdlReflectionServiceImpl_create, core_getImplementationName,
		core_getSupportedServiceNames, createSingleComponentFactory,
		&g_moduleCount.modCnt , 0
	},
	{ 0, 0, 0, 0, 0, 0 }
};

extern "C"
{
sal_Bool SAL_CALL component_canUnload( TimeValue *pTime )
{
	return g_moduleCount.canUnload( &g_moduleCount , pTime );
}

//==================================================================================================
void SAL_CALL component_getImplementationEnvironment(
	const sal_Char ** ppEnvTypeName, uno_Environment ** )
{
	*ppEnvTypeName = CPPU_CURRENT_LANGUAGE_BINDING_NAME;
}
//==================================================================================================
void * SAL_CALL component_getFactory(
	const sal_Char * pImplName, void * pServiceManager, void * pRegistryKey )
{
	return component_getFactoryHelper( pImplName, pServiceManager, pRegistryKey , g_entries );
}
}