/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   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 .
 */

#include <comphelper/sequence.hxx>
#include <cppuhelper/queryinterface.hxx>
#include <cppuhelper/exc_hlp.hxx>
#include <cppuhelper/weak.hxx>
#include <cppuhelper/supportsservice.hxx>
#include <cppuhelper/implbase.hxx>

#include <com/sun/star/script/CannotConvertException.hpp>
#include <com/sun/star/script/XTypeConverter.hpp>
#include <com/sun/star/script/XInvocation.hpp>
#include <com/sun/star/script/XInvocation2.hpp>
#include <com/sun/star/reflection/XIdlReflection.hpp>
#include <com/sun/star/reflection/theCoreReflection.hpp>
#include <com/sun/star/container/XNameContainer.hpp>
#include <com/sun/star/container/XIndexContainer.hpp>
#include <com/sun/star/container/XEnumerationAccess.hpp>
#include <com/sun/star/beans/XExactName.hpp>
#include <com/sun/star/beans/XMaterialHolder.hpp>
#include <com/sun/star/beans/theIntrospection.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/beans/PropertyAttribute.hpp>
#include <com/sun/star/beans/MethodConcept.hpp>
#include <com/sun/star/beans/PropertyConcept.hpp>
#include <com/sun/star/lang/XSingleServiceFactory.hpp>
#include <com/sun/star/lang/XServiceInfo.hpp>
#include <com/sun/star/lang/XTypeProvider.hpp>

#include <memory>
#include <vector>

using namespace css::uno;
using namespace css::lang;
using namespace css::script;
using namespace css::reflection;
using namespace css::beans;
using namespace css::container;
using namespace cppu;
using namespace osl;

namespace stoc_inv
{


// TODO: Implement centrally
static Reference<XIdlClass> TypeToIdlClass( const Type& rType, const Reference< XIdlReflection > & xRefl )
{
    return xRefl->forName( rType.getTypeName() );
}

namespace {

class Invocation_Impl
    : public OWeakObject
    , public XInvocation2
    , public XNameContainer
    , public XIndexContainer
    , public XEnumerationAccess
    , public XExactName
    , public XMaterialHolder
    , public XTypeProvider
{
public:
    Invocation_Impl( const Any & rAdapted, const Reference<XTypeConverter> &,
                                           const Reference<XIntrospection> &,
                                           const Reference<XIdlReflection> &,
                                           bool bFromOLE );

    // XInterface
    virtual Any         SAL_CALL queryInterface( const Type & aType) override;
    virtual void        SAL_CALL acquire() noexcept override { OWeakObject::acquire(); }
    virtual void        SAL_CALL release() noexcept override { OWeakObject::release(); }


    // XTypeProvider
    virtual Sequence< css::uno::Type > SAL_CALL getTypes(  ) override;
    virtual Sequence< sal_Int8 > SAL_CALL getImplementationId(  ) override;

    // XMaterialHolder
    virtual Any         SAL_CALL getMaterial() override;

    // XInvocation
    virtual Reference<XIntrospectionAccess> SAL_CALL getIntrospection() override;
    virtual Any SAL_CALL invoke(const OUString& FunctionName, const Sequence< Any >& Params, Sequence< sal_Int16 >& OutParamIndex, Sequence< Any >& OutParam) override;
    virtual void SAL_CALL setValue(const OUString& PropertyName, const Any& Value) override;
    virtual Any SAL_CALL getValue(const OUString& PropertyName) override;
    virtual sal_Bool SAL_CALL hasMethod(const OUString& Name) override;
    virtual sal_Bool SAL_CALL hasProperty(const OUString& Name) override;

    // XInvocation2
    virtual Sequence< OUString > SAL_CALL getMemberNames(  ) override;
    virtual Sequence< InvocationInfo > SAL_CALL getInfo(  ) override;
    virtual InvocationInfo SAL_CALL getInfoForName( const OUString& aName, sal_Bool bExact ) override;

    // All Access and Container methods are not thread safe
    // XElementAccess
    virtual Type SAL_CALL getElementType() override
        { return _xElementAccess->getElementType(); }

    virtual sal_Bool SAL_CALL hasElements() override
        { return _xElementAccess->hasElements(); }

    // XNameContainer
    virtual void SAL_CALL insertByName( const OUString& Name, const Any& Element ) override
        { _xNameContainer->insertByName( Name, Element ); }

    virtual void SAL_CALL removeByName( const OUString& Name ) override
        { _xNameContainer->removeByName( Name ); }

    // XNameReplace
    virtual void SAL_CALL replaceByName( const OUString& Name, const Any& Element ) override
        { _xNameReplace->replaceByName( Name, Element ); }

    // XNameAccess
    virtual Any SAL_CALL getByName( const OUString& Name ) override
        { return _xNameAccess->getByName( Name ); }

    virtual Sequence<OUString> SAL_CALL getElementNames() override
        { return _xNameAccess->getElementNames(); }

    virtual sal_Bool SAL_CALL hasByName( const OUString& Name ) override
        { return _xNameAccess->hasByName( Name ); }

    // XIndexContainer
    virtual void SAL_CALL insertByIndex( sal_Int32 Index, const Any& Element ) override
        { _xIndexContainer->insertByIndex( Index, Element ); }

    virtual void SAL_CALL removeByIndex( sal_Int32 Index ) override
        { _xIndexContainer->removeByIndex( Index ); }

    // XIndexReplace
    virtual void SAL_CALL replaceByIndex( sal_Int32 Index, const Any& Element ) override
        { _xIndexReplace->replaceByIndex( Index, Element ); }

    // XIndexAccess
    virtual sal_Int32 SAL_CALL getCount() override
        { return _xIndexAccess->getCount(); }

    virtual Any SAL_CALL getByIndex( sal_Int32 Index ) override
        { return _xIndexAccess->getByIndex( Index ); }

    // XEnumerationAccess
    virtual Reference<XEnumeration> SAL_CALL createEnumeration() override
        { return _xEnumerationAccess->createEnumeration(); }

    // XExactName
    virtual OUString SAL_CALL getExactName( const OUString& rApproximateName ) override;


private:
    void setMaterial( const Any& rMaterial );

    void getInfoSequenceImpl( Sequence< OUString >* pStringSeq, Sequence< InvocationInfo >* pInfoSeq );
    void fillInfoForNameAccess( InvocationInfo& rInfo, const OUString& aName );
    static void fillInfoForProperty( InvocationInfo& rInfo, const Property& rProp );
    static void fillInfoForMethod( InvocationInfo& rInfo, const Reference< XIdlMethod >& xMethod );

    Reference<XTypeConverter>           xTypeConverter;
    Reference<XIntrospection>           xIntrospection;
    Reference<XIdlReflection>           xCoreReflection;

    Any                             _aMaterial;
    // _xDirect and (_xIntrospectionAccess, xPropertySet) are exclusive
    Reference<XInvocation>              _xDirect;
    Reference<XInvocation2>             _xDirect2;
    Reference<XPropertySet>             _xPropertySet;
    Reference<XIntrospectionAccess>     _xIntrospectionAccess;

    // supplied Interfaces
    Reference<XNameContainer>           _xNameContainer;
    Reference<XNameReplace>             _xNameReplace;
    Reference<XNameAccess>              _xNameAccess;
    Reference<XIndexContainer>          _xIndexContainer;
    Reference<XIndexReplace>            _xIndexReplace;
    Reference<XIndexAccess>             _xIndexAccess;
    Reference<XEnumerationAccess>       _xEnumerationAccess;
    Reference<XElementAccess>           _xElementAccess;


    Reference<XExactName>               _xENDirect, _xENIntrospection;

    bool                                mbFromOLE;
};

}

Invocation_Impl::Invocation_Impl
(
    const Any & rAdapted,
    const Reference<XTypeConverter> & rTC,
    const Reference<XIntrospection> & rI,
    const Reference<XIdlReflection> & rCR,
    bool bFromOLE
)
    : xTypeConverter( rTC )
    , xIntrospection( rI )
    , xCoreReflection( rCR )
    , mbFromOLE( bFromOLE )
{
    setMaterial( rAdapted );
}

//### INTERFACE IMPLEMENTATIONS ####################################################################


Any SAL_CALL Invocation_Impl::queryInterface( const Type & aType )
{
    // PropertySet implementation
    Any a = ::cppu::queryInterface( aType,
                                   static_cast< XInvocation* >(this),
                                   static_cast< XMaterialHolder* >(this),
                                   static_cast< XTypeProvider * >(this) );
    if( a.hasValue() )
    {
        return a;
    }

    if( aType  == cppu::UnoType<XExactName>::get())
    {
        // Invocation does not support XExactName, if direct object supports
        // XInvocation, but not XExactName. Except when called from OLE Automation.
        if (mbFromOLE ||
            (_xDirect.is() && _xENDirect.is()) ||
            (!_xDirect.is() && _xENIntrospection.is()))
        {
            return makeAny( Reference< XExactName >( static_cast< XExactName* >(this) ) );
        }
    }
    else if ( aType == cppu::UnoType<XNameContainer>::get())
    {
        if( _xNameContainer.is() )
            return makeAny( Reference< XNameContainer >( static_cast< XNameContainer* >(this) ) );
    }
    else if ( aType == cppu::UnoType<XNameReplace>::get())
    {
        if( _xNameReplace.is() )
            return makeAny( Reference< XNameReplace >( static_cast< XNameReplace* >(this) ) );
    }
    else if ( aType == cppu::UnoType<XNameAccess>::get())
    {
        if( _xNameAccess.is() )
            return makeAny( Reference< XNameAccess >( static_cast< XNameAccess* >(this) ) );
    }
    else if ( aType == cppu::UnoType<XIndexContainer>::get())
    {
        if (_xIndexContainer.is())
            return makeAny( Reference< XIndexContainer >( static_cast< XIndexContainer* >(this) ) );
    }
    else if ( aType == cppu::UnoType<XIndexReplace>::get())
    {
        if (_xIndexReplace.is())
            return makeAny( Reference< XIndexReplace >( static_cast< XIndexReplace* >(this) ) );
    }
    else if ( aType == cppu::UnoType<XIndexAccess>::get())
    {
        if (_xIndexAccess.is())
            return makeAny( Reference< XIndexAccess >( static_cast< XIndexAccess* >(this) ) );
    }
    else if ( aType == cppu::UnoType<XEnumerationAccess>::get())
    {
        if (_xEnumerationAccess.is())
            return makeAny( Reference< XEnumerationAccess >( static_cast< XEnumerationAccess* >(this) ) );
    }
    else if ( aType == cppu::UnoType<XElementAccess>::get())
    {
        if (_xElementAccess.is())
        {
            return makeAny( Reference< XElementAccess >(
                static_cast< XElementAccess* >(static_cast< XNameContainer* >(this)) ) );
        }
    }
    else if ( aType == cppu::UnoType<XInvocation2>::get())
    {
        // Invocation does not support XInvocation2, if direct object supports
        // XInvocation, but not XInvocation2.
        if ( mbFromOLE ||
             ( _xDirect.is() && _xDirect2.is()) ||
             (!_xDirect.is() && _xIntrospectionAccess.is() ) )
        {
            return makeAny( Reference< XInvocation2 >( static_cast< XInvocation2* >(this) ) );
        }
    }

    return OWeakObject::queryInterface( aType );
}


Any Invocation_Impl::getMaterial()
{
    // AB, 12.2.1999  Make sure that the material is taken when possible
    // from the direct Invocation of the Introspection, otherwise structs
    // are not handled correctly
    Reference<XMaterialHolder> xMaterialHolder;
    if( _xDirect.is() )
    {
        xMaterialHolder.set( _xDirect, UNO_QUERY );
        //_xDirect->queryInterface( XMaterialHolder::getSmartUik(), xMaterialHolder );
    }
    else if( _xIntrospectionAccess.is() )
    {
        xMaterialHolder.set( _xIntrospectionAccess, UNO_QUERY );
        //_xIntrospectionAccess->queryInterface( XMaterialHolder::getSmartUik(), xMaterialHolder );
    }
    if( xMaterialHolder.is() )
    {
        return xMaterialHolder->getMaterial();
    }
    return _aMaterial;
}


void Invocation_Impl::setMaterial( const Any& rMaterial )
{
    // set the material first and only once
    _aMaterial = rMaterial;

    // First do this outside the guard
    _xDirect.set( rMaterial, UNO_QUERY );

    if( !mbFromOLE && _xDirect.is() )
    {
        // Consult object directly
        _xElementAccess.set( _xDirect, UNO_QUERY );
        _xEnumerationAccess.set( _xDirect, UNO_QUERY );
        _xIndexAccess.set( _xDirect, UNO_QUERY );
        _xIndexReplace.set( _xDirect, UNO_QUERY );
        _xIndexContainer.set( _xDirect, UNO_QUERY );
        _xNameAccess.set( _xDirect, UNO_QUERY );
        _xNameReplace.set( _xDirect, UNO_QUERY );
        _xNameContainer.set( _xDirect, UNO_QUERY );
        _xENDirect.set( _xDirect, UNO_QUERY );
        _xDirect2.set( _xDirect, UNO_QUERY );
    }
    else
    {
        // Make Invocation on the Introspection
        if (xIntrospection.is())
        {
            _xIntrospectionAccess = xIntrospection->inspect( _aMaterial );
            if( _xIntrospectionAccess.is() )
            {
                _xElementAccess.set(
                      _xIntrospectionAccess->queryAdapter(
                                 cppu::UnoType<XElementAccess>::get()), UNO_QUERY );

                if( _xElementAccess.is() )
                {
                    _xEnumerationAccess.set(
                               _xIntrospectionAccess->queryAdapter(
                                    cppu::UnoType<XEnumerationAccess>::get()), UNO_QUERY );

                    _xIndexAccess.set(
                           _xIntrospectionAccess->queryAdapter(
                                    cppu::UnoType<XIndexAccess>::get()), UNO_QUERY );

                    if( _xIndexAccess.is() )
                    {
                        _xIndexReplace.set(
                             _xIntrospectionAccess->queryAdapter(
                                        cppu::UnoType<XIndexReplace>::get()), UNO_QUERY );

                        _xIndexContainer.set(
                             _xIntrospectionAccess->queryAdapter(
                                        cppu::UnoType<XIndexContainer>::get()), UNO_QUERY );
                    }

                    _xNameAccess.set(
                         _xIntrospectionAccess->queryAdapter(
                                    cppu::UnoType<XNameAccess>::get()), UNO_QUERY );

                    if( _xNameAccess.is() )
                    {
                        _xNameReplace.set(
                                   _xIntrospectionAccess->queryAdapter(
                                       cppu::UnoType<XNameReplace>::get()), UNO_QUERY );

                        _xNameContainer.set(
                                   _xIntrospectionAccess->queryAdapter(
                                       cppu::UnoType<XNameContainer>::get()), UNO_QUERY );
                    }
                }

                _xPropertySet.set( _xIntrospectionAccess->queryAdapter( cppu::UnoType<XPropertySet>::get()),
                                   UNO_QUERY );

                _xENIntrospection.set( _xIntrospectionAccess, UNO_QUERY );
            }
        }
    }
}


OUString Invocation_Impl::getExactName( const OUString& rApproximateName )
{
    if (_xENDirect.is())
        return _xENDirect->getExactName( rApproximateName );

    OUString aRet;
    if (_xENIntrospection.is())
        aRet = _xENIntrospection->getExactName( rApproximateName );
    return aRet;
}


Reference<XIntrospectionAccess> Invocation_Impl::getIntrospection()
{
    if( _xDirect.is() )
        return _xDirect->getIntrospection();
    else
        return _xIntrospectionAccess;
}


sal_Bool Invocation_Impl::hasMethod( const OUString& Name )
{
    if (!mbFromOLE && _xDirect.is())
        return _xDirect->hasMethod( Name );
    if( _xIntrospectionAccess.is() )
        return _xIntrospectionAccess->hasMethod( Name, MethodConcept::ALL ^ MethodConcept::DANGEROUS );
    return false;
}


sal_Bool Invocation_Impl::hasProperty( const OUString& Name )
{
    if (_xDirect.is())
    {
        bool bRet = _xDirect->hasProperty( Name );
        if (bRet || !mbFromOLE)
            return bRet;
    }
    // PropertySet
    if( _xIntrospectionAccess.is()
        && _xIntrospectionAccess->hasProperty( Name, PropertyConcept::ALL ^ PropertyConcept::DANGEROUS ) )
        return true;
    // NameAccess
    if( _xNameAccess.is() )
        return _xNameAccess->hasByName( Name );
    return false;
}


Any Invocation_Impl::getValue( const OUString& PropertyName )
{
    try
    {
        if (_xDirect.is())
            return _xDirect->getValue( PropertyName );
    }
    catch (Exception &)
    {
        if (!mbFromOLE)
            throw;
    }
    try
    {
        // PropertySet
        if( _xIntrospectionAccess.is() && _xPropertySet.is()
            && _xIntrospectionAccess->hasProperty
            ( PropertyName, PropertyConcept::ALL ^ PropertyConcept::DANGEROUS ) )
        {
            return _xPropertySet->getPropertyValue( PropertyName );
        }
        // NameAccess
        if( _xNameAccess.is() && _xNameAccess->hasByName( PropertyName ) )
            return _xNameAccess->getByName( PropertyName );
    }
    catch (UnknownPropertyException &)
    {
        throw;
    }
    catch (RuntimeException &)
    {
        throw;
    }
    catch (Exception &)
    {
    }

    throw UnknownPropertyException( "cannot get value " + PropertyName );
}


void Invocation_Impl::setValue( const OUString& PropertyName, const Any& Value )
{
    try
    {
        if (_xDirect.is())
        {
            _xDirect->setValue( PropertyName, Value );
            return;
        }
    }
    catch (Exception &)
    {
        if (!mbFromOLE)
            throw;
    }
    try
    {
        // Properties
        if( _xIntrospectionAccess.is() && _xPropertySet.is()
            && _xIntrospectionAccess->hasProperty(
                PropertyName, PropertyConcept::ALL ^ PropertyConcept::DANGEROUS ) )
        {
            Property aProp = _xIntrospectionAccess->getProperty(
                PropertyName, PropertyConcept::ALL ^ PropertyConcept::DANGEROUS );
            Reference < XIdlClass > r = TypeToIdlClass( aProp.Type, xCoreReflection );
            if( r->isAssignableFrom( TypeToIdlClass( Value.getValueType(), xCoreReflection ) ) )
                _xPropertySet->setPropertyValue( PropertyName, Value );
            else if( xTypeConverter.is() )
                _xPropertySet->setPropertyValue(
                    PropertyName, xTypeConverter->convertTo( Value, aProp.Type ) );
            else
                throw RuntimeException( "no type converter service!" );
        }
        // NameContainer
        else if( _xNameContainer.is() )
        {
            // Note: This misfeature deliberately not adapted to apply to objects which
            // have XNameReplace but not XNameContainer
            Any aConv;
            Reference < XIdlClass > r =
                TypeToIdlClass( _xNameContainer->getElementType(), xCoreReflection );
            if( r->isAssignableFrom(TypeToIdlClass( Value.getValueType(), xCoreReflection ) ) )
                aConv = Value;
            else if( xTypeConverter.is() )
                aConv = xTypeConverter->convertTo( Value, _xNameContainer->getElementType() );
            else
                throw RuntimeException( "no type converter service!" );

            // Replace if present, otherwise insert
            if (_xNameContainer->hasByName( PropertyName ))
                _xNameContainer->replaceByName( PropertyName, aConv );
            else
                _xNameContainer->insertByName( PropertyName, aConv );
        }
        else
            throw UnknownPropertyException( "no introspection nor name container!" );
    }
    catch (UnknownPropertyException &)
    {
        throw;
    }
    catch (CannotConvertException &)
    {
        throw;
    }
    catch (InvocationTargetException &)
    {
        throw;
    }
    catch (RuntimeException &)
    {
        throw;
    }
    catch (const Exception & exc)
    {
        css::uno::Any anyEx = cppu::getCaughtException();
        throw InvocationTargetException(
            "exception occurred in setValue(): " + exc.Message,
            Reference< XInterface >(), anyEx );
    }
}


Any Invocation_Impl::invoke( const OUString& FunctionName, const Sequence<Any>& InParams,
                                Sequence<sal_Int16>& OutIndices, Sequence<Any>& OutParams )
{
    if (!mbFromOLE && _xDirect.is())
        return _xDirect->invoke( FunctionName, InParams, OutIndices, OutParams );

    if (_xIntrospectionAccess.is())
    {
        // throw NoSuchMethodException if not exist
        Reference<XIdlMethod> xMethod = _xIntrospectionAccess->getMethod(
            FunctionName, MethodConcept::ALL ^ MethodConcept::DANGEROUS );

        // ParameterInfos
        Sequence<ParamInfo> aFParams        = xMethod->getParameterInfos();
        const ParamInfo* pFParams           = aFParams.getConstArray();
        sal_Int32 nFParamsLen               = aFParams.getLength();
        if (nFParamsLen != InParams.getLength())
        {
            throw IllegalArgumentException(
                "incorrect number of parameters passed invoking function " + FunctionName +
                ": expected " + OUString::number(nFParamsLen) + ", got " + OUString::number(InParams.getLength()),
                static_cast<OWeakObject *>(this), sal_Int16(1) );
        }

        // IN Parameter
        const Any* pInParams                = InParams.getConstArray();

        // Introspection Invoke Parameter
        Sequence<Any> aInvokeParams( nFParamsLen );
        Any* pInvokeParams                  = aInvokeParams.getArray();

        // OUT Indices
        OutIndices.realloc( nFParamsLen );
        sal_Int16* pOutIndices              = OutIndices.getArray();
        sal_uInt32 nOutIndex                = 0;

        for ( sal_Int32 nPos = 0; nPos < nFParamsLen; ++nPos )
        {
            try
            {
                const ParamInfo& rFParam = pFParams[nPos];
                const Reference<XIdlClass>& rDestType = rFParam.aType;

                // is IN/INOUT parameter?
                if (rFParam.aMode != ParamMode_OUT)
                {
                    if (rDestType->isAssignableFrom( TypeToIdlClass( pInParams[nPos].getValueType(), xCoreReflection ) ))
                    {
                        pInvokeParams[nPos] = pInParams[nPos];
                    }
                    else if (xTypeConverter.is())
                    {
                        Type aDestType( rDestType->getTypeClass(), rDestType->getName() );
                        pInvokeParams[nPos] = xTypeConverter->convertTo( pInParams[nPos], aDestType );
                    }
                    else
                    {
                        CannotConvertException aExc;
                        aExc.Context = *this;
                        aExc.Message = "invocation type mismatch!";
                        throw aExc;
                    }
                }

                // is OUT/INOUT parameter?
                if (rFParam.aMode != ParamMode_IN)
                {
                    pOutIndices[nOutIndex] = static_cast<sal_Int16>(nPos);
                    if (rFParam.aMode == ParamMode_OUT)
                        rDestType->createObject( pInvokeParams[nPos] );     // default init
                    ++nOutIndex;
                }
            }
            catch( CannotConvertException& rExc )
            {
                rExc.ArgumentIndex = nPos;  // Add optional parameter index
                throw;
            }
        }

        // execute Method
        Any aRet = xMethod->invoke( _aMaterial, aInvokeParams );

        // OUT Params
        OutIndices.realloc( nOutIndex );
        OutParams.realloc( nOutIndex );

        std::transform(std::cbegin(OutIndices), std::cend(OutIndices), OutParams.getArray(),
            [&pInvokeParams](const sal_Int16 nIndex) -> Any { return pInvokeParams[nIndex]; });

        return aRet;
    }

    RuntimeException aExc;
    aExc.Context = *this;
    aExc.Message = "invocation lacking of introspection access!";
    throw aExc;
}

namespace {

// Struct to optimize sorting
struct MemberItem
{
    OUString aName;

    // Defines where the member comes from
    enum class Mode { NameAccess, PropertySet, Method };
    Mode eMode;

    // Index to respective sequence
    // (Index to NameAccess sequence for eMode==Mode::NameAccess etc.)
    sal_Int32 nIndex;
};

}

// Implementation of getting name or info
// String sequence will be filled when pStringSeq != NULL
// Info sequence will be filled when pInfoSeq != NULL
void Invocation_Impl::getInfoSequenceImpl
(
    Sequence< OUString >* pStringSeq,
    Sequence< InvocationInfo >* pInfoSeq
)
{
    //Sequence< OUString > aStrSeq;
    //if( !pStringSeq )
        //pStringSeq = &aStrSeq;


    // Get all needed sequences
    Sequence<OUString> aNameAccessNames;
    Sequence<Property> aPropertySeq;
    Sequence< Reference< XIdlMethod > > aMethodSeq;

    if( _xNameAccess.is() )
    {
        aNameAccessNames = _xNameAccess->getElementNames();
    }

    if( _xIntrospectionAccess.is() )
    {
        aPropertySeq = _xIntrospectionAccess->getProperties
            ( PropertyConcept::ALL - PropertyConcept::DANGEROUS );

        aMethodSeq = _xIntrospectionAccess->getMethods
            ( MethodConcept::ALL - MethodConcept::DANGEROUS );
    }

    sal_Int32 nNameAccessCount = aNameAccessNames.getLength();
    sal_Int32 nPropertyCount = aPropertySeq.getLength();
    sal_Int32 nMethodCount = aMethodSeq.getLength();
    sal_Int32 nTotalCount = nNameAccessCount + nPropertyCount + nMethodCount;

    // Create and fill array of MemberItems
    std::unique_ptr< MemberItem []> pItems( new MemberItem[ nTotalCount ] );
    const OUString* pStrings = aNameAccessNames.getConstArray();
    const Property* pProps = aPropertySeq.getConstArray();
    const Reference< XIdlMethod >* pMethods = aMethodSeq.getConstArray();

    // Fill array of MemberItems
    sal_Int32 i, iTotal = 0;

    // Name Access
    for( i = 0 ; i < nNameAccessCount ; i++, iTotal++ )
    {
        MemberItem& rItem = pItems[ iTotal ];
        rItem.aName = pStrings[ i ];
        rItem.eMode = MemberItem::Mode::NameAccess;
        rItem.nIndex = i;
    }

    // Property set
    for( i = 0 ; i < nPropertyCount ; i++, iTotal++ )
    {
        MemberItem& rItem = pItems[ iTotal ];
        rItem.aName = pProps[ i ].Name;
        rItem.eMode = MemberItem::Mode::PropertySet;
        rItem.nIndex = i;
    }

    // Methods
    for( i = 0 ; i < nMethodCount ; i++, iTotal++ )
    {
        MemberItem& rItem = pItems[ iTotal ];
        Reference< XIdlMethod > xMethod = pMethods[ i ];
        rItem.aName = xMethod->getName();
        rItem.eMode = MemberItem::Mode::Method;
        rItem.nIndex = i;
    }

    // Setting up result sequences
    OUString* pRetStrings = nullptr;
    if( pStringSeq )
    {
        pStringSeq->realloc( nTotalCount );
        pRetStrings = pStringSeq->getArray();
    }

    InvocationInfo* pRetInfos = nullptr;
    if( pInfoSeq )
    {
        pInfoSeq->realloc( nTotalCount );
        pRetInfos = pInfoSeq->getArray();
    }

    // Fill result sequences in the correct order of members
    for( iTotal = 0 ; iTotal < nTotalCount ; iTotal++ )
    {
        MemberItem& rItem = pItems[ iTotal ];
        if( pRetStrings )
        {
            pRetStrings[ iTotal ] = rItem.aName;
        }

        if( pRetInfos )
        {
            if( rItem.eMode == MemberItem::Mode::NameAccess )
            {
                fillInfoForNameAccess( pRetInfos[ iTotal ], rItem.aName );
            }
            else if( rItem.eMode == MemberItem::Mode::PropertySet )
            {
                fillInfoForProperty( pRetInfos[ iTotal ], pProps[ rItem.nIndex ] );
            }
            else if( rItem.eMode == MemberItem::Mode::Method )
            {
                fillInfoForMethod( pRetInfos[ iTotal ], pMethods[ rItem.nIndex ] );
            }
        }
    }
}

// XInvocation2
Sequence< OUString > SAL_CALL Invocation_Impl::getMemberNames(  )
{
    if( _xDirect2.is() )
    {
        return _xDirect2->getMemberNames();
    }
    Sequence< OUString > aRetSeq;
    getInfoSequenceImpl( &aRetSeq, nullptr );
    return aRetSeq;
}

Sequence< InvocationInfo > SAL_CALL Invocation_Impl::getInfo(  )
{
    if( _xDirect2.is() )
    {
        return _xDirect2->getInfo();
    }
    Sequence< InvocationInfo > aRetSeq;
    getInfoSequenceImpl( nullptr, &aRetSeq );
    return aRetSeq;
}

InvocationInfo SAL_CALL Invocation_Impl::getInfoForName( const OUString& aName, sal_Bool bExact )
{
    if( _xDirect2.is() )
    {
        return _xDirect2->getInfoForName( aName, bExact );
    }

    bool bFound = false;
    OUString aExactName = aName;
    InvocationInfo aRetInfo;

    if( bExact )
        aExactName = getExactName( aName );
    if( !aExactName.isEmpty() )
    {
        if( _xIntrospectionAccess->hasMethod( aExactName, MethodConcept::ALL ^ MethodConcept::DANGEROUS ) )
        {
            Reference<XIdlMethod> xMethod = _xIntrospectionAccess->getMethod
                ( aExactName, MethodConcept::ALL ^ MethodConcept::DANGEROUS );
            fillInfoForMethod( aRetInfo, xMethod );
            bFound = true;
        }
        else
        {
            if( _xIntrospectionAccess.is() && _xIntrospectionAccess->hasProperty
                 ( aExactName, PropertyConcept::ALL ^ PropertyConcept::DANGEROUS ) )
            {
                Property aProp = _xIntrospectionAccess->getProperty
                    ( aExactName, PropertyConcept::ALL ^ PropertyConcept::DANGEROUS );
                fillInfoForProperty( aRetInfo, aProp );
                bFound = true;
            }
            // NameAccess
            else if( _xNameAccess.is() && _xNameAccess->hasByName( aExactName ) )
            {
                fillInfoForNameAccess( aRetInfo, aExactName );
                bFound = true;
            }
        }
    }
    if( !bFound )
    {
        throw IllegalArgumentException(
            "getExactName(), Unknown name " + aName,
            static_cast<XWeak *>(static_cast<OWeakObject *>(this)), 0 );
    }
    return aRetInfo;
}

// Helper functions to fill InvocationInfo for XNameAccess
void Invocation_Impl::fillInfoForNameAccess
(
    InvocationInfo& rInfo,
    const OUString& aName
)
{
    rInfo.aName = aName;
    rInfo.eMemberType = MemberType_PROPERTY;
    rInfo.PropertyAttribute = 0;
    if( !_xNameContainer.is() )
    {
        rInfo.PropertyAttribute = PropertyAttribute::READONLY;
    }
    rInfo.aType = _xNameAccess->getElementType();
}

void Invocation_Impl::fillInfoForProperty
(
    InvocationInfo& rInfo,
    const Property& rProp
)
{
    rInfo.aName = rProp.Name;
    rInfo.eMemberType = MemberType_PROPERTY;
    rInfo.PropertyAttribute = rProp.Attributes;
    rInfo.aType = rProp.Type;
}

void Invocation_Impl::fillInfoForMethod
(
    InvocationInfo& rInfo,
    const Reference< XIdlMethod >& xMethod
)
{
    rInfo.aName = xMethod->getName();
    rInfo.eMemberType = MemberType_METHOD;
    Reference< XIdlClass > xReturnClass = xMethod->getReturnType();
    Type aReturnType( xReturnClass->getTypeClass(), xReturnClass->getName() );
    rInfo.aType = aReturnType;
    Sequence<ParamInfo> aParamInfos = xMethod->getParameterInfos();
    sal_Int32 nParamCount = aParamInfos.getLength();
    if( nParamCount <= 0 )
        return;

    const ParamInfo* pInfo = aParamInfos.getConstArray();

    rInfo.aParamTypes.realloc( nParamCount );
    Type* pParamTypes = rInfo.aParamTypes.getArray();
    rInfo.aParamModes.realloc( nParamCount );
    ParamMode* pParamModes = rInfo.aParamModes.getArray();

    for( sal_Int32 i = 0 ; i < nParamCount ; i++ )
    {
        Reference< XIdlClass > xParamClass = pInfo[i].aType;
        Type aParamType( xParamClass->getTypeClass(), xParamClass->getName() );
        pParamTypes[ i ] = aParamType;
        pParamModes[ i ] = pInfo[i].aMode;
    }
}


// XTypeProvider
Sequence< Type > SAL_CALL Invocation_Impl::getTypes()
{
    static Sequence<Type> s_types = [this]() {
        std::vector<Type> tmp {
            cppu::UnoType<XTypeProvider>::get(),
            cppu::UnoType<XWeak>::get(),
            cppu::UnoType<XInvocation>::get(),
            cppu::UnoType<XMaterialHolder>::get() };

        // Invocation does not support XExactName if direct object supports
        // XInvocation, but not XExactName.
        if ((_xDirect.is() && _xENDirect.is()) || (!_xDirect.is() && _xENIntrospection.is()))
            tmp.push_back(cppu::UnoType<XExactName>::get());
        if (_xNameContainer.is())
            tmp.push_back(cppu::UnoType<XNameContainer>::get());
        if (_xNameReplace.is())
            tmp.push_back(cppu::UnoType<XNameReplace>::get());
        if (_xNameAccess.is())
            tmp.push_back(cppu::UnoType<XNameAccess>::get());
        if (_xIndexContainer.is())
            tmp.push_back(cppu::UnoType<XIndexContainer>::get());
        if (_xIndexReplace.is())
            tmp.push_back(cppu::UnoType<XIndexReplace>::get());
        if (_xIndexAccess.is())
            tmp.push_back(cppu::UnoType<XIndexAccess>::get());
        if (_xEnumerationAccess.is())
            tmp.push_back(cppu::UnoType<XEnumerationAccess>::get());
        if (_xElementAccess.is())
            tmp.push_back(cppu::UnoType<XElementAccess>::get());
        // Invocation does not support XInvocation2, if direct object supports
        // XInvocation, but not XInvocation2.
        if ((_xDirect.is() && _xDirect2.is()) || (!_xDirect.is() && _xIntrospectionAccess.is()))
            tmp.push_back(cppu::UnoType<XInvocation2>::get());

        return comphelper::containerToSequence(tmp);
    }();
    return s_types;
}

Sequence< sal_Int8 > SAL_CALL Invocation_Impl::getImplementationId(  )
{
    return css::uno::Sequence<sal_Int8>();
}

namespace {

class InvocationService
    : public WeakImplHelper< XSingleServiceFactory, XServiceInfo >
{
public:
    explicit InvocationService( const Reference<XComponentContext> & xCtx );

    // XServiceInfo
    OUString                    SAL_CALL getImplementationName() override;
    sal_Bool                        SAL_CALL supportsService(const OUString& ServiceName) override;
    Sequence< OUString >        SAL_CALL getSupportedServiceNames() override;

    // XSingleServiceFactory
    Reference<XInterface>       SAL_CALL createInstance() override;
    Reference<XInterface>       SAL_CALL createInstanceWithArguments(
        const Sequence<Any>& rArguments ) override;
private:
    Reference<XComponentContext> mxCtx;
    Reference<XMultiComponentFactory> mxSMgr;
    Reference<XTypeConverter> xTypeConverter;
    Reference<XIntrospection> xIntrospection;
    Reference<XIdlReflection> xCoreReflection;
};

}

InvocationService::InvocationService( const Reference<XComponentContext> & xCtx )
    : mxCtx( xCtx )
    , mxSMgr( xCtx->getServiceManager() )
    , xCoreReflection( css::reflection::theCoreReflection::get(mxCtx) )
{
    xTypeConverter.set(
        mxSMgr->createInstanceWithContext( "com.sun.star.script.Converter", xCtx ),
        UNO_QUERY );
    xIntrospection = theIntrospection::get(xCtx);
}

// XServiceInfo
OUString InvocationService::getImplementationName()
{
    return "com.sun.star.comp.stoc.Invocation";
}

// XServiceInfo
sal_Bool InvocationService::supportsService(const OUString& ServiceName)
{
    return cppu::supportsService(this, ServiceName);
}

// XServiceInfo
Sequence< OUString > InvocationService::getSupportedServiceNames()
{
    return { "com.sun.star.script.Invocation" };
}


Reference<XInterface> InvocationService::createInstance()
{
    //TODO:throw( Exception("no default construction of invocation adapter possible!", *this) );
    return Reference<XInterface>(); // dummy
}


Reference<XInterface> InvocationService::createInstanceWithArguments(
    const Sequence<Any>& rArguments )
{
    if (rArguments.getLength() == 2)
    {
        OUString aArg1;
        if ((rArguments[1] >>= aArg1) &&
            aArg1 == "FromOLE")
        {
            return Reference< XInterface >
                ( *new Invocation_Impl( *rArguments.getConstArray(),
                                        xTypeConverter, xIntrospection, xCoreReflection, true ) );
        }
    }
    if (rArguments.getLength() == 1)
    {
        return Reference< XInterface >
            ( *new Invocation_Impl( *rArguments.getConstArray(),
                                    xTypeConverter, xIntrospection, xCoreReflection, false ) );
    }

    //TODO:throw( Exception("no default construction of invocation adapter possible!", *this) );
    return Reference<XInterface>();
}

extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
stoc_InvocationService_get_implementation(
    css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
{
    return cppu::acquire(new InvocationService(context));
}

}


/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
