//
// Cmpnt.cpp - Component
//
#include <objbase.h>
#include <stdio.h>    // sprintf
#include <stdlib.h>   //splitpath

#include "Iface.h"
#include "Util.h"
#include "CUnknown.h"
#include "CFactory.h" // Needed for module handle
#include "Cmpnt.h"

static inline void trace(char* msg)
	{ Util::Trace("Component", msg, S_OK) ;}
static inline void trace(char* msg, HRESULT hr)
	{ Util::Trace("Component", msg, hr) ;}

//
// Type library name
//
const char szTypeLibName[] = "Server.tlb" ;

///////////////////////////////////////////////////////////
//
// Interface IX - Implementation
//
HRESULT __stdcall CA::Fx()
{ 
	trace("!!!!! We made it here!!!!!") ;
	return S_OK ;
}

HRESULT __stdcall CA::FxStringIn(BSTR bstrIn)
{ 
	// Display the incoming string.
	ostrstream sout ;
	sout << "FxStringIn received a string:  "
	     << bstrIn 
	     << ends ;
	trace(sout.str()) ;
	return S_OK ;
}

HRESULT __stdcall CA::FxStringOut(BSTR* pbstrOut)
{
	const wchar_t wsz[] = L"[String from FxStringOut]" ;

	// Allocate an outgoing string.
	*pbstrOut = ::SysAllocString(wsz) ;
	if (*pbstrOut == NULL)
	{
		return E_OUTOFMEMORY ;
	}
	return S_OK ;
}

HRESULT __stdcall CA::FxFakeError()
{
	trace("FxFakeError is faking an error.") ;

	// Create the error info object.
	ICreateErrorInfo* pICreateErr ;
	HRESULT hr = ::CreateErrorInfo(&pICreateErr) ;
	if (FAILED(hr))
	{
		return E_FAIL ;
	}

	// pICreateErr->SetHelpFile(...) ;
	// pICreateErr->SetHelpContext(...) ;
	pICreateErr->SetSource(L"InsideCOM.Chap11") ;
	pICreateErr->SetDescription(
		L"This is a fake error generated by the component.") ;
	IErrorInfo* pIErrorInfo = NULL ;
	hr = pICreateErr->QueryInterface(IID_IErrorInfo,
	                                 (void**)&pIErrorInfo) ;
	if (SUCCEEDED(hr))
	{
		::SetErrorInfo(0L, pIErrorInfo) ;
		pIErrorInfo->Release() ;
	}
	pICreateErr->Release() ;
	return E_FAIL ;
}

//
// Constructor
//
CA::CA(IUnknown* pUnknownOuter)
: CUnknown(pUnknownOuter), 
  m_pITypeInfo(NULL)
{
	// Empty
}

//
// Destructor
//
CA::~CA()
{
	if (m_pITypeInfo != NULL)
	{
		m_pITypeInfo->Release() ;
	}

	trace("Destroy self.") ;
}

//
// NondelegatingQueryInterface implementation
//
HRESULT __stdcall CA::NondelegatingQueryInterface(const IID& iid,
                                                  void** ppv)
{ 	
	if (iid == IID_IX)
	{
		return FinishQI(static_cast<IX*>(this), ppv) ;
	}
	else 	if (iid == IID_IDispatch)
	{
		trace("Queried for IDispatch.") ;
		return FinishQI(static_cast<IDispatch*>(this), ppv) ;
	}
	else 	if (iid == IID_ISupportErrorInfo)
	{
		trace("Queried for ISupportErrorInfo.") ;
		return FinishQI(static_cast<ISupportErrorInfo*>(this), ppv) ;
	}
	else
	{
		return CUnknown::NondelegatingQueryInterface(iid, ppv) ;
	}
}

//
// Load and register the type library.
//
HRESULT CA::Init()
{
	HRESULT hr ;

	// Load TypeInfo on demand if we haven't already loaded it.
	if (m_pITypeInfo == NULL)
	{
		ITypeLib* pITypeLib = NULL ;
		hr = ::LoadRegTypeLib(LIBID_ServerLib, 
		                      1, 0, // Major/Minor version numbers
		                      0x00, 
		                      &pITypeLib) ;
		if (FAILED(hr)) 
		{
			trace("LoadRegTypeLib Failed, now trying LoadTypeLib.", hr) ;
			// If it wasn't registered, try to load it from the path.

			// Get the fullname of the server's executable.
			char szModule[512] ;
			DWORD dwResult = ::GetModuleFileName(CFactory::s_hModule,
			                                     szModule,
			                                     512) ; 

			// Split the fullname to get the pathname.
			char szDrive[_MAX_DRIVE];
			char szDir[_MAX_DIR];
			_splitpath(szModule, szDrive, szDir, NULL, NULL) ;

			// Append name of registry.
			char szTypeLibFullName[_MAX_PATH];
			sprintf(szTypeLibFullName,
			        "%s%s%s",
			        szDrive,
			        szDir,
			        szTypeLibName) ;

			// convert to wide char
			wchar_t wszTypeLibFullName[_MAX_PATH] ;
			mbstowcs(wszTypeLibFullName, szTypeLibFullName, _MAX_PATH) ;

			// if LoadTypeLib succeeds, it will have registered
			// the type library for us.
			// for the next time.  
			hr = ::LoadTypeLib(wszTypeLibFullName,
			                   &pITypeLib) ;
			if(FAILED(hr))        
			{
				trace("LoadTypeLib Failed.", hr) ;
				return hr;   
			}

			// Ensure that the type library is registered.
			hr = RegisterTypeLib(pITypeLib, wszTypeLibFullName, NULL) ;
			if(FAILED(hr))        
			{
				trace("RegisterTypeLib Failed.", hr) ;
				return hr ;   
			}
		}

		// Get type information for the interface of the object.
		hr = pITypeLib->GetTypeInfoOfGuid(IID_IX,
		                                  &m_pITypeInfo) ;
		pITypeLib->Release() ;
		if (FAILED(hr))  
		{ 
			trace("GetTypeInfoOfGuid failed.", hr) ;
			return hr ;
		}   
	}
	return S_OK ;
}

///////////////////////////////////////////////////////////
//
// Creation function used by CFactory
//
HRESULT CA::CreateInstance(IUnknown* pUnknownOuter,
                           CUnknown** ppNewComponent ) 
{
	if (pUnknownOuter != NULL)
	{
		// Don't allow aggregation (just for the heck of it).
		return CLASS_E_NOAGGREGATION ;
	}

	*ppNewComponent = new CA(pUnknownOuter) ;
	return S_OK ;
}

///////////////////////////////////////////////////////////
//
// IDispatch implementation
//
HRESULT __stdcall CA::GetTypeInfoCount(UINT* pCountTypeInfo)
{
	trace("GetTypeInfoCount call succeeded.") ;
	*pCountTypeInfo = 1 ;
	return S_OK ;
}

HRESULT __stdcall CA::GetTypeInfo(
	UINT iTypeInfo,
	LCID,          // This object does not support localization.
	ITypeInfo** ppITypeInfo)
{    
	*ppITypeInfo = NULL ;

	if(iTypeInfo != 0)
	{
		trace("GetTypeInfo call failed -- bad iTypeInfo index.") ;
		return DISP_E_BADINDEX ; 
	}

	trace("GetTypeInfo call succeeded.") ;

	// Call AddRef and return the pointer.
	m_pITypeInfo->AddRef() ; 
	*ppITypeInfo = m_pITypeInfo ;
	return S_OK ;
}

HRESULT __stdcall CA::GetIDsOfNames(  
	const IID& iid,
	OLECHAR** arrayNames,
	UINT countNames,
	LCID,          // Localization is not supported.
	DISPID* arrayDispIDs)
{
	if (iid != IID_NULL)
	{
		trace("GetIDsOfNames call failed -- bad IID.") ;
		return DISP_E_UNKNOWNINTERFACE ;
	}

	trace("GetIDsOfNames call succeeded.") ;
	HRESULT hr = m_pITypeInfo->GetIDsOfNames(arrayNames,
	                                         countNames,
	                                         arrayDispIDs) ;
	return hr ;
}

HRESULT __stdcall CA::Invoke(   
      DISPID dispidMember,
      const IID& iid,
      LCID,          // Localization is not supported.
      WORD wFlags,
      DISPPARAMS* pDispParams,
      VARIANT* pvarResult,
      EXCEPINFO* pExcepInfo,
      UINT* pArgErr)
{        
	if (iid != IID_NULL)
	{
		trace("Invoke call failed -- bad IID.") ;
		return DISP_E_UNKNOWNINTERFACE ;
	}

	::SetErrorInfo(0, NULL) ;

	trace("Invoke call succeeded.") ;
	HRESULT hr = m_pITypeInfo->Invoke(
		static_cast<IDispatch*>(this),
		dispidMember, wFlags, pDispParams,
		pvarResult, pExcepInfo, pArgErr) ; 
	return hr ;
}
