#include <objbase.h>
#include <olectl.h>
#include <initguid.h>
#include "guids.h"
#include "basesnap.h"
#include "comp.h"
#include "compdata.h"
#include "about.h"
#include "uddi.h"
#include <assert.h>

LONG UnRegisterServer( const CLSID& clsid );

//
// Globals Variables
//
HINSTANCE g_hinst;

BOOL WINAPI DllMain( HINSTANCE hinst, DWORD fdwReason, void* lpvReserved )
{
    if( DLL_PROCESS_ATTACH == fdwReason )
	{
		g_hinst = hinst;
    }
    
    return TRUE;
}


STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppvObj)
{
    if( ( rclsid != CLSID_CUDDIServices ) && ( rclsid != CLSID_CSnapinAbout ) )
        return CLASS_E_CLASSNOTAVAILABLE;
    
    if( !ppvObj )
        return E_FAIL;
    
    *ppvObj = NULL;

	//
    // We can only hand out IUnknown and IClassFactory pointers.  Fail
    // if they ask for anything else.
	//
    if( !IsEqualIID(riid, IID_IUnknown) && !IsEqualIID( riid, IID_IClassFactory ) )
        return E_NOINTERFACE;
    
    CClassFactory *pFactory = NULL;
    
	//
    // make the factory passing in the creation function for the type of object they want
	//
    if( CLSID_CUDDIServices == rclsid )
        pFactory = new CClassFactory( CClassFactory::COMPONENT );
    else if( CLSID_CSnapinAbout == rclsid )
        pFactory = new CClassFactory( CClassFactory::ABOUT );
    
    if( NULL == pFactory )
        return E_OUTOFMEMORY;
    
    HRESULT hr = pFactory->QueryInterface( riid, ppvObj );
    
    return hr;
}

STDAPI DllCanUnloadNow(void)
{
    if( ( 0 == g_uObjects ) && ( 0 == g_uSrvLock ) )
        return S_OK;
    else
        return S_FALSE;
}


CClassFactory::CClassFactory(FACTORY_TYPE factoryType)
	: m_cref(0)
	, m_factoryType(factoryType)
{
    OBJECT_CREATED
}

CClassFactory::~CClassFactory()
{
    OBJECT_DESTROYED
}

STDMETHODIMP CClassFactory::QueryInterface(REFIID riid, LPVOID *ppv)
{
    if( !ppv )
        return E_FAIL;
    
    *ppv = NULL;
    
    if( IsEqualIID( riid, IID_IUnknown ) )
        *ppv = static_cast<IClassFactory *>(this);
    else
        if( IsEqualIID(riid, IID_IClassFactory ) )
            *ppv = static_cast<IClassFactory *>(this);
        
        if( *ppv )
        {
            reinterpret_cast<IUnknown *>(*ppv)->AddRef();
            return S_OK;
        }
        
        return E_NOINTERFACE;
}

STDMETHODIMP_(ULONG) CClassFactory::AddRef()
{
    return InterlockedIncrement( (LONG*)&m_cref );
}

STDMETHODIMP_(ULONG) CClassFactory::Release()
{
    if( 0 == InterlockedDecrement( (LONG *)&m_cref ) )
    {
        delete this;
        return 0;
    }
    return m_cref;
}

STDMETHODIMP CClassFactory::CreateInstance( LPUNKNOWN pUnkOuter, REFIID riid, LPVOID * ppvObj )
{
    HRESULT  hr;
    void* pObj;
    
    if( !ppvObj )
        return E_FAIL;
    
    *ppvObj = NULL;
    
	//
    // Our object does does not support aggregation, so we need to
    // fail if they ask us to do aggregation.
	//
    if( pUnkOuter )
        return CLASS_E_NOAGGREGATION;
    
    if( COMPONENT == m_factoryType )
	{
        pObj = new CComponentData();
    }
	else
	{
        pObj = new CSnapinAbout();
    }
    
    if( !pObj )
        return E_OUTOFMEMORY;
    
	//
    // QueryInterface will do the AddRef() for us, so we do not
    // do it in this function
	//
    hr = ( (LPUNKNOWN) pObj )->QueryInterface( riid, ppvObj );
    
    if( FAILED(hr) )
        delete pObj;
    
    return hr;
}

STDMETHODIMP CClassFactory::LockServer( BOOL fLock )
{
    if( fLock )
        InterlockedIncrement( (LONG *) &g_uSrvLock );
    else
        InterlockedDecrement( (LONG *) &g_uSrvLock);
    
    return S_OK;
}

//
// Register the component in the registry.
//
HRESULT RegisterServer( HMODULE hModule,				// DLL module handle
                        const CLSID& clsid,				// Class ID
                        const _TCHAR* szFriendlyName )  // IDs
{
	LPOLESTR wszCLSID = NULL;
	try
	{
		//
		// Get server location.
		//
		_TCHAR szModule[ MAX_PATH + 1];

		DWORD dwResult =
			::GetModuleFileName( hModule,
			szModule,
			sizeof(szModule)/sizeof(_TCHAR) );
		szModule[ MAX_PATH ] = NULL;

		assert( 0 != dwResult );

		//
		// Get CLSID
		//
		HRESULT hr = StringFromCLSID( clsid, &wszCLSID );
		if( FAILED(hr) || ( NULL == wszCLSID ) )
		{
			return hr;
		}

		//
		// Build the key CLSID\\{...}
		//
		tstring strKey( _T("CLSID\\") );
		strKey += wszCLSID;
		
		CUDDIRegistryKey::Create( HKEY_CLASSES_ROOT, strKey );
		CUDDIRegistryKey key( HKEY_CLASSES_ROOT, strKey );
		key.SetValue( _T(""), szFriendlyName );
		key.Close();

		strKey += _T( "\\InprocServer32" );
		CUDDIRegistryKey::Create( HKEY_CLASSES_ROOT, strKey );
		CUDDIRegistryKey keyInprocServer32( HKEY_CLASSES_ROOT, strKey );
		keyInprocServer32.SetValue( _T(""), szModule );
		keyInprocServer32.SetValue( _T("ThreadingModel"), _T("Apartment") );
		keyInprocServer32.Close();

		//
		// Free memory.
		//
		CoTaskMemFree( wszCLSID );
		return S_OK;
	}
	catch( ... )
	{
		CoTaskMemFree( wszCLSID );
		return E_OUTOFMEMORY;
	}
}


//////////////////////////////////////////////////////////
//
// Exported functions
//


//
// Server registration
//
STDAPI DllRegisterServer()
{
	try
	{
		HRESULT hr = S_OK;
		_TCHAR szName[ 256 ];
		_TCHAR szSnapInName[ 256 ];
		_TCHAR szAboutName[ 256 ];
		_TCHAR szProvider[ 256 ];
		//
		// TODO: Fix the version thing here
		//
		//_TCHAR szVersion[ 100 ];

		LoadString( g_hinst, IDS_UDDIMMC_NAME, szName, ARRAYLEN( szName ) );
		LoadString( g_hinst, IDS_UDDIMMC_SNAPINNAME, szSnapInName, ARRAYLEN( szSnapInName ) );
		LoadString( g_hinst, IDS_UDDIMMC_ABOUTNAME, szAboutName, ARRAYLEN( szAboutName ) );
		LoadString( g_hinst, IDS_UDDIMMC_PROVIDER, szProvider, ARRAYLEN( szProvider ) );

		//
		// TODO: Fix the version thing here
		//
		//LoadString( g_hinst, IDS_UDDIMMC_VERSION, szVersion, ARRAYLEN( szVersion ) );
		
		//
		// Register our Components
		//
		hr = RegisterServer( g_hinst, CLSID_CUDDIServices, szName );
		if( FAILED(hr) )
			return hr;

		hr = RegisterServer( g_hinst, CLSID_CSnapinAbout, szAboutName );
		if( FAILED(hr) )
			return hr;

		//
		// Create the primary snapin nodes
		//
		LPOLESTR wszCLSID = NULL;
		hr = StringFromCLSID( CLSID_CUDDIServices, &wszCLSID );
		if( FAILED(hr) )
		{
			return hr;
		}

		LPOLESTR wszCLSIDAbout = NULL;
		hr = StringFromCLSID( CLSID_CSnapinAbout, &wszCLSIDAbout );
		if( FAILED(hr) )
		{
			CoTaskMemFree( wszCLSID );
			return hr;
		}

		TCHAR szPath[ MAX_PATH + 1 ];
		GetModuleFileName( g_hinst, szPath, MAX_PATH );

		tstring strNameStringIndirect( _T("@") );
		strNameStringIndirect += szPath;
		strNameStringIndirect += _T(",-");

		_TCHAR szNameResourceIndex[ 10 ];
		strNameStringIndirect += _itot( IDS_UDDIMMC_NAME, szNameResourceIndex, 10 );

		tstring strMMCKey( g_szMMCBasePath );
		strMMCKey += _T("\\SnapIns\\");
		strMMCKey += wszCLSID;

		CUDDIRegistryKey::Create( HKEY_LOCAL_MACHINE, strMMCKey );
		CUDDIRegistryKey keyMMC( strMMCKey );
		keyMMC.SetValue( _T("About"), wszCLSIDAbout );
		keyMMC.SetValue( _T("NameString"), szName );
		keyMMC.SetValue( _T("NameStringIndirect"), strNameStringIndirect.c_str() );
		keyMMC.SetValue( _T("Provider"), szProvider );
		//
		// TODO: Fix the version thing here
		//
		keyMMC.SetValue( _T("Version" ), _T("1.0") );
		keyMMC.Close();

		tstring strStandAlone( strMMCKey );
		strStandAlone += _T("\\StandAlone");
		CUDDIRegistryKey::Create( HKEY_LOCAL_MACHINE, strStandAlone );

		tstring strNodeTypes( strMMCKey );
		strNodeTypes += _T("\\NodeTypes");
		CUDDIRegistryKey::Create( HKEY_LOCAL_MACHINE, strNodeTypes );
		//
		// No NodeTypes to register
		// We do not allow extensions of our nodes
		//

		//
		// Register as a dynamic extension to computer management
		//
		tstring strExtKey( g_szMMCBasePath );
		strExtKey += _T("\\NodeTypes\\");
		strExtKey += g_szServerAppsGuid;
		strExtKey += _T("\\Dynamic Extensions");
		CUDDIRegistryKey dynamicExtensions( strExtKey );
		dynamicExtensions.SetValue( wszCLSID, szSnapInName );
		dynamicExtensions.Close();

		//
		// Register as a namespace extension to computer management
		//
		tstring strNameSpaceExtensionKey( g_szMMCBasePath );
		strNameSpaceExtensionKey += _T("\\NodeTypes\\");
		strNameSpaceExtensionKey += g_szServerAppsGuid;
		strNameSpaceExtensionKey += _T("\\Extensions\\NameSpace");

		CUDDIRegistryKey hkeyNameSpace( strNameSpaceExtensionKey );
		hkeyNameSpace.SetValue( wszCLSID, szSnapInName );
		hkeyNameSpace.Close();

		CoTaskMemFree( wszCLSID );
		CoTaskMemFree( wszCLSIDAbout );
		return hr;
	}
	catch( ... )
	{
		return E_FAIL;
	}
}

STDAPI DllUnregisterServer()
{
	LPOLESTR wszCLSID = NULL;
	try
	{
		HRESULT hr = S_OK;

		UnRegisterServer( CLSID_CUDDIServices );
		if( FAILED(hr) )
			return hr;

		UnRegisterServer( CLSID_CSnapinAbout );
		if( FAILED(hr) )
			return hr;

		//
		// Remove \\SnapIns\\ entry
		//
		hr = StringFromCLSID( CLSID_CUDDIServices, &wszCLSID );
		if( FAILED( hr) || ( NULL == wszCLSID ) )
		{
			return hr;
		}

		tstring strMMCKey( g_szMMCBasePath );
		strMMCKey += _T("\\SnapIns\\");
		strMMCKey += wszCLSID;
		
		CUDDIRegistryKey::DeleteKey( HKEY_LOCAL_MACHINE, strMMCKey );

		//
		// Remove \\Dynamic Extensions key
		//
		tstring strExtKey( g_szMMCBasePath );
		strExtKey += _T("\\NodeTypes\\");
		strExtKey += g_szServerAppsGuid;
		strExtKey += _T("\\Dynamic Extensions");
		CUDDIRegistryKey dynamicExtensions( strExtKey );
		dynamicExtensions.DeleteValue( wszCLSID );
		dynamicExtensions.Close();

		//
		// Delete \\NodeTypes\\...\\Extensions\\Namespace Value
		//
		tstring strNameSpaceExtensionKey( g_szMMCBasePath );
		strNameSpaceExtensionKey += _T("\\NodeTypes\\");
		strNameSpaceExtensionKey += g_szServerAppsGuid;
		strNameSpaceExtensionKey += _T("\\Extensions\\NameSpace");

		CUDDIRegistryKey hkeyNameSpace( strNameSpaceExtensionKey );
		hkeyNameSpace.DeleteValue( wszCLSID );
		hkeyNameSpace.Close();

		CoTaskMemFree( wszCLSID );
		return S_OK;
	}
	catch(...)
	{
		CoTaskMemFree( wszCLSID );
		return E_FAIL;
	}

}

//
// Remove the component from the registry.
//
LONG UnRegisterServer( const CLSID& clsid )
{
    LPOLESTR wszCLSID = NULL;
	try
	{
		//
		// Get CLSID
		//
		HRESULT hr = StringFromCLSID( clsid, &wszCLSID );
		if( FAILED(hr) || ( NULL == wszCLSID ) )
		{
			return hr;
		}

		//
		// Build the key CLSID\\{...}
		//
		wstring wstrKey( L"CLSID\\" );
		wstrKey += wszCLSID;

		//
		// Delete the CLSID Key - CLSID\{...}
		//
		CUDDIRegistryKey::DeleteKey( HKEY_CLASSES_ROOT, wstrKey );
	}
	catch( ... )
	{
		//
		// Free memory.
		//
	    CoTaskMemFree( wszCLSID );
		return E_OUTOFMEMORY;
	}

	//
    // Free memory.
	//
    CoTaskMemFree( wszCLSID );
    return S_OK ;
}
