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

#ifdef __MINGW32__
#define INITGUID
#include <initguid.h>
#else
#include "ole2uno.hxx"
#include "unoconversionutilities.hxx"
#endif
#include "servprov.hxx"
#include "unoobjw.hxx"
#include "oleobjw.hxx"
#include <rtl/unload.h>

#include <tools/presys.h>
#define _WIN32_WINNT 0x0400

#if defined(_MSC_VER) && (_MSC_VER >= 1300)
#undef _DEBUG
#endif
#include <atlbase.h>
extern CComModule _Module;
#include <atlcom.h>
#include <tools/postsys.h>


using namespace std;
using namespace cppu;
using namespace rtl;
using namespace osl;
using namespace com::sun::star::lang;
using namespace com::sun::star::uno;
using namespace com::sun::star::bridge;
using namespace com::sun::star::bridge::ModelDependent;



namespace ole_adapter
{

#include <initguid.h>
// prior 5.2 ( src569 ver m)
// {3ECF78F0-B149-11D2-8EBE-00105AD848AF}
//DEFINE_GUID(OID_ServiceManager, 0x3ECF78F0, 0xB149, 0x11d2, 0x8E, 0xBE, 0x00, 0x10, 0x5A, 0xD8, 0x48, 0xAF);

#ifndef OWNGUID
// GUID used since 5.2 ( src569 m)
// {82154420-0FBF-11d4-8313-005004526AB4}
DEFINE_GUID(OID_ServiceManager, 0x82154420, 0xfbf, 0x11d4, 0x83, 0x13, 0x0, 0x50, 0x4, 0x52, 0x6a, 0xb4);
#else
// Alternative GUID
// {D9BB9D1D-BFA9-4357-9F11-9A2E9061F06E}
DEFINE_GUID(OID_ServiceManager, 0xd9bb9d1d, 0xbfa9, 0x4357, 0x9f, 0x11, 0x9a, 0x2e, 0x90, 0x61, 0xf0, 0x6e);
#endif

extern	rtl_StandardModuleCount globalModuleCount;

/*****************************************************************************

	class implementation ProviderOleWrapper_Impl

*****************************************************************************/

ProviderOleWrapper_Impl::ProviderOleWrapper_Impl(const Reference<XMultiServiceFactory>& smgr,
												 const Reference<XSingleServiceFactory>& xSFact, GUID* pGuid)
	: m_xSingleServiceFactory(xSFact),
	  m_smgr( smgr)
{
	m_guid = *pGuid;

	Reference<XInterface> xInt = smgr->createInstance(reinterpret_cast<const sal_Unicode*>(L"com.sun.star.bridge.oleautomation.BridgeSupplier"));

	if (xInt.is())
	{
		Any a= xInt->queryInterface( ::getCppuType( reinterpret_cast<
												  Reference< XBridgeSupplier2>* >(0)));
		a >>= m_bridgeSupplier;

	}
}

ProviderOleWrapper_Impl::~ProviderOleWrapper_Impl()
{
}

sal_Bool ProviderOleWrapper_Impl::registerClass()
{
	HRESULT hresult;

	o2u_attachCurrentThread();

	hresult = CoRegisterClassObject(
			m_guid,
			this,
			CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER,
			REGCLS_MULTIPLEUSE,
			&m_factoryHandle);

	return (hresult == NOERROR);
}

sal_Bool ProviderOleWrapper_Impl::deregisterClass()
{
	HRESULT hresult = CoRevokeClassObject(m_factoryHandle);

	return (hresult == NOERROR);
}

STDMETHODIMP ProviderOleWrapper_Impl::QueryInterface(REFIID riid, void FAR* FAR* ppv)
{
    if(IsEqualIID(riid, IID_IUnknown))
	{
		AddRef();
		*ppv = (IUnknown*) (IClassFactory*) this;
		return NOERROR;
    }
    else if (IsEqualIID(riid, IID_IClassFactory))
	{
		AddRef();
		*ppv = (IClassFactory*) this;
		return NOERROR;
	}

    *ppv = NULL;
    return ResultFromScode(E_NOINTERFACE);
}

STDMETHODIMP_(ULONG) ProviderOleWrapper_Impl::AddRef()
{
	return osl_incrementInterlockedCount( &m_refCount);
}

STDMETHODIMP_(ULONG) ProviderOleWrapper_Impl::Release()
{
    MutexGuard aGuard( Mutex::getGlobalMutex());
    ULONG refCount = --m_refCount;
	if (m_refCount == 0)
	{
		delete this;
	}

    return refCount;
}

STDMETHODIMP ProviderOleWrapper_Impl::CreateInstance(IUnknown FAR* punkOuter,
													 REFIID riid,
    												 void FAR* FAR* ppv)
{
	HRESULT ret = ResultFromScode(E_UNEXPECTED);
	punkOuter = NULL;

	Reference<XInterface> xInstance;

	if (m_xSingleServiceFactory.is())
	{
		xInstance = m_xSingleServiceFactory->createInstance();

		if (xInstance.is())
		{
			Any usrAny(&xInstance, getCppuType( & xInstance));

			sal_uInt8 arId[16];
			rtl_getGlobalProcessId( arId );
			Any oleAny = m_bridgeSupplier->createBridge(usrAny,
										Sequence<sal_Int8>((sal_Int8*)arId, 16),
										UNO,
										OLE);


			if (oleAny.getValueTypeClass() == getCppuType( (sal_uInt32 *)0).getTypeClass())
			{
				VARIANT* pVariant = *(VARIANT**)oleAny.getValue();

				if (pVariant->vt == VT_DISPATCH)
				{
		    		ret = pVariant->pdispVal->QueryInterface(riid, ppv);
				}

				VariantClear(pVariant);
				CoTaskMemFree(pVariant);
			}
		}
	}

	return ret;
}

STDMETHODIMP ProviderOleWrapper_Impl::LockServer(int /*fLock*/)
{
    return NOERROR;
}

/*****************************************************************************

	class implementation OneInstanceOleWrapper_Impl

*****************************************************************************/

OneInstanceOleWrapper_Impl::OneInstanceOleWrapper_Impl(  const Reference<XMultiServiceFactory>& smgr,
														 const Reference<XInterface>& xInst,
                                                         GUID* pGuid,
                                                         sal_Bool bAsApplication )
	: m_xInst(xInst), m_refCount(0),
	  m_smgr( smgr),
      m_factoryHandle( 0 ),
      m_bAsApplication( bAsApplication ),
      m_nApplRegHandle( 0 )
{
	m_guid = *pGuid;

	Reference<XInterface> xInt = m_smgr->createInstance(reinterpret_cast<const sal_Unicode*>(L"com.sun.star.bridge.oleautomation.BridgeSupplier"));

	if (xInt.is())
	{
		Any a= xInt->queryInterface( getCppuType(
			reinterpret_cast< Reference<XBridgeSupplier2>*>(0)));
		a >>= m_bridgeSupplier;
	}
}

OneInstanceOleWrapper_Impl::~OneInstanceOleWrapper_Impl()
{
}

sal_Bool OneInstanceOleWrapper_Impl::registerClass()
{
	HRESULT hresult;

	o2u_attachCurrentThread();

	hresult = CoRegisterClassObject(
			m_guid,
			this,
			CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER,
			REGCLS_MULTIPLEUSE,
			&m_factoryHandle);

    if ( hresult == NOERROR && m_bAsApplication )
        hresult = RegisterActiveObject( this, m_guid, ACTIVEOBJECT_WEAK, &m_nApplRegHandle );

	return (hresult == NOERROR);
}

sal_Bool OneInstanceOleWrapper_Impl::deregisterClass()
{
    HRESULT hresult1 = NOERROR;
    if ( m_bAsApplication )
        hresult1 = RevokeActiveObject( m_nApplRegHandle, NULL );

	HRESULT hresult2 = CoRevokeClassObject(m_factoryHandle);

	return (hresult1 == NOERROR && hresult2 == NOERROR);
}

STDMETHODIMP OneInstanceOleWrapper_Impl::QueryInterface(REFIID riid, void FAR* FAR* ppv)
{
    if(IsEqualIID(riid, IID_IUnknown))
	{
		AddRef();
		*ppv = (IUnknown*) (IClassFactory*) this;
		return NOERROR;
    }
    else if (IsEqualIID(riid, IID_IClassFactory))
	{
		AddRef();
		*ppv = (IClassFactory*) this;
		return NOERROR;
	}

    *ppv = NULL;
    return ResultFromScode(E_NOINTERFACE);
}

STDMETHODIMP_(ULONG) OneInstanceOleWrapper_Impl::AddRef()
{
	return osl_incrementInterlockedCount( &m_refCount);
}

STDMETHODIMP_(ULONG) OneInstanceOleWrapper_Impl::Release()
{
    MutexGuard oGuard( Mutex::getGlobalMutex());
	ULONG refCount = --m_refCount;
	if ( m_refCount == 0)
	{
		delete this;
	}

    return refCount;
}

STDMETHODIMP OneInstanceOleWrapper_Impl::CreateInstance(IUnknown FAR* punkOuter,
													 	REFIID riid,
    												 	void FAR* FAR* ppv)
{
	HRESULT ret = ResultFromScode(E_UNEXPECTED);
	punkOuter = NULL;

	if (m_xInst.is())
	{
		Any usrAny(&m_xInst, getCppuType( &m_xInst));
		sal_uInt8 arId[16];
		rtl_getGlobalProcessId( arId);
		Any oleAny = m_bridgeSupplier->createBridge(usrAny,
										Sequence<sal_Int8>( (sal_Int8*)arId, 16),
										UNO,
										OLE);


		if (oleAny.getValueTypeClass() == TypeClass_UNSIGNED_LONG)
		{
			VARIANT* pVariant = *(VARIANT**)oleAny.getValue();

			if ((pVariant->vt == VT_UNKNOWN) || (pVariant->vt == VT_DISPATCH))
			{
	    		ret = pVariant->punkVal->QueryInterface(riid, ppv);
			}

			VariantClear(pVariant);
			CoTaskMemFree(pVariant);
		}
	}

	return ret;
}

STDMETHODIMP OneInstanceOleWrapper_Impl::LockServer(int /*fLock*/)
{
    return NOERROR;
}


/*****************************************************************************

	class implementation OleConverter_Impl2

*****************************************************************************/

OleConverter_Impl2::OleConverter_Impl2( const Reference<XMultiServiceFactory> &smgr):
	UnoConversionUtilities<OleConverter_Impl2>( smgr)

{
	// library unloading support
	globalModuleCount.modCnt.acquire( &globalModuleCount.modCnt);
}

// The XMultiServiceFactory is later set by XInitialization
OleConverter_Impl2::OleConverter_Impl2( const  Reference<XMultiServiceFactory>& smgr, sal_uInt8 unoWrapperClass, sal_uInt8 comWrapperClass ):
	UnoConversionUtilities<OleConverter_Impl2>( smgr, unoWrapperClass, comWrapperClass  )

{
	//library unloading support
	globalModuleCount.modCnt.acquire( &globalModuleCount.modCnt);
}

OleConverter_Impl2::~OleConverter_Impl2()
{
	globalModuleCount.modCnt.release( &globalModuleCount.modCnt);
}

// XBridgeSupplier --------------------------------------------------------------
Any SAL_CALL OleConverter_Impl2::createBridge(const Any& modelDepObject,
									   const Sequence< sal_Int8 >& ProcessId,
									   sal_Int16 sourceModelType,
									   sal_Int16 destModelType)
									   throw (IllegalArgumentException,
									   			RuntimeException )
{
	Any ret;
	sal_uInt8 arId[16];
	rtl_getGlobalProcessId( arId );

	Sequence< sal_Int8 > seqProcessId( (sal_Int8*)arId, 16);

	if ( seqProcessId == ProcessId)
	{
		if (sourceModelType == UNO)
		{
			if (destModelType == UNO)
			{
				// same model -> copy value only
				ret = modelDepObject;
			}
			else if (destModelType == OLE)
			{
				// convert UNO any into variant
				VARIANT* pVariant = (VARIANT*) CoTaskMemAlloc(sizeof(VARIANT));
				VariantInit( pVariant);
                try
                {
                    anyToVariant( pVariant, modelDepObject);
                }
                catch(...)
				{
					CoTaskMemFree(pVariant);
					throw IllegalArgumentException();
				}
                ret.setValue((void*) &pVariant, getCppuType((sal_uInt32*)0));
			}
			else
				throw IllegalArgumentException();
		}
		else if (sourceModelType == OLE)
		{
			if (modelDepObject.getValueType() != getCppuType((sal_uInt32*)0))
			{
				throw IllegalArgumentException();
			}
			else if (destModelType == OLE)
			{
				// same model -> copy value only
				VARIANT* pVariant = (VARIANT*) CoTaskMemAlloc(sizeof(VARIANT));

				if (NOERROR != VariantCopy(pVariant, *(VARIANT**)modelDepObject.getValue()))
				{
					CoTaskMemFree(pVariant);
					throw(IllegalArgumentException());
				}
				else
				{
					ret.setValue((void*) &pVariant, getCppuType((sal_uInt32*)0));
				}
			}
			else if (destModelType == UNO)
			{
				// convert variant into UNO any
				VARIANT* pVariant = *(VARIANT**)modelDepObject.getValue();
				try
				{
					variantToAny(pVariant, ret);
				}
				catch (CannotConvertException & e)
				{
					throw IllegalArgumentException(
						e.Message, 0, -1);
				}
			}
			else
				throw IllegalArgumentException();

		}
		else
			throw IllegalArgumentException();
	}

	return ret;
}


// XInitialize ------------------------------------------------------------------------------
// the first argument is an XMultiServiceFactory if at all
void SAL_CALL OleConverter_Impl2::initialize( const Sequence< Any >& aArguments )
				throw(Exception, RuntimeException)
{
	if( aArguments.getLength() == 1 && aArguments[0].getValueTypeClass() == TypeClass_INTERFACE)
	{
		Reference < XInterface > xInt;
		aArguments[0] >>= xInt;
		Reference <XMultiServiceFactory> xMulti( xInt, UNO_QUERY);
		m_smgrRemote= xMulti;
	}
}

// UnoConversionUtilities -------------------------------------------------------------------
Reference< XInterface > OleConverter_Impl2::createUnoWrapperInstance()
{
	if( m_nUnoWrapperClass == INTERFACE_OLE_WRAPPER_IMPL)
	{
		Reference<XWeak> xWeak= static_cast<XWeak*>( new InterfaceOleWrapper_Impl(
								m_smgr, m_nUnoWrapperClass, m_nComWrapperClass));
		return Reference<XInterface>( xWeak, UNO_QUERY);
	}
	else if( m_nUnoWrapperClass == UNO_OBJECT_WRAPPER_REMOTE_OPT)
	{
		Reference<XWeak> xWeak= static_cast<XWeak*>( new UnoObjectWrapperRemoteOpt(
								m_smgr, m_nUnoWrapperClass, m_nComWrapperClass));
		return Reference<XInterface>( xWeak, UNO_QUERY);
	}
	else
		return Reference<XInterface>();
}

Reference< XInterface > OleConverter_Impl2::createComWrapperInstance()
{
	Reference<XWeak> xWeak= static_cast<XWeak*>( new IUnknownWrapper_Impl(
							m_smgr, m_nUnoWrapperClass, m_nComWrapperClass));
	return Reference<XInterface>( xWeak, UNO_QUERY);
}



/*****************************************************************************

	class implementation OleClient_Impl

*****************************************************************************/

OleClient_Impl::OleClient_Impl( const Reference<XMultiServiceFactory>& smgr):
	UnoConversionUtilities<OleClient_Impl>( smgr)
{
	// library unloading support
	globalModuleCount.modCnt.acquire( &globalModuleCount.modCnt);
	Reference<XInterface> xInt;// = m_smgr->createInstance(L"com.sun.star.bridge.OleBridgeSupplier2");

	if (xInt.is())
	{
		Any a= xInt->queryInterface(getCppuType(
			reinterpret_cast<Reference<XBridgeSupplier2>*>(0)));
		a >>= m_bridgeSupplier;
	}
}

OleClient_Impl::~OleClient_Impl()
{
	// library unloading support
	globalModuleCount.modCnt.release( &globalModuleCount.modCnt);
}

Sequence< OUString >	SAL_CALL OleClient_Impl::getAvailableServiceNames() throw( RuntimeException )
{
	Sequence< OUString > ret;

	return ret;
}


OUString OleClient_Impl::getImplementationName()
{
	return OUString(reinterpret_cast<const sal_Unicode*>(L"com.sun.star.comp.ole.OleClient"));
}

Reference<XInterface> SAL_CALL OleClient_Impl::createInstance(const OUString& ServiceSpecifier) throw (Exception, RuntimeException )
{
	Reference<XInterface> 	ret;
	HRESULT 		result;
	IUnknown* 		pUnknown = NULL;
	CLSID			classId;

	o2u_attachCurrentThread();

	result = CLSIDFromProgID(
  				reinterpret_cast<LPCWSTR>(ServiceSpecifier.getStr()),  	//Pointer to the ProgID
  				&classId);         				//Pointer to the CLSID


	if (result == NOERROR)
	{
		result = CoCreateInstance(
					  classId,     			//Class identifier (CLSID) of the object
					  NULL, 			   	//Pointer to whether object is or isn't part of an aggregate
					  CLSCTX_SERVER,  //Context for running executable code
					  IID_IUnknown,         //Reference to the identifier of the interface
					  (void**)&pUnknown);   //Address of output variable that receives
	                       				   	// the interface pointer requested in riid
	}

	if (pUnknown != NULL)
	{
		Any	any;
		CComVariant variant;

		V_VT(&variant) = VT_UNKNOWN;
		V_UNKNOWN(&variant)	= pUnknown;
		// AddRef for Variant
		pUnknown->AddRef();

		// When the object is wrapped, then its refcount is increased
		variantToAny(&variant, any);
        if (any.getValueTypeClass() == TypeClass_INTERFACE)
        {
            any >>= ret;
        }
		pUnknown->Release(); // CoCreateInstance
	}

	return ret;
}

Reference<XInterface> SAL_CALL OleClient_Impl::createInstanceWithArguments(const OUString& ServiceSpecifier, const Sequence< Any >& /*Arguments*/) throw (Exception, RuntimeException)
{
	return createInstance( ServiceSpecifier);
}

// UnoConversionUtilities -----------------------------------------------------------------------------
Reference< XInterface > OleClient_Impl::createUnoWrapperInstance()
{
	if( m_nUnoWrapperClass == INTERFACE_OLE_WRAPPER_IMPL)
	{
		Reference<XWeak> xWeak= static_cast<XWeak*>( new InterfaceOleWrapper_Impl(
								m_smgr, m_nUnoWrapperClass, m_nComWrapperClass));
		return Reference<XInterface>( xWeak, UNO_QUERY);
	}
	else if( m_nUnoWrapperClass == UNO_OBJECT_WRAPPER_REMOTE_OPT)
	{
		Reference<XWeak> xWeak= static_cast<XWeak*>( new UnoObjectWrapperRemoteOpt(
								m_smgr, m_nUnoWrapperClass, m_nComWrapperClass));
		return Reference<XInterface>( xWeak, UNO_QUERY);
	}
	else
		return Reference< XInterface>();
}
// UnoConversionUtilities -----------------------------------------------------------------------------
Reference< XInterface > OleClient_Impl::createComWrapperInstance( )
{
	Reference<XWeak> xWeak= static_cast<XWeak*>( new IUnknownWrapper_Impl(
							m_smgr, m_nUnoWrapperClass, m_nComWrapperClass));
	return Reference<XInterface>( xWeak, UNO_QUERY);
}



/*****************************************************************************

	class implementation OleServer_Impl

*****************************************************************************/

OleServer_Impl::OleServer_Impl( const Reference<XMultiServiceFactory>& smgr):
	m_smgr( smgr)
{
	//library unloading support
	globalModuleCount.modCnt.acquire( &globalModuleCount.modCnt);
	Reference<XInterface> xInt = m_smgr->createInstance(reinterpret_cast<const sal_Unicode*>(L"com.sun.star.bridge.oleautomation.BridgeSupplier"));

	if (xInt.is())
	{
		Any a= xInt->queryInterface( getCppuType(
			reinterpret_cast< Reference<XBridgeSupplier2>*>(0)));
		a >>= m_bridgeSupplier;
	}

#ifndef OWNGUID
	sal_Bool bOLERegister = sal_False;
#else
	sal_Bool bOLERegister = sal_True;
#endif	
	sal_Bool ret = provideInstance( m_smgr, (GUID*)&OID_ServiceManager, bOLERegister );
    (void)ret;
}

OleServer_Impl::~OleServer_Impl()
{
	while (!m_wrapperList.empty())
	{
		(*m_wrapperList.begin())->deregisterClass();
		(*m_wrapperList.begin())->Release();
		m_wrapperList.pop_front();
	}
	//library unloading support
	globalModuleCount.modCnt.release( &globalModuleCount.modCnt);
}
// XInterface --------------------------------------------------
Any SAL_CALL OleServer_Impl::queryInterface( const Type& aType ) throw(RuntimeException)
{
	Any a= ::cppu::queryInterface( aType, static_cast<XTypeProvider*>(this));
	if( a == Any())
		return OWeakObject::queryInterface( aType);
	else
		return a;
}
void SAL_CALL OleServer_Impl::acquire(  ) throw()
{
	OWeakObject::acquire();
}
void SAL_CALL OleServer_Impl::release(  ) throw ()
{
	OWeakObject::release();
}


// XTypeProvider --------------------------------------------------
Sequence< Type > SAL_CALL OleServer_Impl::getTypes( ) throw(RuntimeException)
{
	static OTypeCollection *pCollection = 0;
	if( ! pCollection )
	{
		MutexGuard guard( Mutex::getGlobalMutex() );
		if( ! pCollection )
		{
			static OTypeCollection collection(
				getCppuType(reinterpret_cast< Reference< XWeak>*>(0)),
				getCppuType(reinterpret_cast< Reference< XTypeProvider>*>(0)) );
			pCollection = &collection;
		}
	}
	return (*pCollection).getTypes();
}
Sequence< sal_Int8 > SAL_CALL OleServer_Impl::getImplementationId() throw(RuntimeException)
{
	static OImplementationId *pId = 0;
	if( ! pId )
	{
		MutexGuard guard( Mutex::getGlobalMutex() );
		if( ! pId )
		{
			static OImplementationId id( sal_False );
			pId = &id;
		}
	}
	return (*pId).getImplementationId();
}


sal_Bool OleServer_Impl::provideService(const Reference<XSingleServiceFactory>& xSFact, GUID* guid)
{
	IClassFactoryWrapper* pFac = new ProviderOleWrapper_Impl( m_smgr, xSFact, guid);

	pFac->AddRef();

	m_wrapperList.push_back(pFac);

	return pFac->registerClass();
}

sal_Bool OleServer_Impl::provideInstance(const Reference<XInterface>& xInst, GUID* guid, sal_Bool bAsApplication )
{
	IClassFactoryWrapper* pFac = new OneInstanceOleWrapper_Impl( m_smgr, xInst, guid, bAsApplication );

	pFac->AddRef();
	m_wrapperList.push_back(pFac);

	return pFac->registerClass();
}



} // end namespace