#pragma once

using namespace ATL;

template <class T>
class CProxy_IScannerEvents : public IConnectionPointImpl<T, &__uuidof(_IScannerEvents), CComDynamicUnkArray>
{
	
public:
	VOID Fire_DocumentScanned(char * const Filename1, char * const Filename2, VARIANT_BOOL AreDeskewed, VARIANT_BOOL* pCancel)
	{
		USES_CONVERSION;
		const auto bstr1 = ::SysAllocString(A2W(Filename1));
		const auto bstr2 = ::SysAllocString(A2W(Filename2));
		Fire_DocumentScanned(bstr1, bstr2, AreDeskewed, pCancel);
		::SysFreeString(bstr1);
		::SysFreeString(bstr2);
	}

	VOID Fire_DocumentScanned(BSTR Filename1, BSTR Filename2, VARIANT_BOOL AreDeskewed, VARIANT_BOOL* pCancel)
	{
		const auto numberOfArgs = 4U;
		
		const auto pT = static_cast<T*>(this);
		const auto pvars = new CComVariant[numberOfArgs];
		const auto nConnections = m_vec.GetSize();
		

		
		for (auto nConnectionIndex = 0; nConnectionIndex < nConnections; nConnectionIndex++)
		{
			pT->Lock();
			const CComPtr<IUnknown> sp = m_vec.GetAt(nConnectionIndex);
			pT->Unlock();
			const auto pDispatch = reinterpret_cast<IDispatch*>(sp.p);

			if (pDispatch != nullptr)
			{
				
				pvars[3] = Filename1;
				pvars[2] = Filename2;
				pvars[1] = AreDeskewed;
				pvars[1].vt = VT_BOOL;
				pvars[0].pboolVal = pCancel;
				pvars[0].vt = VT_BOOL | VT_BYREF;
				DISPPARAMS disp = { pvars, nullptr, numberOfArgs, 0U };

				
				
				pDispatch->Invoke(0x1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, nullptr, nullptr, nullptr);
			}
		}
		delete[] pvars;
	}

	VOID Fire_DocumentScannedToByteArrays(SAFEARRAY* Page1, SAFEARRAY* Page2, VARIANT_BOOL AreDeskewed, VARIANT_BOOL* pCancel)
	{
		const auto numberOfArgs = 4U;
		
		const auto pT = static_cast<T*>(this);
		const auto pvars = new CComVariant[numberOfArgs];
		const auto nConnections = m_vec.GetSize();
		

		
		for (auto nConnectionIndex = 0; nConnectionIndex < nConnections; nConnectionIndex++)
		{
			pT->Lock();
			const CComPtr<IUnknown> sp = m_vec.GetAt(nConnectionIndex);
			pT->Unlock();
			const auto pDispatch = reinterpret_cast<IDispatch*>(sp.p);

			if (pDispatch != nullptr)
			{
				
				pvars[3] = Page1;
				pvars[2] = Page2;
				pvars[1] = AreDeskewed;
				pvars[1].vt = VT_BOOL;
				pvars[0].pboolVal = pCancel;
				pvars[0].vt = VT_BOOL | VT_BYREF;
				DISPPARAMS disp = { pvars, nullptr, numberOfArgs, 0U };

				
				
				pDispatch->Invoke(0x7, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, nullptr, nullptr, nullptr);
			}
		}
		delete[] pvars;
	}

	VOID Fire_DocumentScannedToSharedMemory(BSTR SharedMemoryName, long Offset1, long Length1, long Offset2, long Length2, VARIANT_BOOL AreDeskewed, VARIANT_BOOL* pCancel)
	{
		const auto numberOfArgs = 6U;
		
		const auto pT = static_cast<T*>(this);
		const auto pvars = new CComVariant[numberOfArgs];
		const auto nConnections = m_vec.GetSize();

		
		for (auto nConnectionIndex = 0; nConnectionIndex < nConnections; nConnectionIndex++)
		{
			pT->Lock();
			const auto sp = m_vec.GetAt(nConnectionIndex);
			pT->Unlock();
			const auto pDispatch = reinterpret_cast<IDispatch*>(sp.p);

			if (pDispatch != nullptr)
			{
				
				pvars[5] = Offset1;
				pvars[4] = Length1;
				pvars[3] = Offset2;
				pvars[2] = Length2;
				pvars[1] = AreDeskewed;
				pvars[1].vt = VT_BOOL;
				pvars[0].pboolVal = pCancel;
				pvars[0].vt = VT_BOOL | VT_BYREF;
				DISPPARAMS disp = { pvars, nullptr, numberOfArgs, 0U };

				
				pDispatch->Invoke(0x2, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, nullptr, nullptr, nullptr);
			}
		}
		delete[] pvars; 
	}

	VOID Fire_FeederEmpty()
	{
		logg->debug("Entering Fire_FeederEmpty()");
		const auto numberOfArgs = 0U;
		
		const auto pT = static_cast<T*>(this);
		CComVariant* pvars = nullptr;
		const auto nConnections = m_vec.GetSize();

		
		for (auto nConnectionIndex = 0; nConnectionIndex < nConnections; nConnectionIndex++)
		{
			pT->Lock();
			const CComPtr<IUnknown> sp = m_vec.GetAt(nConnectionIndex);
			pT->Unlock();
			const auto pDispatch = reinterpret_cast<IDispatch*>(sp.p);

			if (pDispatch != nullptr)
			{
				DISPPARAMS disp = { pvars, nullptr, numberOfArgs, 0U };

				
				pDispatch->Invoke(0x3, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, nullptr, nullptr, nullptr);
			}
		}
		delete[] pvars; 
		logg->debug("Leaving Fire_FeederEmpty()");

	}

	VOID Fire_BatchAborted()
	{
		const auto numberOfArgs = 0U;
		
		const auto pT = static_cast<T*>(this);
		CComVariant* pvars = nullptr;
		const auto nConnections = m_vec.GetSize();

		
		for (auto nConnectionIndex = 0; nConnectionIndex < nConnections; nConnectionIndex++)
		{
			pT->Lock();
			const CComPtr<IUnknown> sp = m_vec.GetAt(nConnectionIndex);
			pT->Unlock();
			const auto pDispatch = reinterpret_cast<IDispatch*>(sp.p);

			if (pDispatch != nullptr)
			{
				DISPPARAMS disp = { pvars, nullptr, numberOfArgs, 0U };

				
				pDispatch->Invoke(0x4, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, nullptr, nullptr, nullptr);
			}
		}
		delete[] pvars; 
	}

	VOID Fire_ScanningError(LONG ErrorCode, BSTR ErrorText)
	{
		const auto numberOfArgs = 2U;
		
		const auto pT = static_cast<T*>(this);
		CComVariant* pvars = new CComVariant[numberOfArgs];
		const auto nConnections = m_vec.GetSize();

		
		for (auto nConnectionIndex = 0; nConnectionIndex < nConnections; nConnectionIndex++)
		{
			pT->Lock();
			const CComPtr<IUnknown> sp = m_vec.GetAt(nConnectionIndex);
			pT->Unlock();
			IDispatch* pDispatch = reinterpret_cast<IDispatch*>(sp.p);

			if (pDispatch != nullptr)
			{
				
				pvars[1] = ErrorCode;
				pvars[0] = ErrorText;
				DISPPARAMS disp = { pvars, nullptr, numberOfArgs, 0U };

				
				pDispatch->Invoke(0x5, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, nullptr, nullptr, nullptr);
			}
		}
		delete[] pvars; 
	}

	VOID Fire_NoScannerDetected()
	{
		const auto numberOfArgs = 0U;
		
		const auto pT = static_cast<T*>(this);
		CComVariant* pvars = nullptr;
		const auto nConnections = m_vec.GetSize();

		
		for (auto nConnectionIndex = 0; nConnectionIndex < nConnections; nConnectionIndex++)
		{
			pT->Lock();
			const CComPtr<IUnknown> sp = m_vec.GetAt(nConnectionIndex);
			pT->Unlock();
			const auto pDispatch = reinterpret_cast<IDispatch*>(sp.p);

			if (pDispatch != nullptr)
			{
				DISPPARAMS disp = { pvars, nullptr, numberOfArgs, 0U };

				
				pDispatch->Invoke(0x6, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, nullptr, nullptr, nullptr);
			}
		}
		delete[] pvars; 
	}

};

