/* Generated by Together */
#include "stdafx.h"
#include "../include/ActivationErrors.h"
#include "../include/RequestObserver.h"
#include "RequestData.h"
#include "ContentImp.h"
#include "FutureResponse.h"
#include "RecurrentRequest.h"

namespace OnlineActivation
{
///////////////////////////////////////////////////////////////////////////////
FutureResponse::FutureResponse(std::auto_ptr<RecurrentRequest>& pRequest)
	: m_pRequest(pRequest)
{
	DWORD dwThreadId = 0;
	// create worker thread
	m_hThread.reset(CreateThread(NULL, 0, &ThreadProc, this, CREATE_SUSPENDED, &dwThreadId));
	// check for errors
	if (!m_hThread)
		throw SystemError(GetLastError());
	// impersonate created thread
	handle_t hToken;
	// try to open thread token
	if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_IMPERSONATE, FALSE, &hToken.get_ref()))
	{
		// impersonate created thread
		if (!SetThreadToken(&m_hThread.get_ref(), hToken.get()))
			throw SystemError(GetLastError());
	}
	// start processing
	if (ResumeThread(m_hThread.get()) == DWORD(-1))
		throw SystemError(GetLastError());
}
///////////////////////////////////////////////////////////////////////////////
FutureResponse::~FutureResponse()
{
	try
	{
		Cancel();
	}
	catch (const ActivationError&)
	{
		_ASSERTE("Unexpected exception occurred");
	}
	catch (const std::exception&)
	{
		_ASSERTE("Unexpected exception occurred");
	}
	// wait for thread completion
	WaitForSingleObject(m_hThread.get(), INFINITE);
}
///////////////////////////////////////////////////////////////////////////////
void FutureResponse::Cancel() const
{
	DWORD dwExitCode = ERROR_SUCCESS;
	if (!GetExitCodeThread(m_hThread.get(), &dwExitCode))
		throw SystemError(GetLastError());
	// if thread is running
	if (dwExitCode == STILL_ACTIVE)
	{
		// abort pending request
		m_pRequest->Abort();
	}
}
///////////////////////////////////////////////////////////////////////////////
boost::shared_ptr<Content> FutureResponse::GetContent() const
{
	// wait for thread completion
	WaitForSingleObject(m_hThread.get(), INFINITE);
	// check for errors
	CheckErrors();
	// create content object
	boost::shared_ptr<Content> pContent(new ContentImp(m_pResponse));

	return pContent;
}
///////////////////////////////////////////////////////////////////////////////
boost::shared_ptr<Request> FutureResponse::ComposeReply
(
	const char* szHeaders, 
	size_t nHeadersLength, 
	const void* pData,
	size_t nDataSize
)
{
	// wait for thread completion
	WaitForSingleObject(m_hThread.get(), INFINITE);
	// check for errors
	CheckErrors();

	std::string sHeaders(szHeaders, nHeadersLength);
	// compose request data
	boost::shared_ptr<RequestData> requestData
		(new RequestData(sHeaders.c_str(), pData, nDataSize));
	// create reply
	return m_pRequest->ComposeReply(requestData);
}
///////////////////////////////////////////////////////////////////////////////
void FutureResponse::SendRequestDeferred()
{
	// request execution started
	m_pRequest->GetObserver()->OnRequestStarted();
	// try to send request and analyze result
	try
	{
		// try to send request
		m_pResponse = m_pRequest->Send();
	}
	// store error codes
	catch (const SystemError& e)
	{
		if (m_pRequest->IsAborted())
			m_pError = std::auto_ptr<ActivationError>(new LogicalError(LogicalError::OPERATION_ABORTED));
		else
			m_pError = std::auto_ptr<ActivationError>(new SystemError(e));
	}
	catch (const HttpError& e)
	{
		m_pError = std::auto_ptr<ActivationError>(new HttpError(e));
	}
	catch (const ServerError& e)
	{
		m_pError = std::auto_ptr<ActivationError>(new ServerError(e));
	}
	catch (const LogicalError& e)
	{
		m_pError = std::auto_ptr<ActivationError>(new LogicalError(e));
	}
	catch (const std::exception&)
	{
		m_pError = std::auto_ptr<ActivationError>(new LogicalError(LogicalError::UNEXPECTED_ERROR));
	}
	// notify about request completion
	if (m_pError.get())
	{
		m_pRequest->GetObserver()->OnRequestFailed(*m_pError);
	}
	else
	{
		m_pRequest->GetObserver()->OnRequestCompleted();
	}
}
///////////////////////////////////////////////////////////////////////////////
DWORD WINAPI FutureResponse::ThreadProc(LPVOID lpContext)
{
	FutureResponse* const pThis = static_cast<FutureResponse* const>(lpContext);
	// call deferred send
	pThis->SendRequestDeferred();

	return ERROR_SUCCESS;
}
///////////////////////////////////////////////////////////////////////////////
void FutureResponse::CheckErrors() const
{
	if (!m_pError.get())
		return;
	switch(m_pError->GetErrorType())
	{
	case ActivationError::SYSTEM_ERROR:
		throw SystemError(m_pError->GetErrorCode());
		break;
	case ActivationError::HTTP_ERROR:
		throw HttpError(m_pError->GetErrorCode());
		break;
	case ActivationError::SERVER_ERROR:
		throw ServerError(static_cast<ServerError::ErrorCode>(m_pError->GetErrorCode()));
		break;
	case ActivationError::LOGICAL_ERROR:
		throw LogicalError(static_cast<LogicalError::ErrorCode>(m_pError->GetErrorCode()));
		break;
	default:
		_ASSERTE("Unexpected error type");
		throw LogicalError(LogicalError::UNEXPECTED_ERROR);
		break;
	}
}
///////////////////////////////////////////////////////////////////////////////
} // namespace OnlineActivation
