/**************************************************************
 * 
 * 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.
 * 
 *************************************************************/


//  #define TEST_LIST_CLASSES
//  #define TRACE(x) OSL_TRACE(x)
#define TRACE(x)

#include <osl/diagnose.h>
#include <osl/mutex.hxx>
#include <uno/mapping.hxx>
#include <uno/dispatcher.h>
#include <cppuhelper/weak.hxx>
#include <cppuhelper/factory.hxx>
#include <cppuhelper/component.hxx>
#include <cppuhelper/typeprovider.hxx>

#include "lrucache.hxx"

#ifdef TEST_LIST_CLASSES
#include <list>
#include <algorithm>
#endif
#include <hash_map>

#include <com/sun/star/uno/XComponentContext.hpp>
#include <com/sun/star/lang/XServiceInfo.hpp>
#include <com/sun/star/container/XHierarchicalNameAccess.hpp>

#include <com/sun/star/reflection/XIdlClass.hpp>
#include <com/sun/star/reflection/XIdlReflection.hpp>
#include <com/sun/star/reflection/XIdlField.hpp>
#include <com/sun/star/reflection/XIdlField2.hpp>
#include <com/sun/star/reflection/XIdlMethod.hpp>

using namespace std;
using namespace osl;
using namespace rtl;
using namespace cppu;
using namespace com::sun::star::uno;
using namespace com::sun::star::lang;
using namespace com::sun::star::reflection;
using namespace com::sun::star::container;


namespace stoc_corefl
{

#ifdef TEST_LIST_CLASSES
typedef list< OUString > ClassNameList;
extern ClassNameList g_aClassNames;
#endif

//--------------------------------------------------------------------------------------------------
Mutex & getMutexAccess();

//--------------------------------------------------------------------------------------------------
inline bool td_equals( typelib_TypeDescription * pTD, typelib_TypeDescriptionReference * pType )
{
	return (pTD->pWeakRef == pType ||
			(pTD->pTypeName->length == pType->pTypeName->length &&
			 rtl_ustr_compare( pTD->pTypeName->buffer, pType->pTypeName->buffer ) == 0));
}
//--------------------------------------------------------------------------------------------------
inline typelib_TypeDescription * getTypeByName( const OUString & rName )
{
	typelib_TypeDescription * pTypeDescr = 0;
	typelib_typedescription_getByName( &pTypeDescr, rName.pData );
	if (! pTypeDescr->bComplete)
		typelib_typedescription_complete( &pTypeDescr );
	return pTypeDescr;
}

typedef std::hash_map< OUString, WeakReference< XIdlField >,
	FctHashOUString, equal_to< OUString > > OUString2Field;
typedef std::hash_map< OUString, WeakReference< XIdlMethod >,
	FctHashOUString, equal_to< OUString > > OUString2Method;

//==================================================================================================
class IdlReflectionServiceImpl
	: public OComponentHelper
	, public XIdlReflection
	, public XHierarchicalNameAccess
	, public XServiceInfo
{
	Mutex									_aComponentMutex;
	Reference< XMultiServiceFactory >		_xMgr;
	Reference< XHierarchicalNameAccess >	_xTDMgr;
	
	// caching
	LRU_CacheAnyByOUString					_aElements;

    Mapping						_aCpp2Uno;
	Mapping						_aUno2Cpp;

	inline Reference< XIdlClass > constructClass( typelib_TypeDescription * pTypeDescr );
public:
	Reference< XHierarchicalNameAccess > getTDMgr() const
		{ return _xTDMgr; }
	Reference< XMultiServiceFactory > getSMgr() const
		{ return _xMgr; }

    const Mapping & getCpp2Uno() throw(::com::sun::star::uno::RuntimeException);
	const Mapping & getUno2Cpp() throw(::com::sun::star::uno::RuntimeException);
	uno_Interface * mapToUno( const Any & rObj, typelib_InterfaceTypeDescription * pTo ) throw(::com::sun::star::uno::RuntimeException);

	// ctor/ dtor
	IdlReflectionServiceImpl( const Reference< XComponentContext > & xContext );
	virtual ~IdlReflectionServiceImpl();
	
	// XInterface
	virtual Any SAL_CALL queryInterface( const Type & rType ) throw(::com::sun::star::uno::RuntimeException);
	virtual void SAL_CALL acquire() throw();
	virtual void SAL_CALL release() throw();
	
	// some XComponent part from OComponentHelper
	virtual void SAL_CALL dispose() throw(::com::sun::star::uno::RuntimeException);
	
	// XServiceInfo
	virtual OUString SAL_CALL getImplementationName() throw(::com::sun::star::uno::RuntimeException);
	virtual sal_Bool SAL_CALL supportsService( const OUString & rServiceName ) throw(::com::sun::star::uno::RuntimeException);
	virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() throw(::com::sun::star::uno::RuntimeException);
	
	// XTypeProvider
	virtual Sequence< Type > SAL_CALL getTypes() throw (::com::sun::star::uno::RuntimeException);
	virtual Sequence< sal_Int8 > SAL_CALL getImplementationId() throw (::com::sun::star::uno::RuntimeException);
	
	// XIdlReflection
	virtual Reference< XIdlClass > SAL_CALL forName( const OUString & rTypeName ) throw(::com::sun::star::uno::RuntimeException);
	virtual Reference< XIdlClass > SAL_CALL getType( const Any & rObj ) throw(::com::sun::star::uno::RuntimeException);
	
	// XHierarchicalNameAccess
	virtual Any SAL_CALL getByHierarchicalName( const OUString & rName ) throw(::com::sun::star::container::NoSuchElementException, ::com::sun::star::uno::RuntimeException);
	virtual sal_Bool SAL_CALL hasByHierarchicalName( const OUString & rName ) throw(::com::sun::star::uno::RuntimeException);
	
	Reference< XIdlClass > forType( typelib_TypeDescription * pTypeDescr ) throw(::com::sun::star::uno::RuntimeException);
	Reference< XIdlClass > forType( typelib_TypeDescriptionReference * pRef ) throw(::com::sun::star::uno::RuntimeException);
};

//==================================================================================================
class IdlClassImpl
	: public WeakImplHelper1< XIdlClass >
{
	IdlReflectionServiceImpl *	_pReflection;
	
	OUString					_aName;
	TypeClass					_eTypeClass;
	
	typelib_TypeDescription *	_pTypeDescr;
	
public:
	typelib_TypeDescription *	getTypeDescr() const
		{ return _pTypeDescr; }
	IdlReflectionServiceImpl *	getReflection() const
		{ return _pReflection; }
	Reference< XMultiServiceFactory > getSMgr() const
		{ return _pReflection->getSMgr(); }
	Reference< XHierarchicalNameAccess > getTDMgr() const
		{ return getReflection()->getTDMgr(); }
	
	// Ctor
	IdlClassImpl( IdlReflectionServiceImpl * pReflection,
				  const OUString & rName, typelib_TypeClass eTypeClass,
				  typelib_TypeDescription * pTypeDescr );
	virtual ~IdlClassImpl();
	
	// XIdlClassImpl default implementation
    virtual TypeClass SAL_CALL getTypeClass() throw(::com::sun::star::uno::RuntimeException);
    virtual OUString SAL_CALL getName() throw(::com::sun::star::uno::RuntimeException);
    virtual sal_Bool SAL_CALL equals( const Reference< XIdlClass >& xType ) throw(::com::sun::star::uno::RuntimeException);
	
    virtual sal_Bool SAL_CALL isAssignableFrom( const Reference< XIdlClass > & xType ) throw(::com::sun::star::uno::RuntimeException);
    virtual void SAL_CALL createObject( Any & rObj ) throw(::com::sun::star::uno::RuntimeException);
	
	// def impl ????
    virtual Sequence< Reference< XIdlClass > > SAL_CALL getClasses() throw(::com::sun::star::uno::RuntimeException);
    virtual Reference< XIdlClass > SAL_CALL getClass( const OUString & rName ) throw(::com::sun::star::uno::RuntimeException);
    virtual Sequence< Reference< XIdlClass > > SAL_CALL getInterfaces() throw(::com::sun::star::uno::RuntimeException);
	
	// structs, interfaces
    virtual Sequence< Reference< XIdlClass > > SAL_CALL getSuperclasses() throw(::com::sun::star::uno::RuntimeException);
	// structs
    virtual Reference< XIdlField > SAL_CALL getField( const OUString & rName ) throw(::com::sun::star::uno::RuntimeException);
    virtual Sequence< Reference< XIdlField > > SAL_CALL getFields() throw(::com::sun::star::uno::RuntimeException);
	// interfaces
    virtual Uik SAL_CALL getUik() throw(::com::sun::star::uno::RuntimeException);
    virtual Reference< XIdlMethod > SAL_CALL getMethod( const OUString & rName ) throw(::com::sun::star::uno::RuntimeException);
    virtual Sequence< Reference< XIdlMethod > > SAL_CALL getMethods() throw(::com::sun::star::uno::RuntimeException);
	// array
    virtual Reference< XIdlClass > SAL_CALL getComponentType() throw(::com::sun::star::uno::RuntimeException);
    virtual Reference< XIdlArray > SAL_CALL getArray() throw(::com::sun::star::uno::RuntimeException);
};

//==================================================================================================
class InterfaceIdlClassImpl
	: public IdlClassImpl
{
	typedef pair< OUString, typelib_TypeDescription * > MemberInit;
	
	Sequence< Reference< XIdlClass > >		_xSuperClasses;
	
	MemberInit *							_pSortedMemberInit; // first methods, then attributes
	OUString2Field							_aName2Field;
	OUString2Method							_aName2Method;
	sal_Int32								_nMethods;
	sal_Int32								_nAttributes;
	
	void initMembers();
	
public:
	typelib_InterfaceTypeDescription * getTypeDescr() const
		{ return (typelib_InterfaceTypeDescription *)IdlClassImpl::getTypeDescr(); }
	
	// ctor/ dtor
	InterfaceIdlClassImpl( IdlReflectionServiceImpl * pReflection,
						   const OUString & rName, typelib_TypeClass eTypeClass,
						   typelib_TypeDescription * pTypeDescr )
		: IdlClassImpl( pReflection, rName, eTypeClass, pTypeDescr )
		, _pSortedMemberInit( 0 )
		, _nMethods( 0 )
		, _nAttributes( 0 )
		{}
	virtual ~InterfaceIdlClassImpl();
	
	// IdlClassImpl modifications
    virtual sal_Bool SAL_CALL isAssignableFrom( const Reference< XIdlClass > & xType ) throw(::com::sun::star::uno::RuntimeException);
    virtual Sequence< Reference< XIdlClass > > SAL_CALL getSuperclasses() throw(::com::sun::star::uno::RuntimeException);
    virtual Uik SAL_CALL getUik() throw(::com::sun::star::uno::RuntimeException);
    virtual Reference< XIdlMethod > SAL_CALL getMethod( const OUString & rName ) throw(::com::sun::star::uno::RuntimeException);
    virtual Sequence< Reference< XIdlMethod > > SAL_CALL getMethods() throw(::com::sun::star::uno::RuntimeException);
    virtual Reference< XIdlField > SAL_CALL getField( const OUString & rName ) throw(::com::sun::star::uno::RuntimeException);
    virtual Sequence< Reference< XIdlField > > SAL_CALL getFields() throw(::com::sun::star::uno::RuntimeException);
    virtual void SAL_CALL createObject( Any & rObj ) throw(::com::sun::star::uno::RuntimeException);
};

//==================================================================================================
class CompoundIdlClassImpl
	: public IdlClassImpl
{
	Reference< XIdlClass >					_xSuperClass;
	
	Sequence< Reference< XIdlField > > *	_pFields;
	OUString2Field							_aName2Field;
	
public:
	typelib_CompoundTypeDescription * getTypeDescr() const
		{ return (typelib_CompoundTypeDescription *)IdlClassImpl::getTypeDescr(); }
	
	// ctor/ dtor
	CompoundIdlClassImpl( IdlReflectionServiceImpl * pReflection,
						  const OUString & rName, typelib_TypeClass eTypeClass,
						  typelib_TypeDescription * pTypeDescr )
		: IdlClassImpl( pReflection, rName, eTypeClass, pTypeDescr )
		, _pFields( 0 )
		{}
	virtual ~CompoundIdlClassImpl();
	
	// IdlClassImpl modifications
    virtual sal_Bool SAL_CALL isAssignableFrom( const Reference< XIdlClass > & xType ) throw(::com::sun::star::uno::RuntimeException);
    virtual Sequence< Reference< XIdlClass > > SAL_CALL getSuperclasses() throw(::com::sun::star::uno::RuntimeException);
    virtual Reference< XIdlField > SAL_CALL getField( const OUString & rName ) throw(::com::sun::star::uno::RuntimeException);
    virtual Sequence< Reference< XIdlField > > SAL_CALL getFields() throw(::com::sun::star::uno::RuntimeException);
};

//==================================================================================================
class ArrayIdlClassImpl
	: public IdlClassImpl
	, public XIdlArray
{
public:
	typelib_IndirectTypeDescription * getTypeDescr() const
		{ return (typelib_IndirectTypeDescription *)IdlClassImpl::getTypeDescr(); }
	
	// ctor
	ArrayIdlClassImpl( IdlReflectionServiceImpl * pReflection,
					   const OUString & rName, typelib_TypeClass eTypeClass,
					   typelib_TypeDescription * pTypeDescr )
		: IdlClassImpl( pReflection, rName, eTypeClass, pTypeDescr )
		{}
	
	virtual Any SAL_CALL queryInterface( const Type & rType ) throw(::com::sun::star::uno::RuntimeException);
	virtual void SAL_CALL acquire() throw();
	virtual void SAL_CALL release() throw();
	
	// XTypeProvider
	virtual Sequence< Type > SAL_CALL getTypes() throw (::com::sun::star::uno::RuntimeException);
	virtual Sequence< sal_Int8 > SAL_CALL getImplementationId() throw (::com::sun::star::uno::RuntimeException);
	
	// IdlClassImpl modifications
    virtual sal_Bool SAL_CALL isAssignableFrom( const Reference< XIdlClass > & xType ) throw(::com::sun::star::uno::RuntimeException);
    virtual Reference< XIdlClass > SAL_CALL getComponentType() throw(::com::sun::star::uno::RuntimeException);
    virtual Reference< XIdlArray > SAL_CALL getArray() throw(::com::sun::star::uno::RuntimeException);
	
	// XIdlArray
    virtual void SAL_CALL realloc( Any & rArray, sal_Int32 nLen ) throw(::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException);
    virtual sal_Int32 SAL_CALL getLen( const Any & rArray ) throw(::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException);
    virtual Any SAL_CALL get( const Any & rArray, sal_Int32 nIndex ) throw(::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::lang::ArrayIndexOutOfBoundsException, ::com::sun::star::uno::RuntimeException);
    virtual void SAL_CALL set( Any & rArray, sal_Int32 nIndex, const Any & rNewValue ) throw(::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::lang::ArrayIndexOutOfBoundsException, ::com::sun::star::uno::RuntimeException);
};

//==================================================================================================
class EnumIdlClassImpl
	: public IdlClassImpl
{
	Sequence< Reference< XIdlField > > * _pFields;
	OUString2Field						 _aName2Field;
	
public:
	typelib_EnumTypeDescription * getTypeDescr() const
		{ return (typelib_EnumTypeDescription *)IdlClassImpl::getTypeDescr(); }
	
	// ctor/ dtor
	EnumIdlClassImpl( IdlReflectionServiceImpl * pReflection,
					  const OUString & rName, typelib_TypeClass eTypeClass,
					  typelib_TypeDescription * pTypeDescr )
		: IdlClassImpl( pReflection, rName, eTypeClass, pTypeDescr )
		, _pFields( 0 )
		{}
	virtual ~EnumIdlClassImpl();
	
	// IdlClassImpl modifications
    virtual Reference< XIdlField > SAL_CALL getField( const OUString & rName ) throw(::com::sun::star::uno::RuntimeException);
    virtual Sequence< Reference< XIdlField > > SAL_CALL getFields() throw(::com::sun::star::uno::RuntimeException);
    virtual void SAL_CALL createObject( Any & rObj ) throw(::com::sun::star::uno::RuntimeException);
};

//==================================================================================================
class IdlMemberImpl
	: public WeakImplHelper1< XIdlMember >
{
	IdlReflectionServiceImpl *	_pReflection;
	OUString					_aName;
	
	typelib_TypeDescription *	_pTypeDescr;
	typelib_TypeDescription *	_pDeclTypeDescr;
	
protected:
	Reference< XIdlClass >		_xDeclClass;
	
public:
	IdlReflectionServiceImpl *	getReflection() const
		{ return _pReflection; }
	Reference< XMultiServiceFactory > getSMgr() const
		{ return _pReflection->getSMgr(); }
	typelib_TypeDescription *	getTypeDescr() const
		{ return _pTypeDescr; }
	typelib_TypeDescription *	getDeclTypeDescr() const
		{ return _pDeclTypeDescr; }
	
	// ctor/ dtor
	IdlMemberImpl( IdlReflectionServiceImpl * pReflection, const OUString & rName,
				   typelib_TypeDescription * pTypeDescr, typelib_TypeDescription * pDeclTypeDescr );
	virtual ~IdlMemberImpl();
	
	// XIdlMember
    virtual Reference< XIdlClass > SAL_CALL getDeclaringClass() throw(::com::sun::star::uno::RuntimeException);
    virtual OUString SAL_CALL getName() throw(::com::sun::star::uno::RuntimeException);
};

//--------------------------------------------------------------------------------------------------
// coerces to type descr pTo else queries for it: the interface pointer is returned via rDest
// ## type to XidlClass coercion possible
inline sal_Bool extract(
	const Any & rObj, typelib_InterfaceTypeDescription * pTo,
	Reference< XInterface > & rDest,
	IdlReflectionServiceImpl * pRefl )
{
	rDest.clear();
	if (0 != pTo)
	{
        if (! rObj.hasValue())
            return sal_True;
		if (rObj.getValueTypeClass() == TypeClass_INTERFACE)
		{
			return ::uno_type_assignData(
				&rDest, ((typelib_TypeDescription *)pTo)->pWeakRef,
				const_cast< void * >( rObj.getValue() ), rObj.getValueTypeRef(),
				reinterpret_cast< uno_QueryInterfaceFunc >(cpp_queryInterface),
                reinterpret_cast< uno_AcquireFunc >(cpp_acquire),
                reinterpret_cast< uno_ReleaseFunc >(cpp_release) );
		}
		else if (rObj.getValueTypeClass() == TypeClass_TYPE)
		{
			rDest = pRefl->forType( reinterpret_cast< const Type * >( rObj.getValue() )->getTypeLibType() );
			return rDest.is();
		}
	}
	return sal_False;
}
//--------------------------------------------------------------------------------------------------
inline sal_Bool coerce_assign(
	void * pDest, typelib_TypeDescription * pTD, const Any & rSource,
	IdlReflectionServiceImpl * pRefl )
{
    if (pTD->eTypeClass == typelib_TypeClass_INTERFACE)
    {
        Reference< XInterface > xVal;
        if (extract( rSource, (typelib_InterfaceTypeDescription *)pTD, xVal, pRefl ))
        {
            if (*(XInterface **)pDest)
                (*(XInterface **)pDest)->release();
            *(XInterface **)pDest = xVal.get();
            if (*(XInterface **)pDest)
                (*(XInterface **)pDest)->acquire();
            return sal_True;
        }
        return sal_False;
    }
    else if (pTD->eTypeClass == typelib_TypeClass_ANY)
    {
        return uno_assignData(
            pDest, pTD,
            (void *)&rSource, pTD,
            reinterpret_cast< uno_QueryInterfaceFunc >(cpp_queryInterface),
            reinterpret_cast< uno_AcquireFunc >(cpp_acquire),
            reinterpret_cast< uno_ReleaseFunc >(cpp_release) );
    }
    else
    {
        return uno_type_assignData(
            pDest, pTD->pWeakRef,
            (void *)rSource.getValue(), rSource.getValueTypeRef(),
            reinterpret_cast< uno_QueryInterfaceFunc >(cpp_queryInterface),
            reinterpret_cast< uno_AcquireFunc >(cpp_acquire),
            reinterpret_cast< uno_ReleaseFunc >(cpp_release) );
    }
}

}


