//
// Copyright (c) 2002
// Ronald Kevin Burton
//
// Z poniższym kodem nie jest związana żadna gwarancja poprawności działania.
// Program został dołączony do książki ".NET CLR. Księga eksperta" w celu
// ilustracji koncepcji i zasad przedstawionych w tej książce. Program może być 
// używany na własne ryzyko.
//
// Przyznaje się prawo do użycia lub kopiowania tego oprogramowania do dowolnego celu
// bez konieczności ponoszenia żadnych opłat pod warunkiem, że powyższe uwagi zostaną 
// zachowane we wszystkich kopiach. Przyznaje się także prawo do modyfikacji kodu
// i dystrybucji zmodyfikowanego kodu pod warunkiem zachowania powyższych uwag
// oraz dołączenia informacji mówiącej o modyfikacji kodu.
//
// 
// TypeTest.cpp : Implementacja CTypeTest

#include "stdafx.h"
#include "TypeTest.h"
#include <sstream>


UINT __stdcall CTypeTest::ProcessCallback(LPVOID pArg)
{
	CTypeTest *pTypeTest = reinterpret_cast<CTypeTest *>(pArg);
	
	// COM inicjalizuje ten wątek.
#ifdef _ATL_APARTMENT_THREADED
	::CoInitialize(NULL);
#else
	::CoInitializeEx(NULL, COINIT_MULTITHREADED);
#endif
	DWORD dwWaitReturn = 0;
	DWORD count = 0;
	HANDLE hArray[1];
	hArray[0] = pTypeTest->hDatagramThreadTerminateEvent;

	while(1)
	{
		dwWaitReturn = MsgWaitForMultipleObjects(sizeof(hArray)/sizeof(HANDLE), hArray,
			                                     FALSE,
			                                     1000,
			                                     QS_POSTMESSAGE | QS_SENDMESSAGE);
		if(dwWaitReturn == WAIT_TIMEOUT)
		{
			pTypeTest->Callback(count++);
		}
		else if(dwWaitReturn == WAIT_OBJECT_0)
		{
			ATLTRACE("ProcessCallback przerywa pracę na żądanie.\n");
			::CoUninitialize();
			return 1;
		}
		else if(dwWaitReturn == (WAIT_OBJECT_0 + 1))
		{
			// Komunikat Windows
			MSG msg;
			while(PeekMessage(&msg,
				              0,
				              0,
				              0,
				              PM_REMOVE))
			{
				if(msg.message == WM_QUIT)
					ATLTRACE(_T("WM_QUIT odebrany\n"));
				DispatchMessage(&msg);
			}
		}
	}
	
	::CoUninitialize();
	
	return 0;
}

// CTypeTest

STDMETHODIMP CTypeTest::get_Name(BSTR* pVal)
{
	*pVal = name.copy();
	return S_OK;
}

STDMETHODIMP CTypeTest::put_Name(BSTR newVal)
{
	name = newVal;
	NameCallback(newVal);
	return S_OK;
}

STDMETHODIMP CTypeTest::get_Date(DATE* pVal)
{
	*pVal = date;
	return S_OK;
}

STDMETHODIMP CTypeTest::put_Date(DATE newVal)
{
	date = newVal;
	DateCallback(newVal);
	return S_OK;
}

STDMETHODIMP CTypeTest::get_Color(VARIANT* pVal)
{
	*pVal = color;
	return S_OK;
}

STDMETHODIMP CTypeTest::put_Color(VARIANT newVal)
{
	if(newVal.vt == VT_DISPATCH)
	{
		color = newVal;
		ColorCallback(newVal);
		return S_OK;
	}
	else
	{
		return E_INVALIDARG;
	}
}

STDMETHODIMP CTypeTest::get_Currency(CY* pVal)
{
	*pVal = currency;
	return S_OK;
}

STDMETHODIMP CTypeTest::put_Currency(CY newVal)
{
	currency = newVal;
	CurrencyCallback(newVal);
	return S_OK;
}

STDMETHODIMP CTypeTest::get_Object(VARIANT* pVal)
{
	*pVal = object;
	return S_OK;
}

STDMETHODIMP CTypeTest::put_Object(VARIANT newVal)
{
	object = newVal;
	ObjectCallback(newVal);
	return S_OK;
}

STDMETHODIMP CTypeTest::ErrorTest(BSTR bstrError)
{
	_bstr_t error = bstrError;
	ComErrorIterator it = errorMap.find((LPCWSTR)error);
	HRESULT hr = E_NOTIMPL;
	if(it == errorMap.end())
		hr = E_FAIL;
	else
	{
		hr = it->second;
	}

	std::wostringstream werrmsg;
	werrmsg << std::endl;
	werrmsg << L"To jest test systemu błędów COM." << std::endl;
	werrmsg << L"To NIE JEST prawdziwy błąd." << std::endl;
	werrmsg << L"W przypadku wystąpienia błędu należy zapewnić jego obsługę." << std::endl;

	Error(werrmsg.str().c_str(),
		  __uuidof(ITypeTest),
		  hr);

	return hr;
}

STDMETHODIMP CTypeTest::get_Array(VARIANT* pVal)
{
	::VariantInit(pVal);
	pVal->vt = VT_ARRAY | array.GetType();
	pVal->parray = *(array.GetSafeArrayPtr());
	return S_OK;
}

STDMETHODIMP CTypeTest::put_Array(VARIANT newVal)
{
	if(newVal.vt | VT_ARRAY)
	{
		array = newVal.parray;
		ArrayCallback(newVal);
		return S_OK;
	}
	else
	{
		return E_INVALIDARG;
	}
}

STDMETHODIMP CTypeTest::get_Enumerator(IEnumVARIANT** pVal)
{
	long lCount = array.GetCount();

	VARIANT *pVariant = new VARIANT[lCount];

	// Wypełnienie tymczasowej tablicy obiektami
	for (int i = 0; 
		i < lCount;
		++i)
	{
		::VariantInit(&pVariant[i]);
		pVariant[i] = array.GetAt(i);
	}

	// Dzięki temu typedef poniższy kod jest bardziej czytelny!
	// Potrzebny jest enumerator VARIANT
	typedef CComObject< CComEnum< IEnumVARIANT,
		                &IID_IEnumVARIANT,
		                VARIANT,
		                _Copy<VARIANT> > > EnumVar;

	// Utworzenie nowego egzemplarza obiektu
	EnumVar* pVar = new EnumVar;

	// Inicjalizacja przy użyciu obiektów
	pVar->Init(&pVariant[0],
		&pVariant[i],
		NULL, AtlFlagCopy);

	// Teraz zwolnienie obiektów z tymczasowej tablicy
	lCount--;
	while(lCount >= 0)
	{
		VariantClear(&pVariant[lCount]);
		lCount--;
	}

	delete [] pVariant;

	// Zwrócenie IUnknown dla enumeratora
	pVar->QueryInterface(IID_IUnknown, reinterpret_cast<void**>(pVal));
	// Celowo nie usuwamy pVar!

	return S_OK;

}
