#pragma once

_ATL_FUNC_INFO _infoOnData = { CC_STDCALL, VT_I4, 3,{ VT_I4, VT_DISPATCH, VT_BSTR } };
_ATL_FUNC_INFO _infoOnDataComplete = { CC_STDCALL, VT_I4, 2,{ VT_I4, VT_BSTR } };
_ATL_FUNC_INFO _infoOnClear = { CC_STDCALL, VT_I4, 1,{ VT_BSTR } };
_ATL_FUNC_INFO _infoOnClose = { CC_STDCALL, VT_I4, 1,{ VT_BSTR } };
_ATL_FUNC_INFO _infoOnInsert = { CC_STDCALL, VT_I4, 2,{ VT_DISPATCH, VT_BSTR } };
_ATL_FUNC_INFO _infoOnModify = { CC_STDCALL, VT_I4, 2,{ VT_DISPATCH, VT_BSTR } };
_ATL_FUNC_INFO _infoOnDelete = { CC_STDCALL, VT_I4, 2,{ VT_I4, VT_BSTR } };

IID DIID__ITableEvents = __uuidof(_ITableEvents);

class CTableEvents : public IDispEventSimpleImpl<1, CTableEvents, &DIID__ITableEvents>
{
public:
	CTableEvents()
	{}

	BEGIN_SINK_MAP(CTableEvents)
		SINK_ENTRY_INFO(1, DIID__ITableEvents, 1, OnData, &_infoOnData)
		SINK_ENTRY_INFO(1, DIID__ITableEvents, 2, OnDataComplete, &_infoOnDataComplete)
		SINK_ENTRY_INFO(1, DIID__ITableEvents, 3, OnClear, &_infoOnClear)
		SINK_ENTRY_INFO(1, DIID__ITableEvents, 4, OnClose, &_infoOnClose)
		SINK_ENTRY_INFO(1, DIID__ITableEvents, 5, OnInsert, &_infoOnInsert)
		SINK_ENTRY_INFO(1, DIID__ITableEvents, 6, OnModify, &_infoOnModify)
		SINK_ENTRY_INFO(1, DIID__ITableEvents, 7, OnDelete, &_infoOnDelete)
	END_SINK_MAP()


	/*	Пример обработки истории	*/
	//HRESULT  __stdcall OnData(long Code, IDispatch* pDataRowSet, BSTR TableName)
	//{
	//	IDataRowSetPtr spDataRowSet(pDataRowSet);
	//	long lRowSetSize = spDataRowSet->GetCount();

	//	std::string String;
	//	std::string s9(1, 9), s10(1, 10); 
	//	VARIANT	indexVar, Var, dstVar;
	//	Var.vt		= VT_EMPTY;
	//	indexVar.vt = VT_I4;
	//	// здесь важно, чтобы dstVar имел тип не VT_EMPTY для корректной работы VariantChangeType
	//	dstVar.vt	= VT_I4;

	//	for(long i = 0; i < lRowSetSize; ++i)
	//	{
	//		IDataRowPtr spDataRow(spDataRowSet->GetRow(i));

	//		for(long j = 0; j < spDataRow->GetCount(); ++j)
	//		{
	//			indexVar.lVal = j;
	//			if (S_OK == spDataRow->get_Item(&indexVar,&Var)) {
	//				// переводим данные в строковые
	//				::VariantChangeType(&dstVar, &Var, 0, VT_BSTR);

	//				String.append(CW2A(dstVar.bstrVal));
	//				String.append(s9);
	//			}
	//			// очищаем VARIANT
	//			VariantClear(&Var);
	//			VariantClear(&dstVar);
	//		}
	//		String.append(s10);
	//	}

	//	// к примеру, пишем данные в файл через разделители
	//	CAtlFile File;
	//	if ( S_OK == File.Create(file_name, GENERIC_WRITE, FILE_SHARE_READ, OPEN_ALWAYS) )
	//	{
	//		if( S_OK == File.Seek(0, FILE_END) )
	//			File.Write(String.c_str(), (DWORD)String.size());
	//		File.Close();
	//	}
	//	return S_OK;
	//}

	/*
	Пример обработки истории с использованием IDataRowSet2::raw_GetRowBSTRArr.
	Полезно использовать при больших объемах загружаемых данных. Работает значительно
	быстрее варианта выше благодаря отсутствию необходимости приведения типов в цикле
	( строка IDataRowPtr spDataRow(spDataRowSet->GetRow(i)); )
	*/
	HRESULT  __stdcall OnData(long Code, IDispatch* pDataRowSet, BSTR TableName)
	{

		IDataRowSet2Ptr spDataRowSet(pDataRowSet);

		std::string String;	String.reserve(10000);
		std::string s9(1, 9), s10(1, 10);
		VARIANT	indexVar;
		indexVar.vt = VT_I4;

		bstr_t bstrItem;

		long lRowCount = 0;
		spDataRowSet->get_Count(&lRowCount);
		for (long i = 0; i < lRowCount; ++i)
		{
			VARIANT arrayVar;
			arrayVar.vt = VT_EMPTY;
			spDataRowSet->raw_GetRowBSTRArr(i, &arrayVar);

			long lColCount = (long)arrayVar.parray->rgsabound[0].cElements;
			for (long j = 0; j < lColCount; ++j)
			{
				SafeArrayGetElement(arrayVar.parray, &j, bstrItem.GetAddress());
				String.append((const char *)bstrItem);
				String.append(s9);
			}

			SafeArrayDestroyData(arrayVar.parray);
			SafeArrayDestroy(arrayVar.parray);

			String.append(s10);
		}

		return S_OK;
	}

	HRESULT  __stdcall OnDataComplete(long Result, BSTR TableName)
	{
		printf("\tOnDataComplete()[%d]\n", GetCurrentThreadId());
		return S_OK;
	}

	HRESULT  __stdcall OnClear(BSTR TableName)
	{
		printf("\tOnClear()[%d]\n", GetCurrentThreadId());
		return S_OK;
	}

	HRESULT  __stdcall OnClose(BSTR TableName)
	{
		printf("\tOnClose()[%d]\n", GetCurrentThreadId());
		return S_OK;
	}

	HRESULT  __stdcall OnInsert(IDispatch* pNiDataRow, BSTR TableName)
	{
		static unsigned long ins = 0;
		printf("\tOnInsert()[%d]\t\t%d\n", GetCurrentThreadId(), ins++);

		IDataRowPtr spDataRow(pNiDataRow);

		std::string String;
		std::string s9(1, 9), s10(1, 10);
		VARIANT	indexVar, Var, dstVar;
		Var.vt = VT_EMPTY;
		indexVar.vt = VT_I4;
		dstVar.vt = VT_I4;

		for (long j = 0; j < spDataRow->GetCount(); ++j)
		{
			indexVar.lVal = j;
			if (S_OK == spDataRow->get_Item(&indexVar, &Var)) {
				::VariantChangeType(&dstVar, &Var, 0, VT_BSTR);
				String.append(CW2A(dstVar.bstrVal));
				String.append(s9);
			}
			VariantClear(&Var);
			VariantClear(&dstVar);
		}
		String.append(s10);

		return S_OK;
	}

	HRESULT  __stdcall OnModify(IDispatch* pNiDataRow, BSTR TableName)
	{
		printf("\tOnModify()[%d]\n", GetCurrentThreadId());
		return S_OK;
	}

	HRESULT  __stdcall OnDelete(long RecId, BSTR TableName)
	{
		printf("\tOnDelete()[%d]\n", GetCurrentThreadId());
		return S_OK;
	}
};