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



#include <stdio.h>
#include <inprocembobj.h>
#ifdef __MINGW32__
#define INITGUID
#endif
#include <embservconst.h>

static const GUID* guidList[ SUPPORTED_FACTORIES_NUM ] = {
	&OID_WriterTextServer,
	&OID_WriterOASISTextServer,
	&OID_CalcServer,
	&OID_CalcOASISServer,
	&OID_DrawingServer,
	&OID_DrawingOASISServer,
	&OID_PresentationServer,
	&OID_PresentationOASISServer,
	&OID_MathServer,
	&OID_MathOASISServer
};

static HINSTANCE g_hInstance = NULL;
static ULONG g_nObj = 0;
static ULONG g_nLock = 0;


namespace {
    void FillCharFromInt( int nValue, char* pBuf, int nLen )
    {
        int nInd = 0;
        while( nInd < nLen )
        {
            char nSign = ( nValue / ( 1 << ( ( nLen - nInd ) * 4 ) ) ) % 16;
            if ( nSign >= 0 && nSign <= 9 )
                pBuf[nInd] = nSign + '0';
            else if ( nSign >= 10 && nSign <= 15 )
                pBuf[nInd] = nSign - 10 + 'a';

            nInd++;
        }
    }

    int GetStringFromClassID( const GUID& guid, char* pBuf, int nLen )
    {
        // is not allowed to insert 
        if ( nLen < 38 )
            return 0;
        
        pBuf[0] = '{';
        FillCharFromInt( guid.Data1, &pBuf[1], 8 );
        pBuf[9] = '-';
        FillCharFromInt( guid.Data2, &pBuf[10], 4 );
        pBuf[14] = '-';
        FillCharFromInt( guid.Data3, &pBuf[15], 4 );
        pBuf[19] = '-';

        int nInd = 0;
        for ( nInd = 0; nInd < 2 ; nInd++ )
            FillCharFromInt( guid.Data4[nInd], &pBuf[20 + 2*nInd], 2 );
        pBuf[24] = '-';
        for ( nInd = 2; nInd < 8 ; nInd++ )
            FillCharFromInt( guid.Data4[nInd], &pBuf[20 + 1 + 2*nInd], 2 );
        pBuf[37] = '}';

        return 38;
	}

    HRESULT WriteLibraryToRegistry( char* pLibrary, DWORD nLen )
    {
        HRESULT hRes = E_FAIL;
        if ( pLibrary && nLen )
        {
            HKEY hKey = NULL;

            hRes = S_OK;
            for ( int nInd = 0; nInd < SUPPORTED_FACTORIES_NUM; nInd++ )
            {
                char* pSubKey = "Software\\Classes\\CLSID\\.....................................\\InprocHandler32";

                int nGuidLen = GetStringFromClassID( *guidList[nInd], &pSubKey[23], 38 );

                BOOL bLocalSuccess = FALSE;
                if ( nGuidLen && nGuidLen == 38 )
                {
                    if ( ERROR_SUCCESS == RegOpenKey( HKEY_LOCAL_MACHINE, pSubKey, &hKey ) )
                    {
                        if ( ERROR_SUCCESS == RegSetValueEx( hKey, "", 0, REG_SZ, (const BYTE*)pLibrary, nLen ) )
                            bLocalSuccess = TRUE;
                    }

                    if ( hKey )
                    {
                        RegCloseKey( hKey );
                        hKey = NULL;
                    }
                }

                if ( !bLocalSuccess )
                    hRes = E_FAIL;
            }
        }

        return hRes;
    }
};

// ===========================
// InprocEmbedProvider_Impl declaration
// ===========================

namespace inprocserv
{

class InprocEmbedProvider_Impl : public IClassFactory, public InprocCountedObject_Impl
{
public:

	InprocEmbedProvider_Impl( const GUID& guid );
	virtual ~InprocEmbedProvider_Impl();

	/* IUnknown methods */
	STDMETHOD(QueryInterface)(REFIID riid, LPVOID FAR * ppvObj);
	STDMETHOD_(ULONG, AddRef)();
	STDMETHOD_(ULONG, Release)();

	/* IClassFactory methods */
	STDMETHOD(CreateInstance)(IUnknown FAR* punkOuter, REFIID riid, void FAR* FAR* ppv);
	STDMETHOD(LockServer)(int fLock);

protected:

	ULONG               m_refCount;
	GUID				m_guid;
};
}; // namespace inprocserv


// ===========================
// Entry points
// ===========================

// -------------------------------------------------------------------------------
extern "C" BOOL WINAPI DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/ )
{
    if (dwReason == DLL_PROCESS_ATTACH)
    {
        g_hInstance = hInstance;
    }
    else if (dwReason == DLL_PROCESS_DETACH)
    {
    }

    return TRUE;    // ok
}

// -------------------------------------------------------------------------------
extern "C" STDAPI DllGetClassObject( REFCLSID rclsid, REFIID riid, LPVOID* ppv )
{
	for( int nInd = 0; nInd < SUPPORTED_FACTORIES_NUM; nInd++ )
		 if ( *guidList[nInd] == rclsid )
         {
            if ( !IsEqualIID( riid, IID_IUnknown ) && !IsEqualIID( riid, IID_IClassFactory ) )
                return E_NOINTERFACE;

            *ppv = new inprocserv::InprocEmbedProvider_Impl( rclsid );
            if ( *ppv == NULL )
                return E_OUTOFMEMORY;

            ((LPUNKNOWN)*ppv)->AddRef();
            return S_OK;
         }

    return E_FAIL;
}

// -------------------------------------------------------------------------------
extern "C" STDAPI DllCanUnloadNow()
{
    if ( !g_nObj && !g_nLock )
        return S_OK;

    return S_FALSE;
}

// -------------------------------------------------------------------------------
STDAPI DllRegisterServer( void )
{
	char aLibPath[1024];
	HMODULE aCurModule = GetModuleHandleA( "inprocserv.dll" );
	if( aCurModule )
    {
        DWORD nLen = GetModuleFileNameA( aCurModule, aLibPath, 1019 );
        if ( nLen && nLen < 1019 )
        {
            aLibPath[nLen++] = 0;
            return WriteLibraryToRegistry( aLibPath, nLen );
        }
    }

    return E_FAIL;
}

// -------------------------------------------------------------------------------
STDAPI DllUnregisterServer( void )
{
    return WriteLibraryToRegistry( "ole32.dll", 10 );
}

// ===========================
// End of entry points
// ===========================

namespace inprocserv
{

// ===========================
// InprocCountedObject_Impl implementation
// ===========================

// -------------------------------------------------------------------------------
InprocCountedObject_Impl::InprocCountedObject_Impl()
{
    g_nObj++;
}

// -------------------------------------------------------------------------------
InprocCountedObject_Impl::~InprocCountedObject_Impl()
{
    g_nObj--;
}

// ===========================
// InprocEmbedProvider_Impl implementation
// ===========================

// -------------------------------------------------------------------------------
InprocEmbedProvider_Impl::InprocEmbedProvider_Impl( const GUID& guid )
: m_refCount( 0 )
, m_guid( guid )
{
}

// -------------------------------------------------------------------------------
InprocEmbedProvider_Impl::~InprocEmbedProvider_Impl()
{
}

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

    *ppv = NULL;
    return E_NOINTERFACE;
}

// -------------------------------------------------------------------------------
STDMETHODIMP_(ULONG) InprocEmbedProvider_Impl::AddRef()
{
	return ++m_refCount;
}

// -------------------------------------------------------------------------------
STDMETHODIMP_(ULONG) InprocEmbedProvider_Impl::Release()
{
	sal_Int32 nCount = --m_refCount;
	if ( nCount == 0 )
		delete this;
    return nCount;
}

// -------------------------------------------------------------------------------
STDMETHODIMP InprocEmbedProvider_Impl::CreateInstance(IUnknown FAR* punkOuter,
                                                       REFIID riid,
                                                       void FAR* FAR* ppv)
{
    // TODO/LATER: should the aggregation be supported?
    // if ( punkOuter != NULL && riid != IID_IUnknown )
    //     return E_NOINTERFACE;
    if ( punkOuter != NULL )
        return CLASS_E_NOAGGREGATION;

    InprocEmbedDocument_Impl* pEmbedDocument = new InprocEmbedDocument_Impl( m_guid );
    if ( !pEmbedDocument )
        return E_OUTOFMEMORY;

    pEmbedDocument->AddRef();
    HRESULT hr = pEmbedDocument->Init();
    if ( SUCCEEDED( hr ) )
        hr = pEmbedDocument->QueryInterface( riid, ppv );
	pEmbedDocument->Release();

	if ( !SUCCEEDED( hr ) )
        *ppv = NULL;
    
    return hr;
}

// -------------------------------------------------------------------------------
STDMETHODIMP InprocEmbedProvider_Impl::LockServer( int fLock )
{
    if ( fLock )
        g_nLock++;
    else
        g_nLock--;

    return S_OK;
}

}; // namespace inprocserv

