/*==========================================================================
 *
 *  Copyright (C) 1998 Microsoft Corporation.  All Rights Reserved.
 *
 *  File:       classfac.c
 *  Content:	a generic class factory
 *
 *
 *	This is a generic C class factory.  All you need to do is implement
 *	a function called DoCreateInstance that will create an instace of
 *	your object.  
 *
 *	GP_ stands for "General Purpose"
 *
 *  History:
 *   Date		By		Reason
 *   ====		==		======
 *	10/13/98	jwo		Created it.
 * 04/11/00     rodtoll     Added code for redirection for custom builds if registry bit is set 
 * 08/23/2000	rodtoll	DllCanUnloadNow always returning TRUE! 
 * 10/05/2000	rodtoll	Bug #46541 - DPVOICE: A/V linking to dpvoice.lib could cause application to fail init and crash
 ***************************************************************************/

#include "dxvoicepch.h"


HRESULT DVT_Create(LPDIRECTVOICESETUPOBJECT *piDVT);
HRESULT DVS_Create(LPDIRECTVOICESERVEROBJECT *piDVS);
HRESULT DVC_Create(LPDIRECTVOICECLIENTOBJECT *piDVC);

#define EXP __declspec(dllexport)

class CClassFactory : IClassFactory
{
public:
	CClassFactory(CLSID* pclsid) : m_lRefCnt(0), m_clsid(*pclsid) {}

	STDMETHOD(QueryInterface)(REFIID riid, LPVOID* ppvObj);
	STDMETHOD_(ULONG, AddRef)() 
	{
		return InterlockedIncrement(&m_lRefCnt);
	}
	STDMETHOD_(ULONG, Release)()
	{
		ULONG l = InterlockedDecrement(&m_lRefCnt);
		if (l == 0)
		{
			delete this;
	    	DecrementObjectCount();
		}
		return l;
	}
	STDMETHOD(CreateInstance)(LPUNKNOWN pUnkOuter, REFIID riid, LPVOID *ppvObj);
	STDMETHOD(LockServer)(BOOL fLock)
	{
		if( fLock )
		{
    		InterlockedIncrement( &g_lNumLocks );
		}
		else
		{
    		InterlockedDecrement( &g_lNumLocks );
		}	
		return S_OK;	
	}

private:	
	LONG					m_lRefCnt;
	CLSID					m_clsid;
};

STDMETHODIMP CClassFactory::QueryInterface(REFIID riid, LPVOID* ppvObj)
{
    *ppvObj = NULL;

    if( IsEqualIID(riid, IID_IClassFactory) ||
        IsEqualIID(riid, IID_IUnknown))
    {
        InterlockedIncrement( &m_lRefCnt );
        *ppvObj = this;
		return S_OK;
    }
    else
    { 
		return E_NOINTERFACE;
    }
}

#undef DPF_MODNAME
#define DPF_MODNAME "CClassFactory::CreateInstance"
STDMETHODIMP CClassFactory::CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, LPVOID *ppvObj)
{
	HRESULT hr = DV_OK;

	if( ppvObj == NULL ||
	    !DNVALID_WRITEPTR( ppvObj, sizeof(LPVOID) ) )
	{
		DPFX(DPFPREP,  DVF_ERRORLEVEL, "Invalid pointer passed for object" );
		return DVERR_INVALIDPOINTER;
	}

	if( pUnkOuter != NULL )
	{
		DPFX(DPFPREP,  DVF_ERRORLEVEL, "Object does not support aggregation" );
		return CLASS_E_NOAGGREGATION;
	}

	if( IsEqualGUID(riid,IID_IDirectPlayVoiceClient) )
	{
		hr = DVC_Create((LPDIRECTVOICECLIENTOBJECT *) ppvObj);
		if (FAILED(hr))
		{
			DPFX(DPFPREP,  DVF_ERRORLEVEL, "DVC_Create Failed hr=0x%x", hr );
			return hr;
		}

		// get the right interface and bump the refcount
		hr = DVC_QueryInterface((LPDIRECTVOICECLIENTOBJECT) *ppvObj, riid, ppvObj);
	}
	else if( IsEqualGUID(riid,IID_IDirectPlayVoiceServer) )
	{
		hr = DVS_Create((LPDIRECTVOICESERVEROBJECT *) ppvObj);
		if (FAILED(hr))
		{
			DPFX(DPFPREP,  DVF_ERRORLEVEL, "DVS_Create Failed hr=0x%x", hr );
			return hr;
		}

		// get the right interface and bump the refcount
		hr = DVS_QueryInterface((LPDIRECTVOICESERVEROBJECT) *ppvObj, riid, ppvObj);
	}
	else if( IsEqualGUID(riid,IID_IDirectPlayVoiceTest) )
	{
		hr = DVT_Create((LPDIRECTVOICESETUPOBJECT *) ppvObj);
		if (FAILED(hr))
		{
			DPFX(DPFPREP,  DVF_ERRORLEVEL, "DVT_Create Failed hr=0x%x", hr );
			return hr;
		}

		// get the right interface and bump the refcount
		hr = DVT_QueryInterface((LPDIRECTVOICESETUPOBJECT) *ppvObj, riid, ppvObj);
	}
	else if( IsEqualGUID(riid,IID_IUnknown) )
	{
		if( m_clsid == CLSID_DirectPlayVoice )
		{
			DPFX(DPFPREP,  0, "Requesting IUnknown through generic CLSID" );
			return E_NOINTERFACE;
		}
		else if( m_clsid == CLSID_DirectPlayVoiceClient )
		{
			hr = DVC_Create((LPDIRECTVOICECLIENTOBJECT *) ppvObj);
			if (FAILED(hr))
			{
				DPFX(DPFPREP,  DVF_ERRORLEVEL, "DVC_Create Failed hr=0x%x", hr );
				return hr;
			}

			// get the right interface and bump the refcount
			hr = DVC_QueryInterface((LPDIRECTVOICECLIENTOBJECT) *ppvObj, riid, ppvObj);
		}
		else if( m_clsid == CLSID_DirectPlayVoiceServer )
		{
			hr = DVS_Create((LPDIRECTVOICESERVEROBJECT *) ppvObj);
			if (FAILED(hr))
			{
				DPFX(DPFPREP,  DVF_ERRORLEVEL, "DVS_Create Failed hr=0x%x", hr );
				return hr;
			}

			// get the right interface and bump the refcount
			hr = DVS_QueryInterface((LPDIRECTVOICESERVEROBJECT) *ppvObj, riid, ppvObj);
		}
		else if( m_clsid == CLSID_DirectPlayVoiceTest ) 
		{
			hr = DVT_Create((LPDIRECTVOICESETUPOBJECT *) ppvObj);
			if (FAILED(hr))
			{
				DPFX(DPFPREP,  DVF_ERRORLEVEL, "DVT_Create Failed hr=0x%x", hr );
				return hr;
			}

			// get the right interface and bump the refcount
			hr = DVT_QueryInterface((LPDIRECTVOICESETUPOBJECT) *ppvObj, riid, ppvObj);
		}
		else
		{
			DPFX(DPFPREP,  DVF_ERRORLEVEL, "Unknown interface" );
			return E_NOINTERFACE;
		}
	}
	else 
	{
		return E_NOINTERFACE;
	}

	IncrementObjectCount();
	
	return hr;
}


#ifdef DPLAY_LOADANDCHECKTRUE
typedef HRESULT (WINAPI *PFN_DLLGETCLASSOBJECT)(REFCLSID rclsid,REFIID riid,LPVOID *ppvObj );
typedef HRESULT (WINAPI *PFN_DLLCANUNLOADNOW)(void);

extern HMODULE ghRedirect;
extern PFN_DLLGETCLASSOBJECT pfnGetClassObject;
extern PFN_DLLCANUNLOADNOW pfnDllCanUnLoadNow;
#endif


/*
 * DllGetClassObject
 *
 * Entry point called by COM to get a ClassFactory pointer
 */
STDAPI  DllGetClassObject(
                REFCLSID rclsid,
                REFIID riid,
                LPVOID *ppvObj )
{
    CClassFactory*	pcf;
    HRESULT		hr;

#ifdef DPLAY_LOADANDCHECKTRUE
	if( ghRedirect != NULL )
	{
		GUID guidCLSID;

		if( IsEqualCLSID( rclsid, DPVOICE_CLSID_DPVOICE ) )
		{
			memcpy( &guidCLSID, &CLSID_DirectPlayVoice, sizeof(GUID) );
		}
		else
		{
			memcpy( &guidCLSID, rclsid, sizeof(GUID) );
		}

		return (*pfnGetClassObject)(&guidCLSID,riid,ppvObj);
	}
#endif

    *ppvObj = NULL;

    /*
     * is this our class id?
     */
	if( !IsEqualCLSID(rclsid, DPVOICE_CLSID_DPVOICE) && 
		!IsEqualCLSID(rclsid, CLSID_DirectPlayVoiceClient) &&
		!IsEqualCLSID(rclsid, CLSID_DirectPlayVoiceServer) && 
		!IsEqualCLSID(rclsid, CLSID_DirectPlayVoiceTest) )
	{
		return CLASS_E_CLASSNOTAVAILABLE;
	}

    /*
     * only allow IUnknown and IClassFactory
     */
    if( !IsEqualIID( riid, IID_IUnknown ) &&
	    !IsEqualIID( riid, IID_IClassFactory ) )
    {
        return E_NOINTERFACE;
    }

    /*
     * create a class factory object
     */
    pcf = new CClassFactory((CLSID*)&rclsid);
    if( NULL == pcf)
    {
        return E_OUTOFMEMORY;
    }

    hr = pcf->QueryInterface( riid, ppvObj );
    if( FAILED( hr ) )
    {
        delete ( pcf );
        *ppvObj = NULL;
    }
    else
    {
		IncrementObjectCount();
	}
	
    return hr;

} /* DllGetClassObject */

/*
 * DllCanUnloadNow
 *
 * Entry point called by COM to see if it is OK to free our DLL
 */
STDAPI DllCanUnloadNow( void )
{
    HRESULT	hr = S_FALSE;

#ifdef DPLAY_LOADANDCHECKTRUE
	if( ghRedirect != NULL )
	{
		return (*pfnDllCanUnLoadNow)();
	}
#endif

	if ( (0 == g_lNumObjects) && (0 == g_lNumLocks) )
	{
		hr = S_OK;
	}
	
    return hr;

} /* DllCanUnloadNow */