/******************************************************************************
 *                                                                         
 *               (C) COPYRIGHT Pacific MindWorks, Inc. 2001-2005
 *                          All rights reserved.
 *
 *****************************************************************************/

#ifndef __NTLPERSONALITYHOST_INCLUDED
#define __NTLPERSONALITYHOST_INCLUDED

#include <atlcoll.h>
#include <atlsafe.h>

#import "IviConfigServer.dll" no_namespace

////////////////////////////////////////////////////////////////////////
//
//	Constants
//

// CATID required for all personality hosts
// {6D76B897-CBEC-4aa9-8A8A-913B43BA6B9F}
static const CATID CATID_IVI_COM_DRIVER_PERSONALITY_HOST = 
{ 0x6d76b897, 0xcbec, 0x4aa9, { 0x8a, 0x8a, 0x91, 0x3b, 0x43, 0xba, 0x6b, 0x9f } };


///////////////////////////////////////////////////////////////////////////////
//
//	IPersonalityHostDispatchImpl
//

enum
{
	// Coercion recording method called from personality
	DISPID_ADD_COERCION_RECORD			= 1,

	// Tracing methods called from personality
	DISPID_GET_CURRENT_TRACE_ELEMENT	= 2,
	DISPID_SET_CURRENT_TRACE_ELEMENT	= 3,
	DISPID_COMMIT_TRACE_DATA			= 4,

	// Active personality change from personality
	DISPID_ACTIVATE_PERSONALITY			= 5,
};

typedef CAtlMap<CString, DISPID>	DISPIDMap;			// key = function name, value = DISPID

template <class DriverClass>
class IPersonalityHostDispatchImpl :
	public IDispatch
{
public:
	IPersonalityHostDispatchImpl();
	virtual ~IPersonalityHostDispatchImpl();

	// IDispatch
	STDMETHOD(GetTypeInfoCount)(UINT* pctinfo);
	STDMETHOD(GetTypeInfo)(UINT itinfo, LCID lcid, ITypeInfo** pptinfo);
	STDMETHOD(GetIDsOfNames)(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgdispid);
	STDMETHOD(Invoke)(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult, EXCEPINFO* pexcepinfo, UINT* puArgErr);

private:
	DISPIDMap m_mapDISPIDs;
};

template <class DriverClass>
IPersonalityHostDispatchImpl<DriverClass>::IPersonalityHostDispatchImpl()
{
	// Coercion recording method called from personality
	m_mapDISPIDs["AddCoercionRecord"]						=	DISPID_ADD_COERCION_RECORD;

	// Tracing methods called from personality
	m_mapDISPIDs["GetCurrentTraceElement"]					=	DISPID_GET_CURRENT_TRACE_ELEMENT;
	m_mapDISPIDs["SetCurrentTraceElement"]					=	DISPID_SET_CURRENT_TRACE_ELEMENT;
	m_mapDISPIDs["CommitTraceData"]							=	DISPID_COMMIT_TRACE_DATA;

	// Active personality change from personality
	m_mapDISPIDs["ActivatePersonality"]						=	DISPID_ACTIVATE_PERSONALITY;
}

template <class DriverClass>
IPersonalityHostDispatchImpl<DriverClass>::~IPersonalityHostDispatchImpl()
{
}

template <class DriverClass>
STDMETHODIMP IPersonalityHostDispatchImpl<DriverClass>::GetTypeInfoCount(UINT* pctinfo)
{
	// We do not support type information for our IDispatch
	*pctinfo = 0;

	return S_OK;
}

template <class DriverClass>
STDMETHODIMP IPersonalityHostDispatchImpl<DriverClass>::GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo** pptinfo)
{
	if (!pptinfo)
		return E_POINTER;
	*pptinfo = NULL;

	// We do not support type information for our IDispatch
	return DISP_E_BADINDEX;
}

template <class DriverClass>
STDMETHODIMP IPersonalityHostDispatchImpl<DriverClass>::GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgdispid)
{
	NTL_UNUSED(riid);
	NTL_UNUSED(lcid);

	ATLASSERT(rgdispid);
	if (!rgdispid)
		return E_POINTER;

	ATLASSERT(cNames == 1);
	if (cNames != 1)
		return E_UNEXPECTED;

	HRESULT hr = S_OK;

	DISPID dispid;
	if (m_mapDISPIDs.Lookup(COLE2T(rgszNames[0]), dispid))
	{
		rgdispid[0] = dispid;
	}
	else
	{
		ATLASSERT(false);
		rgdispid[0] = DISPID_UNKNOWN;
		hr = DISP_E_MEMBERNOTFOUND;
	}

	return hr;
}

template <class DriverClass>
STDMETHODIMP IPersonalityHostDispatchImpl<DriverClass>::Invoke(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult, EXCEPINFO* pexcepinfo, UINT* puArgErr)
{
	NTL_UNUSED(lcid);
	NTL_UNUSED(wFlags);
	NTL_UNUSED(pvarResult);
	NTL_UNUSED(pexcepinfo);
	NTL_UNUSED(puArgErr);

	if (riid != IID_NULL)
		return DISP_E_UNKNOWNINTERFACE;

	HRESULT hr = S_OK;

	DriverClass* pT = static_cast<DriverClass*>(this);

	switch (dispid)
	{
		// Coercion recording method called from personality
		case DISPID_ADD_COERCION_RECORD:
		{
			ATLASSERT(pdispparams->cArgs == 1);
			if (pdispparams->cArgs != 1)
				return DISP_E_BADPARAMCOUNT;

			VARIANTARG varArg = pdispparams->rgvarg[0];
			if (varArg.vt != VT_BSTR)
				return DISP_E_TYPEMISMATCH;

			CString strRecord = COLE2T(varArg.bstrVal);
			pT->AddCoercionRecord(strRecord);
			break;
		}

		// Tracing methods called from personality
		case DISPID_GET_CURRENT_TRACE_ELEMENT:
		{
			ATLASSERT(pdispparams->cArgs == 1);
			if (pdispparams->cArgs != 1)
				return DISP_E_BADPARAMCOUNT;

			VARIANTARG varArg = pdispparams->rgvarg[0];
			if (varArg.vt != (VT_UNKNOWN | VT_BYREF))
				return DISP_E_TYPEMISMATCH;

			MSXML2::IXMLDOMElementPtr spCurrentElement = pT->m_pTracer->GetCurrentElement();
			*(pdispparams->rgvarg[0].ppunkVal) = spCurrentElement.Detach();
			break;
		}
		case DISPID_SET_CURRENT_TRACE_ELEMENT:
		{
			ATLASSERT(pdispparams->cArgs == 1);
			if (pdispparams->cArgs != 1)
				return DISP_E_BADPARAMCOUNT;

			VARIANTARG varArg = pdispparams->rgvarg[0];
			if (varArg.vt != VT_UNKNOWN)
				return DISP_E_TYPEMISMATCH;

			pT->SetCurrentTraceElement(varArg.punkVal);
			break;
		}
		case DISPID_COMMIT_TRACE_DATA:
		{
			ATLASSERT(pdispparams->cArgs == 0);
			if (pdispparams->cArgs != 0)
				return DISP_E_BADPARAMCOUNT;

			hr = pT->CommitTraceData();
			break;
		}
		// Active personality change from personality
		case DISPID_ACTIVATE_PERSONALITY:
		{
			ATLASSERT(pdispparams->cArgs == 1);
			if (pdispparams->cArgs != 1)
				return DISP_E_BADPARAMCOUNT;

			VARIANTARG varArg = pdispparams->rgvarg[0];
			if (varArg.vt != VT_BSTR)
				return DISP_E_TYPEMISMATCH;

			hr = pT->SelectPersonality(varArg.bstrVal, NULL);
			break;
		}

		default:
		{
			hr = DISP_E_MEMBERNOTFOUND;
			break;
		}
	}

	return hr;
}

///////////////////////////////////////////////////////////////////////////////
//
//	CPersonalityDescriptor
//

class CPersonalityDescriptor
{
public:
	CPersonalityDescriptor(REFCLSID clsid = CLSID_NULL, const CString& strID = _T(""), const CString& strName = _T(""), bool bIsIOInitialized = false);

	HRESULT Initialize();

	REFCLSID GetCLSID() const;
	const CString& GetID() const;
	const CString& GetName() const;

	bool GetIOInitialized() const;
	void SetIOInitialized(bool bIOInitialized);

private:
	CLSID m_clsid;
	CString m_strID;
	CString m_strName;
	bool m_bIOInitialized;
};

inline CPersonalityDescriptor::CPersonalityDescriptor(REFCLSID clsid, const CString& strID, const CString& strName, bool bIOInitialized)
	: m_clsid(clsid), m_strID(strID), m_strName(strName), m_bIOInitialized(bIOInitialized)
{
}

inline REFCLSID CPersonalityDescriptor::GetCLSID() const
{
	return m_clsid;
}

inline const CString& CPersonalityDescriptor::GetID() const
{
	return m_strID;
}

inline const CString& CPersonalityDescriptor::GetName() const
{
	return m_strName;
}

inline bool CPersonalityDescriptor::GetIOInitialized() const
{
	return m_bIOInitialized;
}

inline void CPersonalityDescriptor::SetIOInitialized(bool bIOInitialized)
{
	m_bIOInitialized = bIOInitialized;
}

///////////////////////////////////////////////////////////////////////////////
//
//	CPersonalityHost
//

typedef CAtlMap<CString, CPersonalityDescriptor> PersonalityDescriptorMap;		// key = personality name, value = personality descriptor
typedef CAtlMap<CString, IUnknown*> PersonalityObjectMap;						// key = personality name, value = IUnknown for personality

template <class DriverClass, class IOProviderClass>
class CPersonalityHost :
	public CDriver<DriverClass, IOProviderClass>,
	public IPersonalityHostDispatchImpl<DriverClass>
{
public:	
	// IIviDriver overrides
	STDMETHOD(Initialize)(BSTR bstrResourceDescriptor, VARIANT_BOOL bIdQuery, VARIANT_BOOL bReset, BSTR bstrOptionString);
	STDMETHOD(Close)();

	// IIviDriverUtility overrides
	STDMETHOD(Disable)();
	STDMETHOD(LockObject)();
	STDMETHOD(Reset)();
	STDMETHOD(ResetWithDefaults)();
	STDMETHOD(UnlockObject)();

	// IIviDriverOperation overrides
	STDMETHOD(put_Cache)(VARIANT_BOOL newVal);
	STDMETHOD(put_InterchangeCheck)(VARIANT_BOOL newVal);
	STDMETHOD(put_QueryInstrumentStatus)(VARIANT_BOOL newVal);
	STDMETHOD(put_RangeCheck)(VARIANT_BOOL newVal);
	STDMETHOD(put_RecordCoercions)(VARIANT_BOOL newVal);
	STDMETHOD(put_Simulate)(VARIANT_BOOL newVal);
	STDMETHOD(InvalidateAllAttributes)();
	STDMETHOD(ResetInterchangeCheck)();

protected:
	HRESULT InvokeMethodOnAllPersonalities(const CString& strMethodName);
	HRESULT InvokeMethodOnAllPersonalities(const CString& strMethodName, CComVariant varParam);
	HRESULT InvokeMethodOnAllPersonalities(const CString& strMethodName, CComVariant varParamList[], int nParams);
	HRESULT InvokePropertyOnAllPersonalities(const CString& strPropertyName, CComVariant varParam);
	HRESULT InvokeMethodOnPersonality(IUnknown* punkPersonality, const CString& strMethodName);
	HRESULT InvokeMethodOnPersonality(IUnknown* punkPersonality, const CString& strMethodName, CComVariant varParam);
	HRESULT InvokeMethodOnPersonality(IUnknown* punkPersonality, const CString& strMethodName, CComVariant varParamList[], int nParams);
	HRESULT InvokePropertyOnPersonality(IUnknown* punkPersonality, const CString& strPropertyName, CComVariant varParam);

protected:
	HRESULT SelectPersonality(BSTR bstrName, IUnknown** ppunkPersonality);
	HRESULT SelectPersonality(IUnknown* punkPersonality, CPersonalityDescriptor& personalityDescriptor);
	HRESULT GetPersonalityNames(SAFEARRAY** pNames);

public:
	virtual HRESULT SetTraceEnabled(VARIANT_BOOL val);

private:
	const CString& GetPersonalityHostID();					// Implemented in generated code

	HRESULT LoadPersonalityDescriptors();	
	HRESULT GetPersonalityEnumerator(IEnumCLSID** ppEnumCLSID);
	HRESULT CreatePersonalityDescriptorFromCLSID(IIviConfigStorePtr& spConfigStore, const CLSID& clsid);
	HRESULT CreatePersonality(const CPersonalityDescriptor& personalityDescriptor, IUnknown** punkPersonality);

	HRESULT InitializePersonality(IUnknown* punkPersonality, const CString& strPersonalityName, IIviEventServer* pEventServer, long lSourceCookie, long lSinkCookie);
	HRESULT InitializePersonalityIOProvider(IUnknown* punkPersonality);	
	HRESULT FinalInitializePersonality(IUnknown* punkPersonality);	

#ifndef NTL_NO_VISACOM
	HRESULT InitializePersonalityVisaComIOProvider(
		IUnknown* punkPersonality,
		IVisaSession* pVisaSession,
		IMessage* pMessage,		
		IFormattedIO488* pFormattedIO);	 
#endif

#ifndef NTL_NO_VISA
	HRESULT InitializePersonalityVisaIOProvider(
		IUnknown* punkPersonality,
		HMODULE hModuleVisa,
		ViSession viSession);
#endif

	HRESULT InitializePersonalityNullIOProvider(
		IUnknown* punkPersonality);

	HRESULT InitializePersonalityCustomIOProvider(
		IUnknown* punkPersonality,
		long lCustomIOCookie);

	friend class IPersonalityHostDispatchImpl<DriverClass>;

private:
	PersonalityDescriptorMap m_mapPersonalityDescriptors;
	PersonalityObjectMap m_mapPersonalityObjects;
};

// IIviDriver overrides
template <class DriverClass, class IOProviderClass>
STDMETHODIMP CPersonalityHost<DriverClass, IOProviderClass>::Initialize(BSTR bstrResourceDescriptor, VARIANT_BOOL bIdQuery, VARIANT_BOOL bReset, BSTR bstrOptionString)
{
	HRESULT hr = S_OK;

	hr = IIviDriverImpl<DriverClass>::Initialize(bstrResourceDescriptor, bIdQuery, bReset, bstrOptionString);
	if (SUCCEEDED(hr))
	{
		hr = LoadPersonalityDescriptors();
	}

	return hr;
}

template <class DriverClass, class IOProviderClass>
STDMETHODIMP CPersonalityHost<DriverClass, IOProviderClass>::Close()
{
	HRESULT hr = S_OK;

	hr = InvokeMethodOnAllPersonalities("Close");
	if (SUCCEEDED(hr))
	{
		POSITION pos = m_mapPersonalityObjects.GetStartPosition();
		while (pos != NULL)
		{
			IUnknown* punkPersonality = m_mapPersonalityObjects.GetNextValue(pos);
			ATLASSERT(punkPersonality != NULL);
			if (punkPersonality != NULL)
			{
				punkPersonality->Release();
			}
		}
		m_mapPersonalityObjects.RemoveAll();
		m_mapPersonalityDescriptors.RemoveAll();

		hr = IIviDriverImpl<DriverClass>::Close();
	}

	return hr;
}

// IIviDriverUtility overrides
template <class DriverClass, class IOProviderClass>
STDMETHODIMP CPersonalityHost<DriverClass, IOProviderClass>::Disable()
{
	HRESULT hr = S_OK;

	hr = CDriver<DriverClass, IOProviderClass>::Disable();
	if (SUCCEEDED(hr))
	{
		hr = InvokeMethodOnAllPersonalities("Disable");
	}

	return hr;
}

template <class DriverClass, class IOProviderClass>
STDMETHODIMP CPersonalityHost<DriverClass, IOProviderClass>::LockObject()
{
	HRESULT hr = S_OK;

	hr = __super::LockObject();
	if (SUCCEEDED(hr))
	{
		hr = InvokeMethodOnAllPersonalities("LockObject");
	}

	return hr;
}

template <class DriverClass, class IOProviderClass>
STDMETHODIMP CPersonalityHost<DriverClass, IOProviderClass>::Reset()
{
	HRESULT hr = S_OK;

	hr = __super::Reset();
	if (SUCCEEDED(hr))
	{		
		hr = InvokeMethodOnAllPersonalities("Reset");
	}

	return hr;
}

template <class DriverClass, class IOProviderClass>
STDMETHODIMP CPersonalityHost<DriverClass, IOProviderClass>::ResetWithDefaults()
{
	HRESULT hr = S_OK;

	hr = __super::ResetWithDefaults();
	if (SUCCEEDED(hr))
	{
		hr = InvokeMethodOnAllPersonalities("ResetWithDefaults");
	}

	return hr;
}

template <class DriverClass, class IOProviderClass>
STDMETHODIMP CPersonalityHost<DriverClass, IOProviderClass>::UnlockObject()
{
	HRESULT hr = S_OK;

	hr = __super::UnlockObject();
	if (SUCCEEDED(hr))
	{
		hr = InvokeMethodOnAllPersonalities("UnlockObject");
	}

	return hr;
}

// IIviDriverOperation overrides
template <class DriverClass, class IOProviderClass>
STDMETHODIMP CPersonalityHost<DriverClass, IOProviderClass>::put_Cache(VARIANT_BOOL newVal)
{
	HRESULT hr = S_OK;

	hr = __super::put_Cache(newVal);
	if (SUCCEEDED(hr))
	{
		hr = InvokePropertyOnAllPersonalities("Cache", newVal != VARIANT_FALSE);
	}

	return hr;
}

template <class DriverClass, class IOProviderClass>
STDMETHODIMP CPersonalityHost<DriverClass, IOProviderClass>::put_InterchangeCheck(VARIANT_BOOL newVal)
{
	HRESULT hr = S_OK;

	hr = __super::put_InterchangeCheck(newVal);
	if (SUCCEEDED(hr))
	{
		hr = InvokePropertyOnAllPersonalities("InterchangeCheck", newVal != VARIANT_FALSE);
	}

	return hr;
}

template <class DriverClass, class IOProviderClass>
STDMETHODIMP CPersonalityHost<DriverClass, IOProviderClass>::put_QueryInstrumentStatus(VARIANT_BOOL newVal)
{
	HRESULT hr = S_OK;

	hr = __super::put_QueryInstrumentStatus(newVal);
	if (SUCCEEDED(hr))
	{
		hr = InvokePropertyOnAllPersonalities("QueryInstrumentStatus", newVal != VARIANT_FALSE);
	}

	return hr;
}

template <class DriverClass, class IOProviderClass>
STDMETHODIMP CPersonalityHost<DriverClass, IOProviderClass>::put_RangeCheck(VARIANT_BOOL newVal)
{
	HRESULT hr = S_OK;

	hr = __super::put_RangeCheck(newVal);
	if (SUCCEEDED(hr))
	{
		hr = InvokePropertyOnAllPersonalities("RangeCheck", newVal != VARIANT_FALSE);
	}

	return hr;
}

template <class DriverClass, class IOProviderClass>
STDMETHODIMP CPersonalityHost<DriverClass, IOProviderClass>::put_RecordCoercions(VARIANT_BOOL newVal)
{
	HRESULT hr = S_OK;

	hr = __super::put_RecordCoercions(newVal);
	if (SUCCEEDED(hr))
	{
		hr = InvokePropertyOnAllPersonalities("RecordCoercions", newVal != VARIANT_FALSE);
	}

	return hr;
}

template <class DriverClass, class IOProviderClass>
STDMETHODIMP CPersonalityHost<DriverClass, IOProviderClass>::put_Simulate(VARIANT_BOOL newVal)
{
	HRESULT hr = S_OK;

	bool bPreviousSimulate = m_bSimulate;
	hr = __super::put_Simulate(newVal);
	if (SUCCEEDED(hr))
	{
		// Check if we need to initialize the I/O providers on any of the personalities
		if (bPreviousSimulate && (newVal == VARIANT_FALSE))
		{
			POSITION pos = m_mapPersonalityObjects.GetStartPosition();
			while ((pos != NULL) && SUCCEEDED(hr))
			{
				CString strPersonalityName;
				IUnknown* punkPersonality = NULL;
				m_mapPersonalityObjects.GetNextAssoc(pos, strPersonalityName, punkPersonality);
				CPersonalityDescriptor personalityDescriptor;
				bool bFound = m_mapPersonalityDescriptors.Lookup(strPersonalityName, personalityDescriptor);
				ATLASSERT(bFound);
				hr = bFound ? S_OK : E_FAIL;
				if (SUCCEEDED(hr) && !personalityDescriptor.GetIOInitialized())
				{
					hr = InitializePersonalityIOProvider(punkPersonality);
					if (SUCCEEDED(hr))
					{
						personalityDescriptor.SetIOInitialized(true);
					}
				}
			}
		}

		if (SUCCEEDED(hr))
		{
			hr = InvokePropertyOnAllPersonalities("Simulate", newVal != VARIANT_FALSE);
		}
	}

	return hr;
}

template <class DriverClass, class IOProviderClass>
STDMETHODIMP CPersonalityHost<DriverClass, IOProviderClass>::InvalidateAllAttributes()
{
	HRESULT hr = S_OK;

	hr = __super::InvalidateAllAttributes();
	if (SUCCEEDED(hr))
	{
		hr = InvokeMethodOnAllPersonalities("InvalidateAllAttributes");
	}

	return hr;
}

template <class DriverClass, class IOProviderClass>
STDMETHODIMP CPersonalityHost<DriverClass, IOProviderClass>::ResetInterchangeCheck()
{
	HRESULT hr = S_OK;

	hr = __super::ResetInterchangeCheck();
	if (SUCCEEDED(hr))
	{
		hr = InvokeMethodOnAllPersonalities("ResetInterchangeCheck");
	}

	return hr;
}

template <class DriverClass, class IOProviderClass>
HRESULT CPersonalityHost<DriverClass, IOProviderClass>::SetTraceEnabled(VARIANT_BOOL val)
{
	HRESULT hr = S_OK;

	// Note: It's important to change the tracer state on the host first
	// as we pass in the new host trace state to the personalities right after
	hr = __super::SetTraceEnabled(val);
	if (SUCCEEDED(hr))
	{
		CComVariant rgParams[7];
		rgParams[0] = (IUnknown*)m_pTracer->GetTraceDocument();
		rgParams[1] = (long)m_pTracer->GetTraceState();
		rgParams[2] = m_pTracer->GetTraceEnabled();
		rgParams[3] = m_pTracer->GetTraceSizeMax();
		rgParams[4] = m_pTracer->GetTraceArray();
		rgParams[5] = m_pTracer->GetTraceArraySizeMax();
		rgParams[6] = m_pTracer->GetTraceSaveInterval();

		hr = InvokeMethodOnAllPersonalities("SetTracerState", rgParams, 7);
	}

	return hr;
}

template <class DriverClass, class IOProviderClass>
HRESULT CPersonalityHost<DriverClass, IOProviderClass>::InvokeMethodOnAllPersonalities(const CString& strMethodName)
{
	HRESULT hr = S_OK;

	POSITION pos = m_mapPersonalityObjects.GetStartPosition();
	while ((pos != NULL) && SUCCEEDED(hr))
	{
		IUnknown* punkPersonality = m_mapPersonalityObjects.GetNextValue(pos);
		hr = InvokeMethodOnPersonality(punkPersonality, strMethodName);
	}

	return hr;
}

template <class DriverClass, class IOProviderClass>
HRESULT CPersonalityHost<DriverClass, IOProviderClass>::InvokeMethodOnAllPersonalities(const CString& strMethodName, CComVariant varParam)
{
	HRESULT hr = S_OK;

	POSITION pos = m_mapPersonalityObjects.GetStartPosition();
	while ((pos != NULL) && SUCCEEDED(hr))
	{
		IUnknown* punkPersonality = m_mapPersonalityObjects.GetNextValue(pos);
		hr = InvokeMethodOnPersonality(punkPersonality, strMethodName, varParam);
	}

	return hr;
}

template <class DriverClass, class IOProviderClass>
HRESULT CPersonalityHost<DriverClass, IOProviderClass>::InvokeMethodOnAllPersonalities(const CString& strMethodName, CComVariant varParamList[], int nParams)
{
	HRESULT hr = S_OK;

	POSITION pos = m_mapPersonalityObjects.GetStartPosition();
	while ((pos != NULL) && SUCCEEDED(hr))
	{
		IUnknown* punkPersonality = m_mapPersonalityObjects.GetNextValue(pos);
		hr = InvokeMethodOnPersonality(punkPersonality, strMethodName, varParamList, nParams);
	}

	return hr;
}

template <class DriverClass, class IOProviderClass>
HRESULT CPersonalityHost<DriverClass, IOProviderClass>::InvokePropertyOnAllPersonalities(const CString& strPropertyName, CComVariant varParam)
{
	HRESULT hr = S_OK;

	POSITION pos = m_mapPersonalityObjects.GetStartPosition();
	while ((pos != NULL) && SUCCEEDED(hr))
	{
		IUnknown* punkPersonality = m_mapPersonalityObjects.GetNextValue(pos);
		hr = InvokePropertyOnPersonality(punkPersonality, strPropertyName, varParam);
	}

	return hr;
}

template <class DriverClass, class IOProviderClass>
HRESULT CPersonalityHost<DriverClass, IOProviderClass>::InvokeMethodOnPersonality(IUnknown* punkPersonality, const CString& strMethodName)
{
	HRESULT hr = S_OK;

	CComDispatchDriver dispatchDriver(punkPersonality);
	hr = dispatchDriver.Invoke0(CT2OLE(strMethodName));

	return hr;
}

template <class DriverClass, class IOProviderClass>
HRESULT CPersonalityHost<DriverClass, IOProviderClass>::InvokeMethodOnPersonality(IUnknown* punkPersonality, const CString& strMethodName, CComVariant varParam)
{
	HRESULT hr = S_OK;

	CComDispatchDriver dispatchDriver(punkPersonality);
	hr = dispatchDriver.Invoke1(CT2OLE(strMethodName), &varParam);

	return hr;
}

template <class DriverClass, class IOProviderClass>
HRESULT CPersonalityHost<DriverClass, IOProviderClass>::InvokeMethodOnPersonality(IUnknown* punkPersonality, const CString& strMethodName, CComVariant varParamList[], int nParams)
{
	HRESULT hr = S_OK;

	CComDispatchDriver dispatchDriver(punkPersonality);
	hr = dispatchDriver.InvokeN(CT2OLE(strMethodName), varParamList, nParams);

	return hr;
}

template <class DriverClass, class IOProviderClass>
HRESULT CPersonalityHost<DriverClass, IOProviderClass>::InvokePropertyOnPersonality(IUnknown* punkPersonality, const CString& strPropertyName, CComVariant varParam)
{
	HRESULT hr = S_OK;

	CComDispatchDriver dispatchDriver(punkPersonality);
	hr = dispatchDriver.PutPropertyByName(CT2OLE(strPropertyName), &varParam);

	return hr;
}

template <class DriverClass, class IOProviderClass>
HRESULT CPersonalityHost<DriverClass, IOProviderClass>::LoadPersonalityDescriptors()
{
	HRESULT hr = S_OK;

	IIviConfigStorePtr spConfigStore;
	hr = NtlLoadConfigStore(spConfigStore);
	if (FAILED(hr))
		return hr;

	IEnumCLSID* pEnumCLSID = NULL;
	hr = GetPersonalityEnumerator(&pEnumCLSID);
	if (FAILED(hr))
		return hr;

	// Retrive personality CLSIDs 64 at a time
	HRESULT hrEnumCLSID = S_OK;
	enum { MAX_CLSID = 64 };
	CLSID rgCLSID[MAX_CLSID];
	while (hrEnumCLSID == S_OK)
	{
		ULONG cActual = 0;
		hrEnumCLSID = pEnumCLSID->Next(MAX_CLSID, rgCLSID, &cActual);
		if (SUCCEEDED(hrEnumCLSID))
		{
			for (ULONG nCLSIDIndex = 0; nCLSIDIndex < cActual && SUCCEEDED(hr); nCLSIDIndex++)
			{
				// Look in the Config Store to see if this personality supports this host
				hr = CreatePersonalityDescriptorFromCLSID(spConfigStore, rgCLSID[nCLSIDIndex]);
			}
		}
	}

	pEnumCLSID->Release();

	return hr;
}

template <class DriverClass, class IOProviderClass>
HRESULT CPersonalityHost<DriverClass, IOProviderClass>::CreatePersonalityDescriptorFromCLSID(IIviConfigStorePtr& spConfigStore, const CLSID& clsid)
{
	HRESULT hr = S_OK;

	LPOLESTR pszProgID;
	hr = ::ProgIDFromCLSID(clsid, &pszProgID);
	if (FAILED(hr))
		return hr;

	// Look in the Config Store to see if this personality supports this host
	bool bFoundPersonality = false;
	CString strProgID = COLE2T(pszProgID);
	int lastDotIndex = strProgID.ReverseFind('.');
	strProgID = strProgID.Left(lastDotIndex);
	::CoTaskMemFree(pszProgID);

	for (long i = 0; i < spConfigStore->SoftwareModules->Count && !bFoundPersonality; i++)
	{
		try
		{
			IIviSoftwareModulePtr spSoftwareModule = spConfigStore->SoftwareModules->GetItem(i + 1);
			if (spSoftwareModule->ProgID != _bstr_t(strProgID))
				continue;

			IIviStringPtr spPersonalityID = spSoftwareModule->DataComponents->GetItem("PersonalityID");
			ATLASSERT(spPersonalityID != NULL);
			if (spPersonalityID == NULL)
				continue;
			CString strPersonalityID = spPersonalityID->Value;

			IIviStringPtr spPersonalityName = spSoftwareModule->DataComponents->GetItem("PersonalityName");
			ATLASSERT(spPersonalityName != NULL);
			if (spPersonalityName == NULL)
				continue;
			CString strPersonalityName = spPersonalityName->Value;

			IIviStringPtr spSupportedHosts = spSoftwareModule->DataComponents->GetItem("SupportedPersonalityHosts");
			// It's ok if this entry does not exist ==> personality will just never be loaded
			if (spSupportedHosts == NULL)
				continue;

			int nPos = 0;
			CString strSupportedHosts = COLE2T(spSupportedHosts->Value);
			CString strCurrentHostID = strSupportedHosts.Tokenize(",", nPos);
			while (!strCurrentHostID.IsEmpty())
			{
				if (strCurrentHostID == GetPersonalityHostID())
				{
					CPersonalityDescriptor personalityDescriptor(clsid, strPersonalityID, strPersonalityName, false);
					m_mapPersonalityDescriptors[strPersonalityName] = personalityDescriptor;
					bFoundPersonality = true;
					break;
				}
				strCurrentHostID = strSupportedHosts.Tokenize(",", nPos);
			}
		}
		catch (_com_error)
		{
			// Assume the failure is due to the GetItem.  This could occur if a project is registered
			// as a personality and then at some later date a project by the same name is built and not
			// registered as a personality.  In this case, the "PersonalityID" entry (and others) will
			// not be in the Config Store and the GetItem methods will throw.
		}
	}

	return hr;
}

template <class DriverClass, class IOProviderClass>
HRESULT CPersonalityHost<DriverClass, IOProviderClass>::GetPersonalityEnumerator(IEnumCLSID** ppEnumCLSID)
{
	HRESULT hr = S_OK;
	if (ppEnumCLSID == NULL)
		return E_POINTER;
	*ppEnumCLSID = NULL;

	ICatInformation* pCatInfo = NULL;
	hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr, NULL, CLSCTX_ALL, __uuidof(pCatInfo), (void**)&pCatInfo);
	if (SUCCEEDED(hr))
	{
		CATID rgCATID[1];
		rgCATID[0] = CATID_IVI_COM_DRIVER_PERSONALITY; 
		IEnumCLSID* pEnumCLSID = NULL;
		hr = pCatInfo->EnumClassesOfCategories(1, rgCATID, 0, NULL, &pEnumCLSID);
		if (SUCCEEDED(hr) && pEnumCLSID != NULL)
		{
			(*ppEnumCLSID = pEnumCLSID)->AddRef();
			pEnumCLSID->Release();
		}

		pCatInfo->Release();
	}

	return hr;
}

template <class DriverClass, class IOProviderClass>
HRESULT CPersonalityHost<DriverClass, IOProviderClass>::SelectPersonality(BSTR bstrName, IUnknown** ppunkPersonality)
{
	HRESULT hr = S_OK;

	ATLASSERT(m_bInitialized);
	if (m_bInitialized)
	{
		CString strName = COLE2T(bstrName);
		CPersonalityDescriptor personalityDescriptor;
		if (!m_mapPersonalityDescriptors.Lookup(strName, personalityDescriptor))
			return ReportError(GetDriverErrorResourceId(NTL_IDS_PERSONALITY_NOT_INSTALLED));

		IUnknown* punkPersonality = NULL;
		if (!m_mapPersonalityObjects.Lookup(strName, punkPersonality))
		{
			// Personality has not been selected yet, so create it and initialize
			hr = CreatePersonality(personalityDescriptor, &punkPersonality);
			if (SUCCEEDED(hr))
			{	
				IIviEventServer* pEventServer = NULL;
				GetEventServer(&pEventServer);
				hr = InitializePersonality(
					punkPersonality,
					strName,
					pEventServer,
					GetSourceCookie(),
					GetSinkCookie());

				if (SUCCEEDED(hr) && !m_bSimulate)
				{
					// Simulation is not on, so initialize the personality's I/O provider
					hr = InitializePersonalityIOProvider(punkPersonality);
					if (SUCCEEDED(hr))
					{
						personalityDescriptor.SetIOInitialized(true);
					}
				}

				if (SUCCEEDED(hr))
				{
					hr = FinalInitializePersonality(punkPersonality);
				}

				if (SUCCEEDED(hr))
				{
					m_mapPersonalityObjects[strName] = punkPersonality;	// Release called in CPersonalityHost::Close
				}
				pEventServer->Release();
			}
		}

		if (SUCCEEDED(hr))
		{
			hr = SelectPersonality(punkPersonality, personalityDescriptor);
			if (SUCCEEDED(hr))
			{
				// Return the personality pointer if client wants it
				if (ppunkPersonality != NULL)
				{
					(*ppunkPersonality = punkPersonality)->AddRef();
				}
			}
		}
	}

	return hr;
}

template <class DriverClass, class IOProviderClass>
HRESULT CPersonalityHost<DriverClass, IOProviderClass>::SelectPersonality(IUnknown* punkPersonality, CPersonalityDescriptor& personalityDescriptor)
{
	NTL_UNUSED(personalityDescriptor);

	HRESULT hr = S_OK;

	CComDispatchDriver dispatchDriver(punkPersonality);
	hr = dispatchDriver.Invoke0(OLESTR("SelectPersonality"));

	if (SUCCEEDED(hr))
	{
		POSITION pos = m_mapPersonalityObjects.GetStartPosition();
		while ((pos != NULL) && SUCCEEDED(hr))
		{
			CString strPersonalityName;
			IUnknown* punkCurrentPersonality = NULL;
			m_mapPersonalityObjects.GetNextAssoc(pos, strPersonalityName, punkCurrentPersonality);
			if (punkCurrentPersonality != punkPersonality)
			{
				CPersonalityDescriptor currentPersonalityDescriptor;
				bool bFoundDescriptor = m_mapPersonalityDescriptors.Lookup(strPersonalityName, currentPersonalityDescriptor);
				ATLASSERT(bFoundDescriptor);

				dispatchDriver = punkCurrentPersonality;
				hr = dispatchDriver.Invoke0(OLESTR("DeSelectPersonality"));
			}
		}
	}

	return hr;
}

template <class DriverClass, class IOProviderClass>
HRESULT CPersonalityHost<DriverClass, IOProviderClass>::CreatePersonality(const CPersonalityDescriptor& personalityDescriptor, IUnknown** ppunkPersonality)
{
	if (ppunkPersonality == NULL)
		return E_POINTER;
	*ppunkPersonality = NULL;

	HRESULT hr = S_OK;

	IUnknown* punkPersonality = NULL;
	hr = ::CoCreateInstance(personalityDescriptor.GetCLSID(), NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (void**)&punkPersonality);
	if (SUCCEEDED(hr))
	{
		(*ppunkPersonality = punkPersonality)->AddRef();
	}

	return hr;
}

template <class DriverClass, class IOProviderClass>
HRESULT CPersonalityHost<DriverClass, IOProviderClass>::InitializePersonality(
	IUnknown* punkPersonality,
	const CString& strPersonalityName,
	IIviEventServer* pEventServer,
	long lSourceCookie,
	long lSinkCookie)
{
	// Signature of method on personality:
	//
	//	STDMETHOD(InitializePersonality)(
	//		IUnknown* punkHost,
	//		BSTR bstrHostID,
	//		BSTR bstrPersonalityName,
	//		BSTR bstrVisaDllFileName,
	//		IIviEventServer* pEventServer,		
	//		long lSourceCookie,
	//		long lSinkCookie,
	//		long lPerfObjectInstanceID,
	//		BSTR bstrPerfObjectInstanceName,
	//		BSTR bstrModel,
	//		IIviDriverSession* pDriverSession,
	//		VARIANT_BOOL bCache,
	//		VARIANT_BOOL bInterchangeCheck,
	//		VARIANT_BOOL bQueryInstrStatus,
	//		VARIANT_BOOL bRecordCoercions,
	//		VARIANT_BOOL bRangeCheck,
	//		IUnknown* punkTraceXmlDoc,
	//		long nTraceState,
	//		VARIANT_BOOL bTraceEnabled,
	//		long nTraceSizeMax,
	//		VARIANT_BOOL bTraceArray,
	//		long nTraceArraySizeMax,
	//		long nTraceSaveInterval);

	HRESULT hr = S_OK;

	DriverClass* pT = static_cast<DriverClass*>(this);

	// Load up the driver session to pass to the personality so that it can build virtual name translation maps
	IIviDriverSessionPtr spDriverSession = NULL;
	if (m_strLogicalName != _T(""))
	{
		hr = NtlLoadDriverSession(CT2OLE(m_strLogicalName), spDriverSession);
		ATLASSERT(SUCCEEDED(hr));
	}

	if (SUCCEEDED(hr))
	{		
		CComVariant rgParams[24];
		rgParams[0] = pT->GetUnknown();
		rgParams[1] = CT2OLE(GetPersonalityHostID());
		rgParams[2] = CT2OLE(strPersonalityName);
		rgParams[3] = CT2OLE(GetVisaDllFileName());
		rgParams[4] = pEventServer;
		rgParams[5] = lSourceCookie;
		rgParams[6] = lSinkCookie;
		rgParams[7] = GetPerformanceObjectInstanceID();
		rgParams[8] = CT2OLE(GetPerformanceObjectInstanceName());
		rgParams[9] = CT2OLE(m_strModel);
		rgParams[10] = (IUnknown*)spDriverSession;
		rgParams[11] = m_bCache;
		rgParams[12] = m_bInterchangeCheck;
		rgParams[13] = m_bQueryInstrStatus;
		rgParams[14] = m_bRecordCoercions;
		rgParams[15] = m_bRangeCheck;
		rgParams[16] = m_bSimulate;
		rgParams[17] = (IUnknown*)m_pTracer->GetTraceDocument();
		rgParams[18] = (long)m_pTracer->GetTraceState();
		rgParams[19] = m_pTracer->GetTraceEnabled();
		rgParams[20] = m_pTracer->GetTraceSizeMax();
		rgParams[21] = m_pTracer->GetTraceArray();
		rgParams[22] = m_pTracer->GetTraceArraySizeMax();
		rgParams[23] = m_pTracer->GetTraceSaveInterval();

		CComDispatchDriver dispatchDriver(punkPersonality);
		hr = dispatchDriver.InvokeN(OLESTR("InitializePersonality"), rgParams, sizeof(rgParams)/sizeof(rgParams[0]));
	}

	return hr;
}

template <class DriverClass, class IOProviderClass>
HRESULT CPersonalityHost<DriverClass, IOProviderClass>::InitializePersonalityIOProvider(IUnknown* punkPersonality)
{
	if (punkPersonality == NULL)
		return E_INVALIDARG;

	HRESULT hr = S_OK;

	switch (ioprvGetIOProviderType())
	{
#ifndef NTL_NO_VISACOM
		case VisaComIOProvider:
		{
			CVisaComIOProvider* pProvider = dynamic_cast<CVisaComIOProvider*>(this);

			IVisaSession* pVisaSession = NULL;
			pProvider->ioprvGetIVisaSessionPointer(&pVisaSession);

			IMessage* pMessage = NULL;
			pProvider->ioprvGetIMessagePointer(&pMessage);

			IFormattedIO488* pFormattedIO = NULL;
			pProvider->ioprvGetIFormattedIO488Pointer(&pFormattedIO);

			IIviEventServer* pEventServer = NULL;
			GetEventServer(&pEventServer);

			hr = InitializePersonalityVisaComIOProvider(
				punkPersonality,
				pVisaSession,
				pMessage,
				pFormattedIO);

			pVisaSession->Release();
			pMessage->Release();
			pFormattedIO->Release();
			pEventServer->Release();
			break;
		}
#endif

#ifndef NTL_NO_VISA
		case VisaIOProvider:
		{
			CVisaIOProvider* pProvider = dynamic_cast<CVisaIOProvider*>(this);

			ViSession viSession;
			pProvider->ioprvGetVisaSession(&viSession);

			HMODULE hModuleVisa = 0;
			pProvider->ioprvGetModuleHandle(&hModuleVisa);

			IIviEventServer* pEventServer = NULL;
			GetEventServer(&pEventServer);

			hr = InitializePersonalityVisaIOProvider(
				punkPersonality,
				hModuleVisa,
				viSession);
			pEventServer->Release();
			break;
		}
#endif

		case NullIOProvider:
		{
			IIviEventServer* pEventServer = NULL;
			GetEventServer(&pEventServer);

			hr = InitializePersonalityNullIOProvider(
				punkPersonality);
			pEventServer->Release();
			break;
		}

		case CustomIOProvider:
		{
			IIviEventServer* pEventServer = NULL;
			GetEventServer(&pEventServer);

			CCustomIOProvider* pProvider = dynamic_cast<CCustomIOProvider*>(this);

			hr = InitializePersonalityCustomIOProvider(
				punkPersonality,
				pProvider->ioprvGetInitializationCookie());
			pEventServer->Release();
			break;
		}
	}

	return hr;
}

#ifndef NTL_NO_VISACOM
template <class DriverClass, class IOProviderClass>
HRESULT CPersonalityHost<DriverClass, IOProviderClass>::InitializePersonalityVisaComIOProvider(
		IUnknown* punkPersonality,
		IVisaSession* pVisaSession,
		IMessage* pMessage,		
		IFormattedIO488* pFormattedIO)	 
{
	// Signature of method on personality:
	//
	//		STDMETHOD(InitializePersonalityVisaComIOProvider)(
	//			IVisaSession* pVisaSession,
	//			IMessage* pMessage,		
	//			IFormattedIO488* pFormattedIO)

	HRESULT hr = S_OK;

	CComVariant rgParams[3];
	rgParams[0] = pVisaSession;
	rgParams[1] = pMessage;
	rgParams[2] = pFormattedIO;

	CComDispatchDriver dispatchDriver(punkPersonality);
	hr = dispatchDriver.InvokeN(OLESTR("InitializePersonalityVisaComIOProvider"), rgParams, sizeof(rgParams)/sizeof(rgParams[0]));

	return S_OK;
}
#endif

#ifndef NTL_NO_VISA
template <class DriverClass, class IOProviderClass>
HRESULT CPersonalityHost<DriverClass, IOProviderClass>::InitializePersonalityVisaIOProvider(
		IUnknown* punkPersonality,
		HMODULE hModuleVisa,
		ViSession viSession)		
{
	// Signature of method on personality:
	//
	//		STDMETHOD(InitializePersonalityVisaIOProvider)(
	//			HMODULE hModuleVisa,
	//			ViSession viSession)

	HRESULT hr = S_OK;

	CComVariant rgParams[2];
	rgParams[0].ullVal = (ULONGLONG)hModuleVisa;
	rgParams[0].vt = VT_UI8;
	rgParams[1] = CComVariant((ULONG)viSession);

	CComDispatchDriver dispatchDriver(punkPersonality);
	hr = dispatchDriver.InvokeN(OLESTR("InitializePersonalityVisaIOProvider"), rgParams, sizeof(rgParams)/sizeof(rgParams[0]));

	return S_OK;
}
#endif

template <class DriverClass, class IOProviderClass>
HRESULT CPersonalityHost<DriverClass, IOProviderClass>::InitializePersonalityNullIOProvider(
		IUnknown* punkPersonality)
{
	// Signature of method on personality:
	//
	//		STDMETHOD(InitializePersonalityNullIOProvider)()

	HRESULT hr = S_OK;

	CComDispatchDriver dispatchDriver(punkPersonality);
	hr = dispatchDriver.Invoke0(OLESTR("InitializePersonalityNullIOProvider"));

	return S_OK;
}

template <class DriverClass, class IOProviderClass>
HRESULT CPersonalityHost<DriverClass, IOProviderClass>::InitializePersonalityCustomIOProvider(
		IUnknown* punkPersonality,
		long lCustomIOCookie)
{
	// Signature of method on personality:
	//
	//		STDMETHOD(InitializePersonalityCustomIOProvider)(
	//			long lCustomIOCookie)

	HRESULT hr = S_OK;

	CComVariant rgParams[1];
	rgParams[0] = lCustomIOCookie;

	CComDispatchDriver dispatchDriver(punkPersonality);
	hr = dispatchDriver.InvokeN(OLESTR("InitializePersonalityCustomIOProvider"), rgParams, sizeof(rgParams)/sizeof(rgParams[0]));

	return S_OK;
}

template <class DriverClass, class IOProviderClass>
HRESULT CPersonalityHost<DriverClass, IOProviderClass>::FinalInitializePersonality(
	IUnknown* punkPersonality)
{
	// Signature of method on personality:
	//
	//	STDMETHOD(FinalInitializePersonality)(
	//		long lPerfObjectInstanceID,
	//		BSTR bstrPerfObjectInstanceName);

	HRESULT hr = S_OK;

	CComVariant rgParams[2];
	rgParams[0] = GetPerformanceObjectInstanceID();
	rgParams[1] = CT2OLE(GetPerformanceObjectInstanceName());

	CComDispatchDriver dispatchDriver(punkPersonality);
	hr = dispatchDriver.InvokeN(OLESTR("FinalInitializePersonality"), rgParams, sizeof(rgParams)/sizeof(rgParams[0]));

	return hr;
}

template <class DriverClass, class IOProviderClass>
HRESULT CPersonalityHost<DriverClass, IOProviderClass>::GetPersonalityNames(SAFEARRAY** pNames)
{
	if (pNames == NULL)
		return E_POINTER;
	*pNames = NULL;

	CComSafeArray<BSTR> rgbstrPersonalities;
	rgbstrPersonalities.Create();
	POSITION pos = m_mapPersonalityDescriptors.GetStartPosition();
	while (pos != NULL)
	{
		CPersonalityDescriptor personalityDesc = m_mapPersonalityDescriptors.GetNextValue(pos);
		rgbstrPersonalities.Add(CT2OLE(personalityDesc.GetName()));
	}

	*pNames = rgbstrPersonalities.Detach();

	return S_OK;
}

#endif // __NTLPERSONALITYHOST_INCLUDED
