/**************************************************************
 * 
 * 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 <osl/diagnose.h>
#include <rtl/ustrbuf.hxx>
#include "registry/reader.hxx"
#include "registry/version.h"

#include <com/sun/star/reflection/XInterfaceMemberTypeDescription.hpp>
#include <com/sun/star/reflection/XInterfaceAttributeTypeDescription2.hpp>
#include <com/sun/star/reflection/XInterfaceMethodTypeDescription.hpp>
#include <com/sun/star/reflection/XMethodParameter.hpp>
#include <com/sun/star/reflection/XParameter.hpp>
#include "com/sun/star/uno/RuntimeException.hpp"
#include "base.hxx"
#include "functiondescription.hxx"
#include "methoddescription.hxx"

#include <memory>
#include <set>

namespace stoc_rdbtdp
{

//==================================================================================================
class InterfaceMethodImpl : public WeakImplHelper1< XInterfaceMethodTypeDescription >
{
    stoc::registry_tdprovider::MethodDescription _desc;

	Reference< XHierarchicalNameAccess >  _xTDMgr;
	
	OUString							  _aTypeName;
	
	OUString							  _aReturnType;
	Reference< XTypeDescription >		  _xReturnTD;
	
	sal_Bool							  _bIsOneWay;
	sal_Int32							  _nPosition;
	
public:
	InterfaceMethodImpl( const Reference< XHierarchicalNameAccess > & xTDMgr,
						 const OUString & rTypeName,
						 const OUString & rMemberName,
						 const OUString & rReturnType,
						 const Sequence< sal_Int8 > & rBytes,
						 sal_uInt16 nMethodIndex,
						 sal_Bool bIsOneWay,
						 sal_Int32 nPosition )
		: _desc(xTDMgr, rMemberName, rBytes, nMethodIndex)
        , _xTDMgr( xTDMgr )
		, _aTypeName( rTypeName )
		, _aReturnType( rReturnType )
		, _bIsOneWay( bIsOneWay )
		, _nPosition( nPosition )
		{
			g_moduleCount.modCnt.acquire( &g_moduleCount.modCnt );
		}
	virtual ~InterfaceMethodImpl();
	
	// XTypeDescription
    virtual TypeClass SAL_CALL getTypeClass() throw(::com::sun::star::uno::RuntimeException);
    virtual OUString SAL_CALL getName() throw(::com::sun::star::uno::RuntimeException);
	
	// XInterfaceMemberTypeDescription
    virtual OUString SAL_CALL getMemberName() throw(::com::sun::star::uno::RuntimeException)
    { return _desc.getName(); }
    virtual sal_Int32 SAL_CALL getPosition() throw(::com::sun::star::uno::RuntimeException);

	// XInterfaceMethodTypeDescription
	virtual Reference< XTypeDescription > SAL_CALL getReturnType() throw(::com::sun::star::uno::RuntimeException);
    virtual sal_Bool SAL_CALL isOneway() throw(::com::sun::star::uno::RuntimeException);
    virtual Sequence< Reference< XMethodParameter > > SAL_CALL getParameters() throw(::com::sun::star::uno::RuntimeException);
    virtual Sequence< Reference< XTypeDescription > > SAL_CALL getExceptions() throw(::com::sun::star::uno::RuntimeException);
};
//__________________________________________________________________________________________________
InterfaceMethodImpl::~InterfaceMethodImpl()
{
	g_moduleCount.modCnt.release( &g_moduleCount.modCnt );
}

// XTypeDescription
//__________________________________________________________________________________________________
TypeClass InterfaceMethodImpl::getTypeClass()
	throw(::com::sun::star::uno::RuntimeException)
{
	return TypeClass_INTERFACE_METHOD;
}
//__________________________________________________________________________________________________
OUString InterfaceMethodImpl::getName()
	throw(::com::sun::star::uno::RuntimeException)
{
	return _aTypeName;
}

// XInterfaceMemberTypeDescription
//__________________________________________________________________________________________________
sal_Int32 InterfaceMethodImpl::getPosition()
	throw(::com::sun::star::uno::RuntimeException)
{
	return _nPosition;
}

// XInterfaceMethodTypeDescription
//__________________________________________________________________________________________________
Reference<XTypeDescription > InterfaceMethodImpl::getReturnType()
	throw(::com::sun::star::uno::RuntimeException)
{
	if (!_xReturnTD.is() && _aReturnType.getLength())
	{
        try
        {
            Reference< XTypeDescription > xReturnTD;
            if (_xTDMgr->getByHierarchicalName( _aReturnType ) >>= xReturnTD)
            {
                MutexGuard aGuard( getMutex() );
                if (! _xReturnTD.is())
                    _xReturnTD = xReturnTD;
                return _xReturnTD;
            }
        }
        catch (NoSuchElementException &)
        {
        }
        // never try again, if no td was found
        _aReturnType = OUString();
	}
	return _xReturnTD;
}
//__________________________________________________________________________________________________
sal_Bool InterfaceMethodImpl::isOneway()
	throw(::com::sun::star::uno::RuntimeException)
{
	return _bIsOneWay;
}
//__________________________________________________________________________________________________
Sequence<Reference<XMethodParameter > > InterfaceMethodImpl::getParameters()
	throw(::com::sun::star::uno::RuntimeException)
{
    Sequence< Reference< XParameter > > s1(_desc.getParameters());
    Sequence< Reference< XMethodParameter > > s2(s1.getLength());
    for (sal_Int32 i = 0; i < s1.getLength(); ++i) {
        s2[i] = s1[i].get();
    }
    return s2;
}
//__________________________________________________________________________________________________
Sequence<Reference<XTypeDescription > > InterfaceMethodImpl::getExceptions()
	throw(::com::sun::star::uno::RuntimeException)
{
    Sequence< Reference< XCompoundTypeDescription > > s1(
        _desc.getExceptions());
    Sequence< Reference< XTypeDescription > > s2(s1.getLength());
    for (sal_Int32 i = 0; i < s1.getLength(); ++i) {
        s2[i] = s1[i].get();
    }
    return s2;
}


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


//==================================================================================================
class InterfaceAttributeImpl : public WeakImplHelper1< XInterfaceAttributeTypeDescription2 >
{
	Reference< XHierarchicalNameAccess >  _xTDMgr;
	
	OUString							  _aTypeName;
	OUString							  _aMemberName;
	
	OUString							  _aMemberTypeName;
	Reference< XTypeDescription >		  _xMemberTD;
	
	sal_Bool							  _bReadOnly;
	sal_Bool							  _bBound;
	sal_Int32							  _nPosition;

    std::auto_ptr< stoc::registry_tdprovider::FunctionDescription > _getter;
    std::auto_ptr< stoc::registry_tdprovider::FunctionDescription > _setter;
	
public:
	InterfaceAttributeImpl(
        const Reference< XHierarchicalNameAccess > & xTDMgr,
        const OUString & rTypeName,
        const OUString & rMemberName,
        const OUString & rMemberTypeName,
        sal_Bool bReadOnly,
        sal_Bool bBound,
        std::auto_ptr< stoc::registry_tdprovider::FunctionDescription > &
            getter,
        std::auto_ptr< stoc::registry_tdprovider::FunctionDescription > &
            setter,
        sal_Int32 nPosition )
		: _xTDMgr( xTDMgr )
		, _aTypeName( rTypeName )
		, _aMemberName( rMemberName )
		, _aMemberTypeName( rMemberTypeName )
		, _bReadOnly( bReadOnly )
		, _bBound( bBound )
		, _nPosition( nPosition )
        , _getter( getter )
        , _setter( setter )
		{
			g_moduleCount.modCnt.acquire( &g_moduleCount.modCnt );
		}
	virtual ~InterfaceAttributeImpl();
	
	// XTypeDescription
    virtual TypeClass SAL_CALL getTypeClass() throw(::com::sun::star::uno::RuntimeException);
    virtual OUString SAL_CALL getName() throw(::com::sun::star::uno::RuntimeException);
	
	// XInterfaceMemberTypeDescription
    virtual OUString SAL_CALL getMemberName() throw(::com::sun::star::uno::RuntimeException);
    virtual sal_Int32 SAL_CALL getPosition() throw(::com::sun::star::uno::RuntimeException);
	
	// XInterfaceAttributeTypeDescription2
	virtual sal_Bool SAL_CALL isReadOnly() throw(::com::sun::star::uno::RuntimeException);
	virtual Reference< XTypeDescription > SAL_CALL getType() throw(::com::sun::star::uno::RuntimeException);

    virtual sal_Bool SAL_CALL isBound() throw (RuntimeException)
    { return _bBound; }

    virtual Sequence< Reference< XCompoundTypeDescription > > SAL_CALL
    getGetExceptions() throw (RuntimeException)
    {
        if (_getter.get() != 0) {
            return _getter->getExceptions();
        } else {
            return Sequence< Reference< XCompoundTypeDescription > >();
        }
    }

    virtual Sequence< Reference< XCompoundTypeDescription > > SAL_CALL
    getSetExceptions() throw (RuntimeException)
    {
        if (_setter.get() != 0) {
            return _setter->getExceptions();
        } else {
            return Sequence< Reference< XCompoundTypeDescription > >();
        }
    }
};

InterfaceAttributeImpl::~InterfaceAttributeImpl()
{
	g_moduleCount.modCnt.release( &g_moduleCount.modCnt );
}
// XTypeDescription
//__________________________________________________________________________________________________
TypeClass InterfaceAttributeImpl::getTypeClass()
	throw(::com::sun::star::uno::RuntimeException)
{
	return TypeClass_INTERFACE_ATTRIBUTE;
}
//__________________________________________________________________________________________________
OUString InterfaceAttributeImpl::getName()
	throw(::com::sun::star::uno::RuntimeException)
{
	return _aTypeName;
}

// XInterfaceMemberTypeDescription
//__________________________________________________________________________________________________
OUString InterfaceAttributeImpl::getMemberName()
	throw(::com::sun::star::uno::RuntimeException)
{
	return _aMemberName;
}
//__________________________________________________________________________________________________
sal_Int32 InterfaceAttributeImpl::getPosition()
	throw(::com::sun::star::uno::RuntimeException)
{
	return _nPosition;
}

// XInterfaceAttributeTypeDescription2
//__________________________________________________________________________________________________
sal_Bool InterfaceAttributeImpl::isReadOnly()
	throw(::com::sun::star::uno::RuntimeException)
{
	return _bReadOnly;
}
//__________________________________________________________________________________________________
Reference<XTypeDescription > InterfaceAttributeImpl::getType()
	throw(::com::sun::star::uno::RuntimeException)
{
	if (!_xMemberTD.is() && _aMemberTypeName.getLength())
	{
        try
        {
            Reference< XTypeDescription > xMemberTD;
            if (_xTDMgr->getByHierarchicalName( _aMemberTypeName ) >>= xMemberTD)
            {
                MutexGuard aGuard( getMutex() );
                if (! _xMemberTD.is())
                    _xMemberTD = xMemberTD;
                return _xMemberTD;
            }
        }
        catch (NoSuchElementException &)
        {
        }
        // never try again, if no td was found
        _aMemberTypeName = OUString();
    }
	return _xMemberTD;
}


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

void InterfaceTypeDescriptionImpl::checkInterfaceType(
    Reference< XTypeDescription > const & type)
{
    if (resolveTypedefs(type)->getTypeClass() != TypeClass_INTERFACE) {
        throw RuntimeException(
            OUString(
                RTL_CONSTASCII_USTRINGPARAM(
                    "Interface base is not an interface type")),
            static_cast< OWeakObject * >(this));
    }
}

namespace {

class BaseOffset {
public:
    BaseOffset(Reference< XInterfaceTypeDescription2 > const & desc);

    sal_Int32 get() const { return offset; }

private:
    void calculateBases(Reference< XInterfaceTypeDescription2 > const & desc);

    void calculate(Reference< XInterfaceTypeDescription2 > const & desc);

    std::set< rtl::OUString > set;
    sal_Int32 offset;
};

BaseOffset::BaseOffset(Reference< XInterfaceTypeDescription2 > const & desc) {
    offset = 0;
    calculateBases(desc);
}

void BaseOffset::calculateBases(
    Reference< XInterfaceTypeDescription2 > const & desc)
{
    Sequence< Reference < XTypeDescription > > bases(desc->getBaseTypes());
    for (sal_Int32 i = 0; i < bases.getLength(); ++i) {
        calculate(
            Reference< XInterfaceTypeDescription2 >(
                resolveTypedefs(bases[i]), UNO_QUERY_THROW));
    }
}

void BaseOffset::calculate(Reference< XInterfaceTypeDescription2 > const & desc)
{
    if (set.insert(desc->getName()).second) {
        calculateBases(desc);
        offset += desc->getMembers().getLength();
    }
}

}

//__________________________________________________________________________________________________
InterfaceTypeDescriptionImpl::InterfaceTypeDescriptionImpl(
	const Reference< XHierarchicalNameAccess > & xTDMgr,
	const OUString & rName, const Sequence< OUString > & rBaseTypes,
    const Sequence< OUString > & rOptionalBaseTypes,
	const Sequence< sal_Int8 > & rBytes, bool published )
	: _xTDMgr( xTDMgr )
	, _aBytes( rBytes )
	, _aName( rName )
	, _aBaseTypes( rBaseTypes )
	, _aOptionalBaseTypes( rOptionalBaseTypes )
	, _membersInit( false )
    , _published( published )
{
	g_moduleCount.modCnt.acquire( &g_moduleCount.modCnt );
}
//__________________________________________________________________________________________________
InterfaceTypeDescriptionImpl::~InterfaceTypeDescriptionImpl()
{
	g_moduleCount.modCnt.release( &g_moduleCount.modCnt );
}

// XTypeDescription
//__________________________________________________________________________________________________
TypeClass InterfaceTypeDescriptionImpl::getTypeClass()
	throw(::com::sun::star::uno::RuntimeException)
{
	return TypeClass_INTERFACE;
}
//__________________________________________________________________________________________________
OUString InterfaceTypeDescriptionImpl::getName()
	throw(::com::sun::star::uno::RuntimeException)
{
	return _aName;
}

// XInterfaceTypeDescription2
//__________________________________________________________________________________________________
Reference< XTypeDescription > InterfaceTypeDescriptionImpl::getBaseType()
	throw(::com::sun::star::uno::RuntimeException)
{
    Sequence< Reference< XTypeDescription > > aBaseTypes(getBaseTypes());
    return aBaseTypes.getLength() >= 1 ? aBaseTypes[0] : 0;
}
//__________________________________________________________________________________________________
Uik SAL_CALL InterfaceTypeDescriptionImpl::getUik()
	throw(::com::sun::star::uno::RuntimeException)
{
	return Uik();
}
//__________________________________________________________________________________________________
Sequence< Reference< XInterfaceMemberTypeDescription > > InterfaceTypeDescriptionImpl::getMembers()
	throw(::com::sun::star::uno::RuntimeException)
{
    osl::MutexGuard guard(getMutex());
    if (!_membersInit) {
        _nBaseOffset = BaseOffset(this).get();
        typereg::Reader reader(
            _aBytes.getConstArray(), _aBytes.getLength(), false,
            TYPEREG_VERSION_1);
        sal_Int32 count = 0;
        sal_uInt16 methodCount = reader.getMethodCount();
        {for (sal_uInt16 i = 0; i < methodCount; ++i) {
            RTMethodMode flags = reader.getMethodFlags(i);
            if (flags != RT_MODE_ATTRIBUTE_GET
                && flags != RT_MODE_ATTRIBUTE_SET)
            {
                ++count;
            }
        }}
        sal_uInt16 fieldCount = reader.getFieldCount();
        count += fieldCount;
        _members.realloc(count);
        sal_Int32 index = 0;
        {for (sal_uInt16 i = 0; i < fieldCount; ++i) {
            rtl::OUString name(reader.getFieldName(i));
            rtl::OUStringBuffer typeName(getName());
            typeName.appendAscii(RTL_CONSTASCII_STRINGPARAM("::"));
            typeName.append(name);
            RTFieldAccess flags = reader.getFieldFlags(i);
            std::auto_ptr< stoc::registry_tdprovider::FunctionDescription >
                getter;
            std::auto_ptr< stoc::registry_tdprovider::FunctionDescription >
                setter;
            for (sal_uInt16 j = 0; j < methodCount; ++j) {
                if (reader.getMethodName(j) == name) {
                    switch (reader.getMethodFlags(j)) {
                    case RT_MODE_ATTRIBUTE_GET:
                        OSL_ASSERT(getter.get() == 0);
                        getter.reset(
                            new stoc::registry_tdprovider::FunctionDescription(
                                _xTDMgr, _aBytes, j));
                        break;

                    case RT_MODE_ATTRIBUTE_SET:
                        OSL_ASSERT(setter.get() == 0);
                        setter.reset(
                            new stoc::registry_tdprovider::FunctionDescription(
                                _xTDMgr, _aBytes, j));
                        break;

                    default:
                        OSL_ASSERT(false);
                        break;
                    }
                }
            }
            _members[index] = new InterfaceAttributeImpl(
                _xTDMgr, typeName.makeStringAndClear(), name,
                reader.getFieldTypeName(i).replace('/', '.'),
                (flags & RT_ACCESS_READONLY) != 0,
                (flags & RT_ACCESS_BOUND) != 0, getter, setter,
                _nBaseOffset + index);
            ++index;
        }}
        {for (sal_uInt16 i = 0; i < methodCount; ++i) {
            RTMethodMode flags = reader.getMethodFlags(i);
            if (flags != RT_MODE_ATTRIBUTE_GET
                && flags != RT_MODE_ATTRIBUTE_SET)
            {
                rtl::OUString name(reader.getMethodName(i));
                rtl::OUStringBuffer typeName(getName());
                typeName.appendAscii(RTL_CONSTASCII_STRINGPARAM("::"));
                typeName.append(name);
                _members[index] = new InterfaceMethodImpl(
                    _xTDMgr, typeName.makeStringAndClear(), name,
                    reader.getMethodReturnTypeName(i).replace('/', '.'),
                    _aBytes, i, flags == RT_MODE_ONEWAY, _nBaseOffset + index);
                ++index;
            }
        }}
        _membersInit = true;
    }
    return _members;
}

Sequence< Reference< XTypeDescription > >
InterfaceTypeDescriptionImpl::getBaseTypes() throw (RuntimeException) {
    MutexGuard guard(getMutex());
    if (_xBaseTDs.getLength() == 0 && _aBaseTypes.getLength() != 0) {
        Sequence< Reference< XTypeDescription > > tds(_aBaseTypes.getLength());
        for (sal_Int32 i = 0; i < _aBaseTypes.getLength(); ++i) {
            try {
                _xTDMgr->getByHierarchicalName(_aBaseTypes[i]) >>= tds[i];
            } catch (NoSuchElementException & e) {
                throw RuntimeException(
                    (OUString(
                        RTL_CONSTASCII_USTRINGPARAM(
                            "com.sun.star.container.NoSuchElementException: "))
                     + e.Message),
                    static_cast< OWeakObject * >(this));
            }
            OSL_ASSERT(tds[i].is());
            checkInterfaceType(tds[i]);
        }
        _xBaseTDs = tds;
    }
    return _xBaseTDs;
}

Sequence< Reference< XTypeDescription > >
InterfaceTypeDescriptionImpl::getOptionalBaseTypes() throw (RuntimeException) {
    MutexGuard guard(getMutex());
    if (_xOptionalBaseTDs.getLength() == 0
        && _aOptionalBaseTypes.getLength() != 0)
    {
        Sequence< Reference< XTypeDescription > > tds(
            _aOptionalBaseTypes.getLength());
        for (sal_Int32 i = 0; i < _aOptionalBaseTypes.getLength(); ++i) {
            try {
                _xTDMgr->getByHierarchicalName(_aOptionalBaseTypes[i])
                    >>= tds[i];
            } catch (NoSuchElementException & e) {
                throw RuntimeException(
                    (OUString(
                        RTL_CONSTASCII_USTRINGPARAM(
                            "com.sun.star.container.NoSuchElementException: "))
                     + e.Message),
                    static_cast< OWeakObject * >(this));
            }
            OSL_ASSERT(tds[i].is());
            checkInterfaceType(tds[i]);
        }
        _xOptionalBaseTDs = tds;
    }
    return _xOptionalBaseTDs;
}

}