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

#ifndef __NTLIOPROVIDERVISA_INCLUDED
#define __NTLIOPROVIDERVISA_INCLUDED

#include <stdarg.h>
#include <atlrx.h>
#include <atlsafe.h>
#include <vector>
#include "ntlutil.h"
#include "visa.h"

typedef ViStatus (_VI_FUNC * PROC_CLEAR)(ViSession);
typedef ViStatus (_VI_FUNC * PROC_CLOSE)(ViObject);
typedef ViStatus (_VI_FUNC * PROC_FIND_RSRC)(ViSession, ViString, ViPFindList, ViPUInt32, ViChar[]);
typedef ViStatus (_VI_FUNC * PROC_FLUSH)(ViSession, ViUInt16);
typedef ViStatus (_VI_FUNC * PROC_GET_ATTRIBUTE)(ViObject, ViAttr, void*);
typedef ViStatus (_VI_FUNC * PROC_OPEN_DEFAULT_RM)(ViPSession);
typedef ViStatus (_VI_FUNC * PROC_OPEN)(ViSession, ViRsrc, ViAccessMode, ViUInt32, ViPSession);
typedef ViStatus (_VI_FUNC * PROC_PRINTF)(ViSession, ViString, ...);
typedef ViStatus (_VI_FUNC * PROC_QUERYF)(ViSession, ViString, ViString, ...);
typedef ViStatus (_VI_FUNC * PROC_READ)(ViSession, ViBuf, ViUInt32, ViPUInt32);
typedef ViStatus (_VI_FUNC * PROC_READ_STB)(ViSession, ViPUInt16);
typedef ViStatus (_VI_FUNC * PROC_SET_ATTRIBUTE)(ViObject, ViAttr, ViAttrState);
typedef ViStatus (_VI_FUNC * PROC_SET_BUF)(ViSession, ViUInt16, ViUInt32);
typedef ViStatus (_VI_FUNC * PROC_WRITE)(ViSession, ViBuf, ViUInt32, ViPUInt32);
typedef ViStatus (_VI_FUNC * PROC_IN8)(ViSession vi, ViUInt16 space, ViBusAddress offset, ViPUInt8 val8);
typedef ViStatus (_VI_FUNC * PROC_OUT8)(ViSession vi, ViUInt16 space, ViBusAddress offset, ViUInt8 val8);
typedef ViStatus (_VI_FUNC * PROC_IN16)(ViSession vi, ViUInt16 space, ViBusAddress offset, ViPUInt16 val16);
typedef ViStatus (_VI_FUNC * PROC_OUT16)(ViSession vi, ViUInt16 space, ViBusAddress offset, ViUInt16 val16);
typedef ViStatus (_VI_FUNC * PROC_IN32)(ViSession vi, ViUInt16 space, ViBusAddress offset, ViPUInt32 val32);
typedef ViStatus (_VI_FUNC * PROC_OUT32)(ViSession vi, ViUInt16 space, ViBusAddress offset, ViUInt32 val32);
typedef ViStatus (_VI_FUNC * PROC_MOVEIN8)(ViSession vi, ViUInt16 space, ViBusAddress offset, ViBusSize length, ViAUInt8 buf8);
typedef ViStatus (_VI_FUNC * PROC_MOVEOUT8)(ViSession vi, ViUInt16 space, ViBusAddress offset, ViBusSize length, ViAUInt8 buf8);
typedef ViStatus (_VI_FUNC * PROC_MOVEIN16)(ViSession vi, ViUInt16 space, ViBusAddress offset, ViBusSize length, ViAUInt16 buf16);
typedef ViStatus (_VI_FUNC * PROC_MOVEOUT16)(ViSession vi, ViUInt16 space, ViBusAddress offset, ViBusSize length, ViAUInt16 buf16);
typedef ViStatus (_VI_FUNC * PROC_MOVEIN32)(ViSession vi, ViUInt16 space, ViBusAddress offset, ViBusSize length, ViAUInt32 buf32);
typedef ViStatus (_VI_FUNC * PROC_MOVEOUT32)(ViSession vi, ViUInt16 space, ViBusAddress offset, ViBusSize length, ViAUInt32 buf32);

///////////////////////////////////////////////////////////////////////////////
//	CVisaIOProvider

class CVisaIOProvider :
	public CDriverIOProvider
{
public:
	CVisaIOProvider();
	virtual ~CVisaIOProvider();

	// Initialization
	virtual HRESULT ioprvInitializeIOProvider(CObjectModelNodeBase* pObjectModelNode, CErrorManagerBase* pErrorManager, CPerfMonManager* pPerfMonManager, BSTR bstrResourceDescriptor);
	virtual HRESULT ioprvInitializeIOProvider(CObjectModelNodeBase* pObjectModelNode, CErrorManagerBase* pErrorManager, CPerfMonManager* pPerfMonManager, ViSession viSession, HMODULE hModule);
	virtual HRESULT ioprvCloseIOProvider();
	virtual IOProviderType ioprvGetIOProviderType();

	// Helpers for direct I/O
	virtual HRESULT ioprvGetVisaSession(ViSession* pVisaSession);
	virtual HRESULT ioprvGetModuleHandle(HMODULE* pModule);

	// IRegister-style functions
	virtual HRESULT ioprvRegisterIn8(UINT16 space, UINT32 offset, UINT8* val);
	virtual HRESULT ioprvRegisterOut8(UINT16 space, UINT32 offset, UINT8 val);
	virtual HRESULT ioprvRegisterIn16(UINT16 space, UINT32 offset, UINT16* val);
	virtual HRESULT ioprvRegisterOut16(UINT16 space, UINT32 offset, UINT16 val);
	virtual HRESULT ioprvRegisterIn32(UINT16 space, UINT32 offset, UINT32* val);
	virtual HRESULT ioprvRegisterOut32(UINT16 space, UINT32 offset, UINT32 val);
	virtual HRESULT ioprvRegisterMoveIn8(UINT16 space, UINT32 offset, UINT32 length, UINT8 buf[]);
	virtual HRESULT ioprvRegisterMoveOut8(UINT16 space, UINT32 offset, UINT32 length, UINT8 buf[]);
	virtual HRESULT ioprvRegisterMoveIn16(UINT16 space, UINT32 offset, UINT32 length, UINT16 buf[]);
	virtual HRESULT ioprvRegisterMoveOut16(UINT16 space, UINT32 offset, UINT32 length, UINT16 buf[]);
	virtual HRESULT ioprvRegisterMoveIn32(UINT16 space, UINT32 offset, UINT32 length, UINT32 buf[]);
	virtual HRESULT ioprvRegisterMoveOut32(UINT16 space, UINT32 offset, UINT32 length, UINT32 buf[]);

protected:
	// IMessage-style functions.
	virtual HRESULT ioprvMessageIORead(BYTE* pBuf, long cbDesired, long* plActualLength);
	virtual HRESULT ioprvMessageIORead(long cbDesired, SAFEARRAY** ppsa);
	virtual HRESULT ioprvMessageIOReadString(long cchDesired, BSTR* pbstrBuf);
	virtual HRESULT ioprvMessageIOWrite(BYTE* pBuf, long cbDesired, long* pcbActual);
	virtual HRESULT ioprvMessageIOWrite(SAFEARRAY* psa, long cbDesired, long* pcbActual);
	virtual HRESULT ioprvMessageIOWriteString(BSTR bstrBuf, long* pcchActual);
	virtual HRESULT ioprvMessageIOClear();
	virtual HRESULT ioprvMessageIOReadSTB(long* plStatus);
	virtual HRESULT ioprvMessageIOSetSendEndEnabled(bool bEnable);
	virtual HRESULT ioprvMessageIOGetSendEndEnabled(bool* pbEnable);
	virtual HRESULT ioprvMessageIOSetTerminationCharacter(BYTE val);
	virtual HRESULT ioprvMessageIOGetTerminationCharacter(BYTE* pVal);
	virtual HRESULT ioprvMessageIOSetTerminationCharacterEnabled(bool bEnable);
	virtual HRESULT ioprvMessageIOGetTerminationCharacterEnabled(bool* pbEnable);
	virtual HRESULT ioprvMessageIOSetTimeout(long lTimeout);
	virtual HRESULT ioprvMessageIOGetTimeout(long* plTimeout);

	// Formatted I/O functions.
	virtual HRESULT ioprvFormattedIOSetInstrumentBigEndian(bool bBigEndian);
	virtual HRESULT ioprvFormattedIOGetInstrumentBigEndian(bool* pbBigEndian);
	virtual HRESULT ioprvFormattedIOWriteString(const CString& strData, bool bFlushAndEnd);
	virtual HRESULT ioprvFormattedIOWriteNumber(VARIANT data, NtlAsciiType type, bool bFlushAndEnd);
	virtual HRESULT ioprvFormattedIOWriteList(VARIANT data, NtlAsciiType type, const CString& strListSeparator, bool bFlushAndEnd);
	virtual HRESULT ioprvFormattedIOWriteIEEEBlock(const CString& strCommand, VARIANT data, bool bFlushAndEnd);
	virtual HRESULT ioprvFormattedIOReadString(CString& strData);
	virtual HRESULT ioprvFormattedIOReadNumber(NtlAsciiType type, VARIANT* pData, bool bFlushToEnd);
	virtual HRESULT ioprvFormattedIOReadList(NtlAsciiType type, const CString& strListSeparator, VARIANT* pData);
	virtual HRESULT ioprvFormattedIOReadIEEEBlock(NtlBinaryType type, VARIANT* pData, bool bSeekToBlock, bool bFlushToEnd);
	virtual HRESULT ioprvFormattedIOFlushWrite(bool bSendEND);
	virtual HRESULT ioprvFormattedIOFlushRead();
	virtual HRESULT ioprvFormattedIOSetBufferSize(NtlBufferMask mask, long lSize);

protected:
	HRESULT LoadVisaDllEntryPoints(HMODULE hModule);
	HRESULT FreeVisaLibrary();
	ViUInt16 TranslateBufferMask(NtlBufferMask mask);
	bool GetNextSeparatedValue(const CString& strSource, const CString& strSeparator, int& nStart, CString& strValue);
	HRESULT TranslateErrorCode(ViStatus status);

protected:
	// Formatted Output
	HRESULT OutputBufferWriteByte(BYTE val);
	HRESULT OutputBufferWriteShort(short val);
	HRESULT OutputBufferWriteLong(long val);
	HRESULT OutputBufferWriteFloat(float val);
	HRESULT OutputBufferWriteDouble(double val);
	HRESULT OutputBufferWriteString(const CString& strSource);
	HRESULT OutputBufferWriteLittleEndianData(BYTE* pData, int nLength);
	HRESULT OutputBufferWriteBigEndianData(BYTE* pData, int nLength, int nElementSize);
	HRESULT OutputBufferWriteIEEEBlockLength(int nLength);
	HRESULT OutputBufferWriteByteArray(BYTE* values, int nLength);
	HRESULT OutputBufferWriteShortArray(short* values, int nLength);
	HRESULT OutputBufferWriteLongArray(long* values, int nLength);
	HRESULT OutputBufferWriteFloatArray(float* values, int nLength);
	HRESULT OutputBufferWriteDoubleArray(double* values, int nLength);
	HRESULT OutputBufferFlush(bool bSendEND);

protected:
	CString m_strDllFilename;
	HMODULE m_hModule;
	ViSession m_resource;
	ViSession m_session;
	PROC_CLEAR m_procClear;
	PROC_CLOSE m_procClose;
	PROC_FIND_RSRC m_procFindRsrc;
	PROC_FLUSH m_procFlush;
	PROC_GET_ATTRIBUTE m_procGetAttribute;
	PROC_OPEN_DEFAULT_RM m_procOpenDefaultRM;
	PROC_OPEN m_procOpen;
	PROC_PRINTF m_procPrintf;
	PROC_QUERYF m_procQueryf;
	PROC_READ m_procRead;
	PROC_READ_STB m_procReadSTB;
	PROC_SET_ATTRIBUTE m_procSetAttribute;
	PROC_SET_BUF m_procSetBuf;
	PROC_WRITE m_procWrite;
	PROC_IN8 m_procIn8;
	PROC_OUT8 m_procOut8;
	PROC_IN16 m_procIn16;
	PROC_OUT16 m_procOut16;
	PROC_IN32 m_procIn32;
	PROC_OUT32 m_procOut32;
	PROC_MOVEIN8 m_procMoveIn8;
	PROC_MOVEOUT8 m_procMoveOut8;
	PROC_MOVEIN16 m_procMoveIn16;
	PROC_MOVEOUT16 m_procMoveOut16;
	PROC_MOVEIN32 m_procMoveIn32;
	PROC_MOVEOUT32 m_procMoveOut32;

protected:
	// Formatted I/O
	bool m_bBigEndian;
	long m_lInputBufferSize;
	long m_lOutputBufferSize;
	vector<BYTE> m_vecOutputBuffer;
};


inline CVisaIOProvider::CVisaIOProvider() 
	: m_strDllFilename(_T("visa32.dll")), m_hModule(NULL), m_resource(0), m_session(0), m_bBigEndian(true), m_lInputBufferSize(1000), m_lOutputBufferSize(1000)
{
	m_hModule = NULL;
	m_procClear = NULL;
	m_procClose = NULL;
	m_procFindRsrc = NULL;
	m_procFlush = NULL;
	m_procGetAttribute = NULL;
	m_procOpenDefaultRM = NULL;
	m_procOpen = NULL;
	m_procPrintf = NULL;
	m_procQueryf = NULL;
	m_procRead = NULL;
	m_procReadSTB = NULL;
	m_procSetAttribute = NULL;
	m_procSetBuf = NULL;
	m_procWrite = NULL;
	m_procIn8 = NULL;
	m_procOut8 = NULL;
	m_procIn16 = NULL;
	m_procOut16 = NULL;
	m_procIn32 = NULL;
	m_procOut32 = NULL;
	m_procMoveIn8 = NULL;
	m_procMoveOut8 = NULL;
	m_procMoveIn16 = NULL;
	m_procMoveOut16 = NULL;
	m_procMoveIn32 = NULL;
	m_procMoveOut32 = NULL;
}

inline CVisaIOProvider::~CVisaIOProvider()
{
}

inline HRESULT CVisaIOProvider::ioprvInitializeIOProvider(CObjectModelNodeBase* pObjectModelNode, CErrorManagerBase* pErrorManager, CPerfMonManager* pPerfMonManager, BSTR bstrResourceDescriptor)
{
	if (pErrorManager == NULL)
		return E_INVALIDARG;

	if (pPerfMonManager == NULL)
		return E_INVALIDARG;

	if (bstrResourceDescriptor == NULL)
		return E_INVALIDARG;

	HRESULT hr = S_OK;

	m_strDllFilename = pObjectModelNode->GetVisaDllFileName();

	// If it already has been initialized, then it will get closed
	ioprvCloseIOProvider();

	// Initialize base class
	hr = __super::ioprvInitializeIOProvider(pObjectModelNode, pErrorManager, pPerfMonManager, bstrResourceDescriptor);
	if (SUCCEEDED(hr))
	{
		m_hModule = ::LoadLibrary(m_strDllFilename);
		if (m_hModule != 0)
		{
			hr = LoadVisaDllEntryPoints(m_hModule);
			if (SUCCEEDED(hr))
			{
				ViStatus status;
				status = (m_procOpenDefaultRM)(&m_resource);
				if (status == VI_SUCCESS)
				{
					status = (m_procOpen)(m_resource, CW2A(bstrResourceDescriptor), VI_NULL, DEFAULT_TIMEOUT, &m_session);
					if (status == VI_SUCCESS)
					{
						m_vecOutputBuffer.resize(0);
						m_vecOutputBuffer.reserve(m_lOutputBufferSize);
					}
					else
					{
						// Could not open the resource
						hr = E_IVI_RESOURCE_UNKNOWN;
					}
				}
				else
				{
					// Could not open resource manager
					hr = E_FAIL;
				}
			}
			else
			{
				// Could not load the dll in memory
				hr = E_FAIL;
			}
		}
	}

	return hr;
}

inline HRESULT CVisaIOProvider::ioprvInitializeIOProvider(CObjectModelNodeBase* pObjectModelNode, CErrorManagerBase* pErrorManager, CPerfMonManager* pPerfMonManager, ViSession viSession, HMODULE hModule)
{
	if (pErrorManager == NULL)
		return E_INVALIDARG;

	if (pPerfMonManager == NULL)
		return E_INVALIDARG;

	if (viSession == 0)
		return E_INVALIDARG;

	if (hModule == 0)
		return E_INVALIDARG;

	HRESULT hr = S_OK;

	hr = __super::ioprvInitializeIOProvider(pObjectModelNode, pErrorManager, pPerfMonManager, CComBSTR(""));
	if (SUCCEEDED(hr))
	{
		m_session = viSession;
		m_hModule = hModule;

		hr = LoadVisaDllEntryPoints(m_hModule);
	}

	return hr;
}

inline HRESULT CVisaIOProvider::ioprvCloseIOProvider()
{
	HRESULT hr = S_OK;

	ViStatus status;

	// Closing the resource manager will close all sessions at the same time
	if (m_resource != 0)
	{
		status = (m_procClose)(m_resource);
	}

	m_resource = 0;
	m_session = 0;

	hr = __super::ioprvCloseIOProvider();

	return hr;
}

inline IOProviderType CVisaIOProvider::ioprvGetIOProviderType()
{
	return VisaIOProvider;
}

inline HRESULT CVisaIOProvider::ioprvGetVisaSession(ViSession* pVisaSession)
{
	if (pVisaSession == NULL)
		return E_POINTER;

	*pVisaSession = m_session;

	return S_OK;
}

inline HRESULT CVisaIOProvider::ioprvGetModuleHandle(HMODULE* pModule)
{
	if (pModule == NULL)
		return E_POINTER;

	*pModule = m_hModule;

	return S_OK;
}

inline HRESULT CVisaIOProvider::ioprvMessageIORead(BYTE* pBuf, long cbDesired, long* plActualLength)
{
	IO_METHOD_START();

	if (m_hModule == NULL)
		return E_FAIL;

	if (m_procRead == NULL)
		return E_FAIL;

	if (cbDesired < 0)
		return E_INVALIDARG;

	if (pBuf == NULL)
		return E_INVALIDARG;

	if (plActualLength == NULL)
		return E_POINTER;
	*plActualLength = 0;

	HRESULT hr = S_OK;

	ViUInt32 count = 0;
	ViStatus status = (m_procRead)(m_session, pBuf, cbDesired, &count);
	if (status == VI_SUCCESS || status == VI_SUCCESS_TERM_CHAR || status == VI_SUCCESS_MAX_CNT)
	{
		*plActualLength = (long)count;
	}
	else
	{
		hr = TranslateErrorCode(status);
	}

	return hr;
}

inline HRESULT CVisaIOProvider::ioprvMessageIORead(long cbDesired, SAFEARRAY** ppsa)
{
	IO_METHOD_START();

	if (m_hModule == NULL)
		return E_FAIL;

	if (m_procRead == NULL)
		return E_FAIL;

	if (cbDesired < 0)
		return E_INVALIDARG;

	if (ppsa == NULL)
		return E_POINTER;
	*ppsa = NULL;

	HRESULT hr = S_OK;

	ViUInt32 count;
	BYTE* buffer = new BYTE[cbDesired];
	ViStatus status = (m_procRead)(m_session, buffer, cbDesired, &count);
	if (status == VI_SUCCESS || status == VI_SUCCESS_TERM_CHAR || status == VI_SUCCESS_MAX_CNT)
	{
		hr = CSafeArray<BYTE>::CreateFromArray(buffer, count, VT_UI1, ppsa);
	}
	else
	{
		hr = TranslateErrorCode(status);
	}
	delete[] buffer;

	return hr;
}

inline HRESULT CVisaIOProvider::ioprvMessageIOReadString(long cchDesired, BSTR* pbstrBuf)
{
	IO_METHOD_START();

	if (m_hModule == NULL)
		return E_FAIL;

	if (m_procRead == NULL)
		return E_FAIL;

	if (cchDesired < 0)
		return E_INVALIDARG;

	if (pbstrBuf == NULL)
		return E_POINTER;
	*pbstrBuf = NULL;

	HRESULT hr = S_OK;

	ViUInt32 count;
	BYTE* buffer = new BYTE[cchDesired + 1];
	ViStatus status = (m_procRead)(m_session, buffer, cchDesired, &count);
	if (status == VI_SUCCESS || status == VI_SUCCESS_TERM_CHAR || status == VI_SUCCESS_MAX_CNT)
	{
		buffer[count] = 0;
		*pbstrBuf = CComBSTR((char*)buffer).Detach();
	}
	else
	{
		hr = TranslateErrorCode(status);
	}
	delete[] buffer;

	return hr;
}

inline HRESULT CVisaIOProvider::ioprvMessageIOWrite(BYTE* pBuf, long cbDesired, long* pcbActual)
{
	IO_METHOD_START();

	if (m_hModule == NULL)
		return E_FAIL;

	if (m_procWrite == NULL)
		return E_FAIL;

	if (pBuf == NULL)
		return E_INVALIDARG;

	if (cbDesired < 0)
		return E_INVALIDARG;

	if (pcbActual == NULL)
		return E_POINTER;
	*pcbActual = 0;

	HRESULT hr = S_OK;

	ViUInt32 count;
	ViStatus status = (m_procWrite)(m_session, pBuf, cbDesired, &count);
	if (status == VI_SUCCESS)
	{
		*pcbActual = count;
	}
	else
	{
		hr = TranslateErrorCode(status);
	}

	return hr;
}

inline HRESULT CVisaIOProvider::ioprvMessageIOWrite(SAFEARRAY* psa, long cbDesired, long* pcbActual)
{
	IO_METHOD_START();

	if (m_hModule == NULL)
		return E_FAIL;

	if (m_procWrite == NULL)
		return E_FAIL;

	if (psa == NULL)
		return E_INVALIDARG;

	if (cbDesired < 0)
		return E_INVALIDARG;

	if (pcbActual == NULL)
		return E_POINTER;
	*pcbActual = 0;

	HRESULT hr = S_OK;

	BYTE* pBuffer;
	hr = ::SafeArrayAccessData(psa, (void**) &pBuffer);
	if (SUCCEEDED(hr))
	{
		ViUInt32 count;
		ViStatus status = (m_procWrite)(m_session, pBuffer, cbDesired, &count);
		if (status == VI_SUCCESS)
		{
			*pcbActual = count;
		}
		else
		{
			hr = TranslateErrorCode(status);
		}

		::SafeArrayUnaccessData(psa);
	}

	return hr;
}

inline HRESULT CVisaIOProvider::ioprvMessageIOWriteString(BSTR bstrBuf, long* pcchActual)
{
	IO_METHOD_START();

	if (m_hModule == NULL)
		return E_FAIL;

	if (m_procWrite == NULL)
		return E_FAIL;

	if (bstrBuf == NULL)
		return E_INVALIDARG;

	if (pcchActual == NULL)
		return E_POINTER;
	*pcchActual = 0;

	HRESULT hr = S_OK;

	USES_CONVERSION;

	ViBuf buffer = (ViBuf)OLE2A(bstrBuf);
	ViUInt32 length = (ViUInt32)strlen((const char*)buffer);

	ViUInt32 count;
	ViStatus status = (m_procWrite)(m_session, buffer, length, &count);
	if (status == VI_SUCCESS)
	{
		*pcchActual = count;
	}
	else
	{
		hr = TranslateErrorCode(status);
	}

	return hr;
}

inline HRESULT CVisaIOProvider::ioprvMessageIOClear()
{
	IO_METHOD_START();

	if (m_hModule == NULL)
		return E_FAIL;

	if (m_procClear == NULL)
		return E_FAIL;

	HRESULT hr = S_OK;

	ViStatus status = (m_procClear)(m_session);
	if (status != VI_SUCCESS)
	{
		hr = TranslateErrorCode(status);
	}

	return hr;
}

inline HRESULT CVisaIOProvider::ioprvMessageIOReadSTB(long* plStatus)
{
	IO_METHOD_START();

	if (plStatus == NULL)
		return E_POINTER;
	*plStatus = 0;

	if (m_hModule == NULL)
		return E_FAIL;

	if (m_procClear == NULL)
		return E_FAIL;

	HRESULT hr = S_OK;

	ViUInt16 stb;
	ViStatus status = (m_procReadSTB)(m_session, &stb);
	if (status != VI_SUCCESS)
	{
		hr = TranslateErrorCode(status);
	}
	else
	{
		*plStatus = (long)stb;
	}

	return hr;
}

inline HRESULT CVisaIOProvider::ioprvMessageIOSetSendEndEnabled(bool bEnable)
{
	IO_METHOD_START();

	if (m_hModule == NULL)
		return E_FAIL;

	if (m_procSetAttribute == NULL)
		return E_FAIL;

	HRESULT hr = S_OK;

	ViStatus status = (m_procSetAttribute)(m_session, VI_ATTR_SEND_END_EN, bEnable ? VI_TRUE : VI_FALSE);
	if (status != VI_SUCCESS)
	{
		hr = TranslateErrorCode(status);
	}

	return hr;
}

inline HRESULT CVisaIOProvider::ioprvMessageIOGetSendEndEnabled(bool* pbEnable)
{
	IO_METHOD_START();

	if (m_hModule == NULL)
		return E_FAIL;

	if (m_procGetAttribute == NULL)
		return E_FAIL;

	if (pbEnable == NULL)
		return E_POINTER;

	HRESULT hr = S_OK;

	ViBoolean enable;

	ViStatus status = (m_procGetAttribute)(m_session, VI_ATTR_SEND_END_EN, &enable);
	if (status == VI_SUCCESS)
	{
		*pbEnable = (enable != VI_FALSE);
	}
	else
	{
		hr = TranslateErrorCode(status);
	}

	return hr;
}

inline HRESULT CVisaIOProvider::ioprvMessageIOSetTerminationCharacter(BYTE val)
{
	IO_METHOD_START();

	if (m_hModule == NULL)
		return E_FAIL;

	if (m_procSetAttribute == NULL)
		return E_FAIL;

	HRESULT hr = S_OK;

	ViStatus status = (m_procSetAttribute)(m_session, VI_ATTR_TERMCHAR, val);
	if (status != VI_SUCCESS)
	{
		hr = TranslateErrorCode(status);
	}

	return hr;
}

inline HRESULT CVisaIOProvider::ioprvMessageIOGetTerminationCharacter(BYTE* pVal)
{
	IO_METHOD_START();

	if (m_hModule == NULL)
		return E_FAIL;

	if (m_procGetAttribute == NULL)
		return E_FAIL;

	if (pVal == NULL)
		return E_POINTER;

	HRESULT hr = S_OK;

	ViStatus status = (m_procGetAttribute)(m_session, VI_ATTR_TERMCHAR, pVal);
	if (status != VI_SUCCESS)
	{
		hr = TranslateErrorCode(status);
	}

	return hr;
}

inline HRESULT CVisaIOProvider::ioprvMessageIOSetTerminationCharacterEnabled(bool bEnable)
{
	IO_METHOD_START();

	if (m_hModule == NULL)
		return E_FAIL;

	if (m_procSetAttribute == NULL)
		return E_FAIL;

	HRESULT hr = S_OK;

	ViStatus status = (m_procSetAttribute)(m_session, VI_ATTR_TERMCHAR_EN, bEnable ? VI_TRUE : VI_FALSE);
	if (status != VI_SUCCESS)
	{
		hr = TranslateErrorCode(status);
	}

	return hr;
}

inline HRESULT CVisaIOProvider::ioprvMessageIOGetTerminationCharacterEnabled(bool* pbEnable)
{
	IO_METHOD_START();

	if (m_hModule == NULL)
		return E_FAIL;

	if (m_procGetAttribute == NULL)
		return E_FAIL;

	if (pbEnable == NULL)
		return E_POINTER;

	HRESULT hr = S_OK;

	ViBoolean enable;

	ViStatus status = (m_procGetAttribute)(m_session, VI_ATTR_TERMCHAR_EN, &enable);
	if (status == VI_SUCCESS)
	{
		*pbEnable = (enable != VI_FALSE);
	}
	else
	{
		hr = TranslateErrorCode(status);
	}

	return hr;
}

inline HRESULT CVisaIOProvider::ioprvMessageIOSetTimeout(long lTimeout)
{
	IO_METHOD_START();

	if (m_hModule == NULL)
		return E_FAIL;

	if (m_procSetAttribute == NULL)
		return E_FAIL;

	HRESULT hr = S_OK;

	ViStatus status = (m_procSetAttribute)(m_session, VI_ATTR_TMO_VALUE, (ViUInt32)lTimeout);
	if (status != VI_SUCCESS)
	{
		hr = TranslateErrorCode(status);
	}

	return hr;
}

inline HRESULT CVisaIOProvider::ioprvMessageIOGetTimeout(long* plTimeout)
{
	IO_METHOD_START();

	if (m_hModule == NULL)
		return E_FAIL;

	if (m_procGetAttribute == NULL)
		return E_FAIL;

	if (plTimeout == NULL)
		return E_POINTER;

	HRESULT hr = S_OK;

	ViUInt32 timeout;

	ViStatus status = (m_procGetAttribute)(m_session, VI_ATTR_TMO_VALUE, &timeout);
	if (status == VI_SUCCESS)
	{
		*plTimeout = timeout;
	}
	else
	{
		hr = TranslateErrorCode(status);
	}

	return hr;
}

inline HRESULT CVisaIOProvider::ioprvFormattedIOSetInstrumentBigEndian(bool bBigEndian)
{
	IO_METHOD_START();

	HRESULT hr = S_OK;

	m_bBigEndian = bBigEndian;

	return hr;
}

inline HRESULT CVisaIOProvider::ioprvFormattedIOGetInstrumentBigEndian(bool* pbBigEndian)
{
	IO_METHOD_START();

	if (pbBigEndian == NULL)
		return E_POINTER;

	HRESULT hr = S_OK;

	*pbBigEndian = m_bBigEndian;

	return hr;
}

inline HRESULT CVisaIOProvider::ioprvFormattedIOWriteString(const CString& strData, bool bFlushAndEnd)
{
	IO_METHOD_START();

	HRESULT hr = S_OK;

	hr = OutputBufferWriteString(strData);
	if (SUCCEEDED(hr))
	{
		if (bFlushAndEnd)
		{
			hr = OutputBufferFlush(true);
		}
	}

	return hr;
}

inline HRESULT CVisaIOProvider::ioprvFormattedIOWriteNumber(VARIANT data, NtlAsciiType type, bool bFlushAndEnd)
{
	IO_METHOD_START();

	HRESULT hr = S_OK;

	switch (type)
	{
		case NtlAsciiType_UI1:
		{
			ATLASSERT((data.vt & (VT_UI1)) != 0);
			hr = OutputBufferWriteByte(data.bVal);
			break;
		}

		case NtlAsciiType_I2:
		{
			ATLASSERT((data.vt & (VT_I2)) != 0);
			hr = OutputBufferWriteShort(data.iVal);
			break;
		}

		case NtlAsciiType_I4:
		{
			ATLASSERT((data.vt & (VT_I4)) != 0);
			hr = OutputBufferWriteLong(data.lVal);
			break;
		}

		case NtlAsciiType_R4:
		{
			ATLASSERT((data.vt & (VT_R4)) != 0);
			hr = OutputBufferWriteFloat(data.fltVal);
			break;
		}

		case NtlAsciiType_R8:
		{
			ATLASSERT((data.vt & (VT_R8)) != 0);
			hr = OutputBufferWriteDouble(data.dblVal);
			break;
		}

		default:
		{
			hr = E_FAIL;
			break;
		}
	}

	if (SUCCEEDED(hr))
	{
		if (bFlushAndEnd)
		{
			hr = OutputBufferFlush(true);
		}
	}

	return hr;
}

inline HRESULT CVisaIOProvider::ioprvFormattedIOWriteList(VARIANT data, NtlAsciiType type, const CString& strListSeparator, bool bFlushAndEnd)
{
	IO_METHOD_START();

	HRESULT hr = S_OK;

	switch (type)
	{
		case NtlAsciiType_UI1:
		{
			ATLASSERT((data.vt & (VT_ARRAY | VT_UI1)) != 0);
			SAFEARRAY* psa = data.parray;
			CComSafeArray<BYTE> csa;
			csa.Attach(psa);
			long lLowerBound = csa.GetLowerBound();
			long lUpperBound = csa.GetUpperBound();
			for (long i = lLowerBound; i <= lUpperBound; i++)
			{
				if (i > lLowerBound)
				{
					hr = OutputBufferWriteString(strListSeparator);
				}

				hr = OutputBufferWriteByte(csa[i]);
			}
			csa.Detach();
			break;
		}

		case NtlAsciiType_I2:
		{
			ATLASSERT((data.vt & (VT_ARRAY | VT_I2)) != 0);
			SAFEARRAY* psa = data.parray;
			CComSafeArray<short> csa;
			csa.Attach(psa);
			long lLowerBound = csa.GetLowerBound();
			long lUpperBound = csa.GetUpperBound();
			for (long i = lLowerBound; i <= lUpperBound; i++)
			{
				if (i > lLowerBound)
				{
					hr = OutputBufferWriteString(strListSeparator);
				}

				hr = OutputBufferWriteShort(csa[i]);
			}
			csa.Detach();
			break;
		}

		case NtlAsciiType_I4:
		{
			ATLASSERT((data.vt & (VT_ARRAY | VT_I4)) != 0);
			SAFEARRAY* psa = data.parray;
			CComSafeArray<long> csa;
			csa.Attach(psa);
			long lLowerBound = csa.GetLowerBound();
			long lUpperBound = csa.GetUpperBound();
			for (long i = lLowerBound; i <= lUpperBound; i++)
			{
				if (i > lLowerBound)
				{
					hr = OutputBufferWriteString(strListSeparator);
				}

				hr = OutputBufferWriteLong(csa[i]);
			}
			csa.Detach();
			break;
		}

		case NtlAsciiType_BSTR:
		{
			ATLASSERT((data.vt & (VT_ARRAY | VT_BSTR)) != 0);
			SAFEARRAY* psa = data.parray;
			CComSafeArray<BSTR> csa;
			csa.Attach(psa);
			long lLowerBound = csa.GetLowerBound();
			long lUpperBound = csa.GetUpperBound();
			for (long i = lLowerBound; i <= lUpperBound; i++)
			{
				if (i > lLowerBound)
				{
					hr = OutputBufferWriteString(strListSeparator);
				}

				hr = OutputBufferWriteString(CString((BSTR)csa[i]));
			}
			csa.Detach();
			break;
		}

		case NtlAsciiType_R4:
		{
			ATLASSERT((data.vt & (VT_ARRAY | VT_R4)) != 0);
			SAFEARRAY* psa = data.parray;
			CComSafeArray<float> csa;
			csa.Attach(psa);
			long lLowerBound = csa.GetLowerBound();
			long lUpperBound = csa.GetUpperBound();
			for (long i = lLowerBound; i <= lUpperBound; i++)
			{
				if (i > lLowerBound)
				{
					hr = OutputBufferWriteString(strListSeparator);
				}

				hr = OutputBufferWriteFloat(csa[i]);
			}
			csa.Detach();
			break;
		}

		case NtlAsciiType_R8:
		{
			ATLASSERT((data.vt & (VT_ARRAY | VT_R8)) != 0);
			SAFEARRAY* psa = data.parray;
			CComSafeArray<double> csa;
			csa.Attach(psa);
			long lLowerBound = csa.GetLowerBound();
			long lUpperBound = csa.GetUpperBound();
			for (long i = lLowerBound; i <= lUpperBound; i++)
			{
				if (i > lLowerBound)
				{
					hr = OutputBufferWriteString(strListSeparator);
				}

				hr = OutputBufferWriteDouble(csa[i]);
			}
			csa.Detach();
			break;
		}

		default:
		{
			hr = E_FAIL;
			break;
		}
	}

	if (SUCCEEDED(hr))
	{
		if (bFlushAndEnd)
		{
			hr = OutputBufferFlush(true);
		}
	}

	return hr;
}

inline HRESULT CVisaIOProvider::ioprvFormattedIOWriteIEEEBlock(const CString& strCommand, VARIANT data, bool bFlushAndEnd)
{
	IO_METHOD_START();

	HRESULT hr = S_OK;

	hr = OutputBufferWriteString(strCommand + _T(" "));
	if (SUCCEEDED(hr))
	{
		if (data.vt == (VT_ARRAY | VT_UI1))
		{
			SAFEARRAY* psa = data.parray;
			long lLength = GetSafeArrayLength(psa);

			hr = OutputBufferWriteIEEEBlockLength(lLength * sizeof(BYTE));
			if (SUCCEEDED(hr))
			{
				BYTE* prguch;
				hr = ::SafeArrayAccessData(psa, (void**) &prguch);
				if (SUCCEEDED(hr))
				{
					hr = OutputBufferWriteByteArray(prguch, lLength);
					::SafeArrayUnaccessData(psa);
				}
			}
		}
		else if (data.vt == (VT_ARRAY | VT_I2))
		{
			SAFEARRAY* psa = data.parray;
			long lLength = GetSafeArrayLength(psa);

			hr = OutputBufferWriteIEEEBlockLength(lLength * sizeof(short));
			if (SUCCEEDED(hr))
			{
				short* prguch;
				hr = ::SafeArrayAccessData(psa, (void**) &prguch);
				if (SUCCEEDED(hr))
				{
					hr = OutputBufferWriteShortArray(prguch, lLength);
					::SafeArrayUnaccessData(psa);
				}
			}
		}
		else if (data.vt == (VT_ARRAY | VT_I4))
		{
			SAFEARRAY* psa = data.parray;
			long lLength = GetSafeArrayLength(psa);

			hr = OutputBufferWriteIEEEBlockLength(lLength * sizeof(long));
			if (SUCCEEDED(hr))
			{
				long* prguch;
				hr = ::SafeArrayAccessData(psa, (void**) &prguch);
				if (SUCCEEDED(hr))
				{
					hr = OutputBufferWriteLongArray(prguch, lLength);
					::SafeArrayUnaccessData(psa);
				}
			}
		}
		else if (data.vt == (VT_ARRAY | VT_R4))
		{
			SAFEARRAY* psa = data.parray;
			long lLength = GetSafeArrayLength(psa);

			hr = OutputBufferWriteIEEEBlockLength(lLength * sizeof(float));
			if (SUCCEEDED(hr))
			{
				float* prguch;
				hr = ::SafeArrayAccessData(psa, (void**) &prguch);
				if (SUCCEEDED(hr))
				{
					hr = OutputBufferWriteFloatArray(prguch, lLength);
					::SafeArrayUnaccessData(psa);
				}
			}
		}
		else if (data.vt == (VT_ARRAY | VT_R8))
		{
			SAFEARRAY* psa = data.parray;
			long lLength = GetSafeArrayLength(psa);

			hr = OutputBufferWriteIEEEBlockLength(lLength * sizeof(double));
			if (SUCCEEDED(hr))
			{
				double* prguch;
				hr = ::SafeArrayAccessData(psa, (void**) &prguch);
				if (SUCCEEDED(hr))
				{
					hr = OutputBufferWriteDoubleArray(prguch, lLength);
					::SafeArrayUnaccessData(psa);
				}
			}
		}
		else
		{
			ATLASSERT(FALSE); // Other types not supported
			hr = E_FAIL;
		}
	}

	if (SUCCEEDED(hr))
	{
		if (bFlushAndEnd)
		{
			hr = OutputBufferFlush(true);
		}
	}

	return hr;
}

inline HRESULT CVisaIOProvider::ioprvFormattedIOReadString(CString& strData)
{
	IO_METHOD_START();

	HRESULT hr = S_OK;

	NTL_UNUSED(strData);
	ATLASSERT(FALSE); // TODO: Implement this
	hr = E_NOTIMPL;

	return hr;
}

inline HRESULT CVisaIOProvider::ioprvFormattedIOReadNumber(NtlAsciiType type, VARIANT* pData, bool bFlushToEnd)
{
	IO_METHOD_START();

	HRESULT hr = S_OK;

	NTL_UNUSED(type);
	NTL_UNUSED(pData);
	NTL_UNUSED(bFlushToEnd);
	ATLASSERT(FALSE); // TODO: Implement this
	hr = E_NOTIMPL;

	return hr;
}

inline HRESULT CVisaIOProvider::ioprvFormattedIOReadList(NtlAsciiType type, const CString& strListSeparator, VARIANT* pData)
{
	IO_METHOD_START();

	HRESULT hr = S_OK;

	CComBSTR bstrResult;
	hr = ioprvMessageIOReadString(64000, &bstrResult);
	if (SUCCEEDED(hr))
	{
		CString strResult(bstrResult);

		switch (type)
		{
			case NtlAsciiType_R8:
			{
				// Create the safearray
				CComSafeArray<double> csaResults;
				csaResults.Create();

				// Parse the results
				bool bLastValue = false;
				int nStart = 0;
				while (!bLastValue)
				{
					CString strValue;
					bLastValue = GetNextSeparatedValue(strResult, strListSeparator, nStart, strValue);

					double d = _tcstod(strValue, NULL);

					// Add to safearray
					csaResults.Add(d);
				}

				// Return the safearray
				VARIANT v;
				::VariantInit(&v);
				v.parray = csaResults.Detach();
				v.vt = VT_ARRAY | VT_R8;
				
				*pData = v;

				break;
			}

			case NtlAsciiType_R4:
			{
				// Create the safearray
				CComSafeArray<float> csaResults;
				csaResults.Create();

				// Parse the results
				bool bLastValue = false;
				int nStart = 0;
				while (!bLastValue)
				{
					CString strValue;
					bLastValue = GetNextSeparatedValue(strResult, strListSeparator, nStart, strValue);

					float d = (float)_tcstod(strValue, NULL);

					// Add to safearray
					csaResults.Add(d);
				}

				// Return the safearray
				VARIANT v;
				::VariantInit(&v);
				v.parray = csaResults.Detach();
				v.vt = VT_ARRAY | VT_R4;
				
				*pData = v;

				break;
			}

			default:
			{
				hr = E_NOTIMPL;
				ATLASSERT(FALSE); // TODO: Implement this
				break;
			}
		}
	}

	return hr;
}

inline HRESULT CVisaIOProvider::ioprvFormattedIOReadIEEEBlock(NtlBinaryType type, VARIANT* pData, bool bSeekToBlock, bool bFlushToEnd)
{
	NTL_UNUSED(bSeekToBlock);
	NTL_UNUSED(bFlushToEnd);

	IO_METHOD_START();

	if (m_hModule == NULL)
		return E_FAIL;

	if (m_procRead == NULL)
		return E_FAIL;

	HRESULT hr = S_OK;

	ViUInt32 count;
	ViStatus status;

	vector<BYTE> vecSizeLength;
	vecSizeLength.resize(2);
	status = (m_procRead)(m_session, &vecSizeLength[0], 2, &count);
	if (status == VI_SUCCESS || status == VI_SUCCESS_TERM_CHAR || status == VI_SUCCESS_MAX_CNT)
	{
		int nSizeLength = vecSizeLength[1] - '0';

		vector<BYTE> vecSize;
		vecSize.resize(nSizeLength + 1);
		status = (m_procRead)(m_session, &vecSize[0], nSizeLength, &count);
		if (status == VI_SUCCESS || status == VI_SUCCESS_MAX_CNT)
		{
			vecSize[nSizeLength] = 0;
			int nSize = atol((char*)&vecSize[0]);

			vector<BYTE> vecData;
			vecData.resize(nSize);
			status = (m_procRead)(m_session, &vecData[0], nSize, &count);
			if (status == VI_SUCCESS || status == VI_SUCCESS_MAX_CNT)
			{
				switch (type)
				{
					case NtlBinaryType_R8:
					{
						int count = nSize / sizeof(double);
						CComSafeArray<double> csaResults;
						csaResults.Create(count);
						for (int i = 0; i < count; i++)
						{
							double val;
							memcpy(&val, &vecData[i * sizeof(double)], sizeof(double));
							csaResults.SetAt(i, val);
						}

						// Return the safearray
						VARIANT v;
						::VariantInit(&v);
						v.parray = csaResults.Detach();
						v.vt = VT_ARRAY | VT_R8;
						
						*pData = v;

						break;
					}
					case NtlBinaryType_R4:
					{
						int count = nSize / sizeof(float);
						CComSafeArray<float> csaResults;
						csaResults.Create(count);
						for (int i = 0; i < count; i++)
						{
							float val;
							memcpy(&val, &vecData[i * sizeof(float)], sizeof(float));
							csaResults.SetAt(i, val);
						}

						// Return the safearray
						VARIANT v;
						::VariantInit(&v);
						v.parray = csaResults.Detach();
						v.vt = VT_ARRAY | VT_R4;
						
						*pData = v;

						break;
					}
					default:
					{
						hr = E_NOTIMPL;
						ATLASSERT(FALSE); // TODO: Implement this
						break;
					}
				}
			}

			// TODO: Figure out what that last byte is.  We need to read it otherwise we get query interrupted.
			BYTE tempData;
			ViStatus tempStatus = (m_procRead)(m_session, &tempData, 1, &count);
			NTL_UNUSED(tempStatus);
		}
		else
		{
			hr = TranslateErrorCode(status);
		}
	}
	else
	{
		hr = TranslateErrorCode(status);
	}

	return hr;
}

inline HRESULT CVisaIOProvider::ioprvFormattedIOFlushWrite(bool bSendEND)
{
	IO_METHOD_START();

	HRESULT hr = S_OK;

	hr = OutputBufferFlush(bSendEND);

	return hr;
}

inline HRESULT CVisaIOProvider::ioprvFormattedIOFlushRead()
{
	IO_METHOD_START();

	HRESULT hr = S_OK;

	ATLASSERT(FALSE); // TODO: Implement this
	hr = E_NOTIMPL;

	return hr;
}

inline HRESULT CVisaIOProvider::ioprvFormattedIOSetBufferSize(NtlBufferMask mask, long lSize)
{
	IO_METHOD_START();

	HRESULT hr = S_OK;

	if ((mask & NTLIO_IN_BUF) != 0)
	{
		m_lInputBufferSize = lSize;
	}
	
	if ((mask & NTLIO_OUT_BUF) != 0)
	{
		m_lOutputBufferSize = lSize;
	}

	// Next time the buffer is flushed, it will be set to the new size

	return hr;
}

inline HRESULT CVisaIOProvider::ioprvRegisterIn8(UINT16 space, UINT32 offset, UINT8* val)
{
	IO_METHOD_START();

	if (m_hModule == NULL)
		return E_FAIL;

	if (m_procIn8 == NULL)
		return E_FAIL;

	if (val == NULL)
		return E_POINTER;
	*val = 0;

	HRESULT hr = S_OK;

	ViStatus status = (m_procIn8)(m_session, space, offset, val);
	if (status != VI_SUCCESS)
	{
		hr = TranslateErrorCode(status);
	}

	return hr;
}

inline HRESULT CVisaIOProvider::ioprvRegisterOut8(UINT16 space, UINT32 offset, UINT8 val)
{
	IO_METHOD_START();

	if (m_hModule == NULL)
		return E_FAIL;

	if (m_procOut8 == NULL)
		return E_FAIL;

	HRESULT hr = S_OK;

	ViStatus status = (m_procOut8)(m_session, space, offset, val);
	if (status != VI_SUCCESS)
	{
		hr = TranslateErrorCode(status);
	}

	return hr;
}

inline HRESULT CVisaIOProvider::ioprvRegisterIn16(UINT16 space, UINT32 offset, UINT16* val)
{
	IO_METHOD_START();

	if (m_hModule == NULL)
		return E_FAIL;

	if (m_procIn16 == NULL)
		return E_FAIL;

	if (val == NULL)
		return E_POINTER;
	*val = 0;

	HRESULT hr = S_OK;

	ViStatus status = (m_procIn16)(m_session, space, offset, val);
	if (status != VI_SUCCESS)
	{
		hr = TranslateErrorCode(status);
	}

	return hr;
}

inline HRESULT CVisaIOProvider::ioprvRegisterOut16(UINT16 space, UINT32 offset, UINT16 val)
{
	IO_METHOD_START();

	if (m_hModule == NULL)
		return E_FAIL;

	if (m_procOut16 == NULL)
		return E_FAIL;

	HRESULT hr = S_OK;

	ViStatus status = (m_procOut16)(m_session, space, offset, val);
	if (status != VI_SUCCESS)
	{
		hr = TranslateErrorCode(status);
	}

	return hr;
}

inline HRESULT CVisaIOProvider::ioprvRegisterIn32(UINT16 space, UINT32 offset, UINT32* val)
{
	IO_METHOD_START();

	if (m_hModule == NULL)
		return E_FAIL;

	if (m_procIn32 == NULL)
		return E_FAIL;

	if (val == NULL)
		return E_POINTER;
	*val = 0;

	HRESULT hr = S_OK;

	ViStatus status = (m_procIn32)(m_session, space, offset, (ViUInt32*)val);
	if (status != VI_SUCCESS)
	{
		hr = TranslateErrorCode(status);
	}

	return hr;
}

inline HRESULT CVisaIOProvider::ioprvRegisterOut32(UINT16 space, UINT32 offset, UINT32 val)
{
	IO_METHOD_START();

	if (m_hModule == NULL)
		return E_FAIL;

	if (m_procOut32 == NULL)
		return E_FAIL;

	HRESULT hr = S_OK;

	ViStatus status = (m_procOut32)(m_session, space, offset, val);
	if (status != VI_SUCCESS)
	{
		hr = TranslateErrorCode(status);
	}

	return hr;
}

inline HRESULT CVisaIOProvider::ioprvRegisterMoveIn8(UINT16 space, UINT32 offset, UINT32 length, UINT8 buf[])
{
	IO_METHOD_START();

	if (m_hModule == NULL)
		return E_FAIL;

	if (m_procMoveIn8 == NULL)
		return E_FAIL;

	if (buf == NULL)
		return E_INVALIDARG;

	HRESULT hr = S_OK;

	ViStatus status = (m_procMoveIn8)(m_session, space, offset, length, buf);
	if (status != VI_SUCCESS)
	{
		hr = TranslateErrorCode(status);
	}

	return hr;
}

inline HRESULT CVisaIOProvider::ioprvRegisterMoveOut8(UINT16 space, UINT32 offset, UINT32 length, UINT8 buf[])
{
	IO_METHOD_START();

	if (m_hModule == NULL)
		return E_FAIL;

	if (m_procMoveOut8 == NULL)
		return E_FAIL;

	if (buf == NULL)
		return E_INVALIDARG;

	HRESULT hr = S_OK;

	ViStatus status = (m_procMoveOut8)(m_session, space, offset, length, buf);
	if (status != VI_SUCCESS)
	{
		hr = TranslateErrorCode(status);
	}

	return hr;
}

inline HRESULT CVisaIOProvider::ioprvRegisterMoveIn16(UINT16 space, UINT32 offset, UINT32 length, UINT16 buf[])
{
	IO_METHOD_START();

	if (m_hModule == NULL)
		return E_FAIL;

	if (m_procMoveIn16 == NULL)
		return E_FAIL;

	if (buf == NULL)
		return E_INVALIDARG;

	HRESULT hr = S_OK;

	ViStatus status = (m_procMoveIn16)(m_session, space, offset, length, buf);
	if (status != VI_SUCCESS)
	{
		hr = TranslateErrorCode(status);
	}

	return hr;
}

inline HRESULT CVisaIOProvider::ioprvRegisterMoveOut16(UINT16 space, UINT32 offset, UINT32 length, UINT16 buf[])
{
	IO_METHOD_START();

	if (m_hModule == NULL)
		return E_FAIL;

	if (m_procMoveOut16 == NULL)
		return E_FAIL;

	if (buf == NULL)
		return E_INVALIDARG;

	HRESULT hr = S_OK;

	ViStatus status = (m_procMoveOut16)(m_session, space, offset, length, buf);
	if (status != VI_SUCCESS)
	{
		hr = TranslateErrorCode(status);
	}

	return hr;
}

inline HRESULT CVisaIOProvider::ioprvRegisterMoveIn32(UINT16 space, UINT32 offset, UINT32 length, UINT32 buf[])
{
	IO_METHOD_START();

	if (m_hModule == NULL)
		return E_FAIL;

	if (m_procMoveIn32 == NULL)
		return E_FAIL;

	if (buf == NULL)
		return E_INVALIDARG;

	HRESULT hr = S_OK;

	ViStatus status = (m_procMoveIn32)(m_session, space, offset, length, (ViUInt32*)buf);
	if (status != VI_SUCCESS)
	{
		hr = TranslateErrorCode(status);
	}

	return hr;
}

inline HRESULT CVisaIOProvider::ioprvRegisterMoveOut32(UINT16 space, UINT32 offset, UINT32 length, UINT32 buf[])
{
	IO_METHOD_START();

	if (m_hModule == NULL)
		return E_FAIL;

	if (m_procMoveOut32 == NULL)
		return E_FAIL;

	if (buf == NULL)
		return E_INVALIDARG;

	HRESULT hr = S_OK;

	ViStatus status = (m_procMoveOut32)(m_session, space, offset, length, (ViUInt32*)buf);
	if (status != VI_SUCCESS)
	{
		hr = TranslateErrorCode(status);
	}

	return hr;
}

inline HRESULT CVisaIOProvider::LoadVisaDllEntryPoints(HMODULE hModule)
{
	HRESULT hr = S_OK;

	// Load the DLL in memory and setup all the function pointers
	if (hModule != NULL)
	{
		m_procClear = (PROC_CLEAR)::GetProcAddress(hModule, "viClear");
		m_procClose = (PROC_CLOSE)::GetProcAddress(hModule, "viClose");
		m_procFindRsrc = (PROC_FIND_RSRC)::GetProcAddress(hModule, "viFindRsrc");
		m_procFlush = (PROC_FLUSH)::GetProcAddress(hModule, "viFlush");
		m_procGetAttribute = (PROC_GET_ATTRIBUTE)::GetProcAddress(hModule, "viGetAttribute");
		m_procOpenDefaultRM = (PROC_OPEN_DEFAULT_RM)::GetProcAddress(hModule, "viOpenDefaultRM");
		m_procOpen = (PROC_OPEN)::GetProcAddress(hModule, "viOpen");
		m_procPrintf = (PROC_PRINTF)::GetProcAddress(hModule, "viPrintf");
		m_procQueryf = (PROC_QUERYF)::GetProcAddress(hModule, "viQueryf");
		m_procRead = (PROC_READ)::GetProcAddress(hModule, "viRead");
		m_procReadSTB = (PROC_READ_STB)::GetProcAddress(hModule, "viReadSTB");
		m_procSetAttribute = (PROC_SET_ATTRIBUTE)::GetProcAddress(hModule, "viSetAttribute");
		m_procSetBuf = (PROC_SET_BUF)::GetProcAddress(hModule, "viSetBuf");
		m_procWrite = (PROC_WRITE)::GetProcAddress(hModule, "viWrite");
		m_procIn8 = (PROC_IN8)::GetProcAddress(hModule, "viIn8");
		m_procOut8 = (PROC_OUT8)::GetProcAddress(hModule, "viOut8");
		m_procIn16 = (PROC_IN16)::GetProcAddress(hModule, "viIn16");
		m_procOut16 = (PROC_OUT16)::GetProcAddress(hModule, "viOut16");
		m_procIn32 = (PROC_IN32)::GetProcAddress(hModule, "viIn32");
		m_procOut32 = (PROC_OUT32)::GetProcAddress(hModule, "viOut32");
		m_procMoveIn8 = (PROC_MOVEIN8)::GetProcAddress(hModule, "viMoveIn8");
		m_procMoveOut8 = (PROC_MOVEOUT8)::GetProcAddress(hModule, "viMoveOut8");
		m_procMoveIn16 = (PROC_MOVEIN16)::GetProcAddress(hModule, "viMoveIn16");
		m_procMoveOut16 = (PROC_MOVEOUT16)::GetProcAddress(hModule, "viMoveOut16");
		m_procMoveIn32 = (PROC_MOVEIN32)::GetProcAddress(hModule, "viMoveIn32");
		m_procMoveOut32 = (PROC_MOVEOUT32)::GetProcAddress(hModule, "viMoveOut32");
	}
	else
	{
		hr = E_FAIL;
	}

	return hr;
}

inline HRESULT CVisaIOProvider::FreeVisaLibrary()
{
	HRESULT hr = S_OK;

	if (m_hModule != NULL)
	{
		::FreeLibrary(m_hModule);
		m_hModule = NULL;

		m_procClear = NULL;
		m_procClose = NULL;
		m_procFindRsrc = NULL;
		m_procFlush = NULL;
		m_procGetAttribute = NULL;
		m_procOpenDefaultRM = NULL;
		m_procOpen = NULL;
		m_procPrintf = NULL;
		m_procQueryf = NULL;
		m_procRead = NULL;
		m_procReadSTB = NULL;
		m_procSetAttribute = NULL;
		m_procSetBuf = NULL;
		m_procWrite = NULL;
		m_procIn8 = NULL;
		m_procOut8 = NULL;
		m_procIn16 = NULL;
		m_procOut16 = NULL;
		m_procIn32 = NULL;
		m_procOut32 = NULL;
		m_procMoveIn8 = NULL;
		m_procMoveOut8 = NULL;
		m_procMoveIn16 = NULL;
		m_procMoveOut16 = NULL;
		m_procMoveIn32 = NULL;
		m_procMoveOut32 = NULL;
	}

	return hr;
}

inline ViUInt16 CVisaIOProvider::TranslateBufferMask(NtlBufferMask mask)
{
	ViUInt16 visaMask = VI_READ_BUF | VI_WRITE_BUF;

	switch (mask)
	{
		case NTLIO_IN_BUF:			visaMask = VI_WRITE_BUF;				break;
		case NTLIO_OUT_BUF:			visaMask = VI_READ_BUF;					break;
		case NTLIO_IN_AND_OUT_BUFS:	visaMask = VI_WRITE_BUF | VI_READ_BUF;	break;
	}

	return visaMask;
}

inline bool CVisaIOProvider::GetNextSeparatedValue(const CString& strSource, const CString& strSeparator, int& nStart, CString& strValue)
{
	// The source string is separated by strSeparator
	// This method will return the next string value from the start position
	// Assumes that the last value is not followed by a separator

	bool bLastValue = false;

	int nEnd = strSource.Find(strSeparator, nStart);
	if (nEnd != -1)
	{
		strValue = strSource.Mid(nStart, nEnd - nStart);
		nStart = nEnd + strSeparator.GetLength();
	}
	else
	{
		// The last value
		strValue = strSource.Mid(nStart);
		nStart = 0;
		bLastValue = true;
	}

	return bLastValue;
}

inline HRESULT CVisaIOProvider::TranslateErrorCode(ViStatus status)
{
	HRESULT hr = E_FAIL;

	// Translate VISA-COM specific error codes into NTL error codes
	if (status == VI_ERROR_TMO)
	{
		hr = E_NTL_IO_TIMEOUT;
	}

	return hr;
}

inline HRESULT CVisaIOProvider::OutputBufferWriteByte(BYTE val)
{
	CString strItem;
	strItem.Format(_T("%d"), val);

	HRESULT hr = OutputBufferWriteString(strItem);

	return hr;
}

inline HRESULT CVisaIOProvider::OutputBufferWriteShort(short val)
{
	CString strItem;
	strItem.Format(_T("%d"), val);

	HRESULT hr = OutputBufferWriteString(strItem);

	return hr;
}

inline HRESULT CVisaIOProvider::OutputBufferWriteLong(long val)
{
	CString strItem;
	strItem.Format(_T("%d"), val);

	HRESULT hr = OutputBufferWriteString(strItem);

	return hr;
}

inline HRESULT CVisaIOProvider::OutputBufferWriteFloat(float val)
{
	CString strItem;
	strItem.Format(_T("%.15g"), val);

	HRESULT hr = OutputBufferWriteString(strItem);

	return hr;
}

inline HRESULT CVisaIOProvider::OutputBufferWriteDouble(double val)
{
	CString strItem;
	strItem.Format(_T("%.15g"), val);

	HRESULT hr = OutputBufferWriteString(strItem);

	return hr;
}

inline HRESULT CVisaIOProvider::OutputBufferWriteString(const CString& strSource)
{
	CT2A szTemp(strSource);
	LPCSTR pszText = szTemp.m_psz;
	int nLength = strSource.GetLength();

	// TODO: need more efficient way of doing this
	for (int i = 0; i < nLength; i++)
	{
		m_vecOutputBuffer.push_back(pszText[i]);
	}

	return S_OK;
}

inline HRESULT CVisaIOProvider::OutputBufferWriteLittleEndianData(BYTE* pData, int nLength)
{
	size_t nPreviousLength = m_vecOutputBuffer.size();
	
	m_vecOutputBuffer.resize(nPreviousLength + nLength);
	memcpy(&m_vecOutputBuffer[nPreviousLength], pData, nLength);

	return S_OK;
}

inline HRESULT CVisaIOProvider::OutputBufferWriteBigEndianData(BYTE* pData, int nLength, int nElementSize)
{
	int nElementCount = nLength / nElementSize;

	BYTE* pIterator = pData;
	for (int i = 0; i < nElementCount; i++)
	{
		pIterator += nElementSize;
		for (int j = 0; j < nElementSize; j++)
		{
			pIterator--;
			m_vecOutputBuffer.push_back(*pIterator);
		}
		pIterator += nElementSize;
	}

	return S_OK;
}

inline HRESULT CVisaIOProvider::OutputBufferWriteIEEEBlockLength(int nLength)
{
	HRESULT hr = S_OK;

	CString strLength;
	strLength.Format(_T("%d"), nLength);

	CString strText;
	strText.Format(_T("#%d%s"), strLength.GetLength(), LPCTSTR(strLength));

	hr = OutputBufferWriteString(strText);

	return hr;
}

inline HRESULT CVisaIOProvider::OutputBufferWriteByteArray(BYTE* values, int nLength)
{
	HRESULT hr = S_OK;

	hr = OutputBufferWriteLittleEndianData(values, nLength);

	return hr;
}

inline HRESULT CVisaIOProvider::OutputBufferWriteShortArray(short* values, int nLength)
{
	HRESULT hr = S_OK;

	if (m_bBigEndian)
	{
		hr = OutputBufferWriteBigEndianData(reinterpret_cast<BYTE*>(values), nLength * sizeof(short), sizeof(short));
	}
	else
	{
		hr = OutputBufferWriteLittleEndianData(reinterpret_cast<BYTE*>(values), nLength * sizeof(short));
	}

	return hr;
}

inline HRESULT CVisaIOProvider::OutputBufferWriteLongArray(long* values, int nLength)
{
	HRESULT hr = S_OK;

	if (m_bBigEndian)
	{
		hr = OutputBufferWriteBigEndianData(reinterpret_cast<BYTE*>(values), nLength * sizeof(long), sizeof(long));
	}
	else
	{
		hr = OutputBufferWriteLittleEndianData(reinterpret_cast<BYTE*>(values), nLength * sizeof(long));
	}

	return hr;
}

inline HRESULT CVisaIOProvider::OutputBufferWriteFloatArray(float* values, int nLength)
{
	HRESULT hr = S_OK;

	if (m_bBigEndian)
	{
		hr = OutputBufferWriteBigEndianData(reinterpret_cast<BYTE*>(values), nLength * sizeof(float), sizeof(float));
	}
	else
	{
		hr = OutputBufferWriteLittleEndianData(reinterpret_cast<BYTE*>(values), nLength * sizeof(float));
	}

	return hr;
}

inline HRESULT CVisaIOProvider::OutputBufferWriteDoubleArray(double* values, int nLength)
{
	HRESULT hr = S_OK;

	if (m_bBigEndian)
	{
		hr = OutputBufferWriteBigEndianData(reinterpret_cast<BYTE*>(values), nLength * sizeof(double), sizeof(double));
	}
	else
	{
		hr = OutputBufferWriteLittleEndianData(reinterpret_cast<BYTE*>(values), nLength * sizeof(double));
	}

	return hr;
}

inline HRESULT CVisaIOProvider::OutputBufferFlush(bool bSendEND)
{
	HRESULT hr = S_OK;

	// Save the SendEND
	bool bPreviousSendEND = true;
	ioprvMessageIOGetSendEndEnabled(&bPreviousSendEND);
	
	// Set the SendEND
	ioprvMessageIOSetSendEndEnabled(bSendEND);

	long lLength = static_cast<long>(m_vecOutputBuffer.size());

	// Create a SAFEARRAY to pass to the IO
	SAFEARRAY* psa = ::SafeArrayCreateVector(VT_UI1, 0, lLength);
	if (psa != NULL)
	{
		// Copy contents of output buffer into SAFEARRAY
		hr = CSafeArray<BYTE>::Read(psa, &m_vecOutputBuffer[0], lLength);
		if (SUCCEEDED(hr))
		{
			// Send SAFEARRAY to the IO
			long lActual = 0;
			hr = ioprvMessageIOWrite(psa, lLength, &lActual);
		}

		::SafeArrayDestroy(psa);
	}
	else
	{
		hr = E_OUTOFMEMORY;
	}

	// Restore the SendEND
	ioprvMessageIOSetSendEndEnabled(bPreviousSendEND);

	m_vecOutputBuffer.resize(0);
	m_vecOutputBuffer.reserve(m_lOutputBufferSize);

	return hr;
}

#endif // #endif __NTLIOPROVIDERVISA_INCLUDED
