/*

Na zacatku tohoto souboru je nastaveni chovani bloku. Pote jsou zde funkce
nezvytne k vytvoreni instance COMu, a funkce, ktera vraci ukazatele na
rozhrani bloku. Prozatim se predpokladaji pouze 2 pridavna rozhrani k tomuto
objektu. 

-- IBlockConfig --
je urcene ke konfiguraci bloku. Je pouzito
v pripade, kdyz uzivatel klikne pravym tlacitkem mysi na blok a vybere
volbu "properties...". Melo by se otevrit okno s nastavenim, ktere musi
byt implementovano

-- IBlockAutoTrigger --
slouzi k automatickemu spousteni procesu. Vyuzije se napriklad pri realtime
zpracovani zvuku a videa, kdy potrebujeme pravidelne zpracovavat useky dat

*/

#include "stdafx.h"
#include "imp_block.h"
#include "imp_config.h"
#include <deque>

#define __MYREADBUFFERSIZE 65536

// jmeno, popis a cesta k bloku
const LPWSTR g_pName = L"HTTP Get Image With Parameters";
const LPWSTR g_pDesc = L"Gets image from another computer using HTTP protocol with parameters specifien in loaded page";
const LPWSTR g_pPath = L"Internet";
// jestli ma blok rohrani, odkomentujte prislusny radek
#define __BLOCKHAVECONFIGWINDOW
//#define __BLOCKHAVEAUTOTRIGGER
// pocty vstupnich a vystupnich portu
const DWORD g_dwInPortCount = 3;
const DWORD g_dwOutPortCount = 1;
// nazvy vstupnich a vystupnich portu
const WCHAR *g_pInputNameArray[] = {L"RAW/SRVR", L"RAW/PATH", L"RAW/IMGNM"};
const WCHAR *g_pOutputNameArray[] = {L"IMAGE"};

// Tyto funkce nemenit !!!
// funkce volana klientskym programem k vytvoreni instance tridy

HRESULT CreateBlockInstance(REFIID riid, void **ppv)
{
	return CImpBlock::CreateInstance(riid, ppv);
}
// staticka funkce, ktera vytvari instanci tridy
HRESULT CImpBlock::CreateInstance(const IID &riid, void **ppv)
{
	CImpBlock* pObj; 
	pObj = new CImpBlock();
	if (pObj == NULL) 
		return E_OUTOFMEMORY; 
	HRESULT hr; 
	if ( FAILED(hr = pObj->QueryInterface(riid, (void**)ppv))) 
		delete pObj ; 
	return hr; 
}
// funkce, ktera vraci adresu rozhrani
void* CImpBlock::GetInterface(REFIID riid)
{
#ifdef __BLOCKHAVECONFIGWINDOW
	if ( IID_IBlockConfig == riid )
		return m_pxConfig;
#endif
#ifdef __BLOCKHAVEAUTOTRIGGER
	if ( IID_IBlockAutoTrigger == riid )
		return m_pxAutoTrigger;
#endif
	return NULL;
}
// konstruktor
CImpBlock::CImpBlock() : m_pxConfig(NULL), m_pxAutoTrigger(NULL)
{
#ifdef __BLOCKHAVECONFIGWINDOW
	m_pxConfig = new CImpBlockConfig(this);
#endif
#ifdef __BLOCKHAVEAUTOTRIGGER
	m_pxAutoTrigger = new CImpBlockAutoTrigger(this);
#endif
	m_dwInPortCount = g_dwInPortCount;
	m_dwOutPortCount = g_dwOutPortCount;
	m_pName = g_pName;
	m_pDesc = g_pDesc;
	m_pPath = g_pPath;
	m_pInputNameArray = g_pInputNameArray;
	m_pOutputNameArray = g_pOutputNameArray;

	// inicializace promennych

}
// destruktor
CImpBlock::~CImpBlock()
{
#ifdef __BLOCKHAVEAUTOTRIGGER
	if ( m_pxAutoTrigger )
		delete ( m_pxAutoTrigger );
#endif
#ifdef __BLOCKHAVECONFIGWINDOW
	if ( m_pxConfig )
		delete ( m_pxConfig );
#endif
}

///////////////////////////////
// TYTO FUNKCE IMPLEMENTOVAT //
///////////////////////////////

STDMETHODIMP CImpBlock::InitInstance()
{
	CBlock::InitInstance();
	AFX_MANAGE_STATE(AfxGetStaticModuleState( ))
	m_pISess = new CInternetSession(NULL, 1, PRE_CONFIG_INTERNET_ACCESS, NULL, NULL, 0);
	return S_OK;
}

STDMETHODIMP CImpBlock::ExitInstance()
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState( ))
	delete(m_pISess);
	CBlock::ExitInstance();
	return S_OK;
}

STDMETHODIMP CImpBlock::ProcessRev(const DWORD dwProcessId, REVDATAMAP &rRevDataMap)
{
	return S_OK;
}

STDMETHODIMP CImpBlock::ClearRevMap(REVDATAMAP &rRevDataMapClean)
{
	return S_OK;
}

STDMETHODIMP CImpBlock::ProcessData(const LPBLOCKDATA pInputData)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState( ))
	CString strServer;
	CString strPath;
	CString strImageName;
	INTERNET_PORT nPort = 80;

	if ( 1 == pInputData->dwLayersArray[0] )
	{
		try
		{
			strServer = (WCHAR*)pInputData->pDataArrayArray[0][0];
		}
		catch(...)
		{
			strServer = L"";
		}
	}

	if ( 1 == pInputData->dwLayersArray[1] )
	{
		try
		{
			strPath = (WCHAR*)pInputData->pDataArrayArray[1][0];
		}
		catch(...)
		{
			strPath = L"";
		}
	}

	if ( 1 == pInputData->dwLayersArray[2] )
	{
		try
		{
			strImageName = (WCHAR*)pInputData->pDataArrayArray[2][0];
		}
		catch(...)
		{
			strImageName = L"";
		}
	}

	EnterCriticalSection(&m_cs);
	if ( strServer.IsEmpty() )
		strServer = m_strServer;
	if ( strPath.IsEmpty() )
		strPath = m_strPath;
	if ( strImageName.IsEmpty() )
		strImageName = m_strImageName;
	LeaveCriticalSection(&m_cs);

	std::deque<std::pair<LPVOID, DWORD> > dqData;

	DWORD dwUsedLayers = 0;

	if ( pInputData->dwLayersArray[0] && 
		pInputData->dwLayersArray[1] &&
		pInputData->dwLayersArray[2] )
	{
		VOID* data = NULL;
		DWORD dwLen = 0;
		for ( DWORD dwLayer = 0; dwLayer < pInputData->dwLayersArray[0]; dwLayer++ )
		{	// pro kazdou vrstvu
			dwUsedLayers ++;									// TOTO IMPLEMENTOVAT POTOM
			dqData.push_back(std::pair<LPVOID, DWORD>(data, /*dwLen*/0));
		}
	}
	else
	{
		VOID* data = NULL;
		DWORD dwLen = 0;
		if ( AllocFileFromServer(strServer, nPort, strPath, strImageName, &data, &dwLen) )
		{
			dqData.push_back(std::pair<VOID*, DWORD>(data, dwLen));
			dwUsedLayers++;
		}
	}

	LockBlockData();
	DeleteBlockData();
	PrepareBlockData();
	NewBlockPortData(0, dwUsedLayers);
	for ( DWORD i = 0; i < dwUsedLayers; i++ )
	{
		SetDataPtr(0, i, dqData.front().first, dqData.front().second);
		dqData.pop_front();
	}
	UnlockBlockData();

	return S_OK;
}

BOOL CImpBlock::AllocFileFromServer(CString strServer, INTERNET_PORT nPort, CString strPath, CString strImageName, VOID **pTgtData, DWORD *pdwLen)
{
	ASSERT(pTgtData);
	ASSERT(pdwLen);
	BOOL ret = FALSE;
	LPVOID data = NULL;
	CHttpConnection *pHttpConn;
	TRY
	{
		pHttpConn = m_pISess->GetHttpConnection(strServer, nPort);
	}
	CATCH ( CInternetException, e )
	{
		WCHAR err[128], fn[128];
		
		e->GetErrorMessage(err, 128);
		GetModuleFileName(GetModuleHandle(NULL), fn, 128);
		MessageBox(NULL, err, fn, MB_ICONERROR | MB_OK | MB_TASKMODAL);
	}
	END_CATCH

	if ( pHttpConn != NULL )
	{
		CHttpFile* pHttpFile = NULL;
		pHttpFile = pHttpConn->OpenRequest( CHttpConnection::HTTP_VERB_GET, strPath, NULL, 1, NULL, L"HTTP/1.1"/*NULL*/);

		if ( pHttpFile )
		{
			pHttpFile->SetReadBufferSize(__MYREADBUFFERSIZE - 1);
			BOOL bOk;
			TRY
			{
				bOk = pHttpFile->SendRequest();
			}
			CATCH ( CInternetException, e )
			{
				WCHAR err[128], fn[128];

				e->GetErrorMessage(err, 128);
				GetModuleFileName(GetModuleHandle(NULL), fn, 128);
				MessageBox(NULL, err, fn, MB_ICONERROR | MB_OK | MB_TASKMODAL);
				if ( pHttpFile )
					delete(pHttpFile);
				pHttpFile = NULL;
			}
			END_CATCH


			if ( bOk )
			{
				DWORD dwCode;
				pHttpFile->QueryInfoStatusCode(dwCode);
				if ( 200 == dwCode )
				{
					USES_CONVERSION;
					char *imgname = W2A(strImageName);
					ATL::CStringT<CHAR, StrTraitMFC<CHAR> > strPage, strName_;

					char buf[__MYREADBUFFERSIZE];

					DWORD dwLen, dwReaded;
					do
					{
						dwLen = (DWORD)pHttpFile->GetLength();
						dwReaded = pHttpFile->Read(buf, __MYREADBUFFERSIZE - 1);
						buf[dwReaded] = '\0';
						strPage += buf;
					} while ( dwReaded );
					
					int offset = strPage.Find(imgname);
					LPSTR namebuf = strPage.GetBuffer();
					LPSTR namebeg = &namebuf[offset];
					
					while ( namebeg )
					{
						if ( '"' == *namebeg )
						{
							namebeg++;
							offset++;
							break;
						}
						else if ( namebeg > namebuf )
						{
							namebeg--;
							offset--;
						}
						else
							namebeg = NULL;
					}

					if ( namebeg )
					{
						LPSTR nameend = strchr(namebeg, '"');
						if ( nameend )
						{
							int nLen = nameend - namebeg;
							strName_ = strPage.Mid(offset, nLen);
							CString strName = A2W(strName_.GetString());
							if ( strName.Left(2) == L"./" )
							{
								int nEnd = strPath.ReverseFind(L'/');
								strName = strPath.Left(nEnd) + strName.Mid(1);
							}

							strName.Replace(L"&amp;", L"&");	// clean := @ReplaceSubstring(submission; " & "; " &amp; ");
							strName.Replace(L"&#039;",L"'");	// clean := @ReplaceSubstring(clean;"'";"&#039;");
							strName.Replace(L"&quot;",L"\"");	// clean := @ReplaceSubstring(clean;"\""; "&quot;");
							strName.Replace(L"&lt;",L"<");		// clean := @ReplaceSubstring(clean; "<"; "&lt;");
							strName.Replace(L"&gt;",L">");		// clean := @ReplaceSubstring(clean; ">"; "&gt;");

							// mam jmeno souboru s obrazkem s parametry. Vytvorim tedy vrstvu

							pHttpFile->Close();
							CHttpFile *pImgFile;
							pImgFile = pHttpConn->OpenRequest(CHttpConnection::HTTP_VERB_GET, strName, NULL, 2);
							DWORD dwErr = GetLastError();
							
							if ( pImgFile && pImgFile->SendRequest() )
							{
								DWORD dwImgCode;
								pImgFile->QueryInfoStatusCode(dwImgCode);
								if ( 200 == dwImgCode )
								{
									CMemFile mf;
									CArchive ar(&mf, CArchive::store);
									do
									{
										dwLen = (DWORD)pImgFile->GetLength();
										dwReaded = pImgFile->Read(buf, __MYREADBUFFERSIZE - 1);
										buf[dwReaded] = '\0';
										ar.Write(buf, dwReaded);
									} while ( dwReaded );
									ar.Close();
									DWORD dwLen = (LONG)mf.GetLength();
									data = (BYTE*)malloc(dwLen);
									mf.SeekToBegin();
									mf.Read(data, dwLen);
									//dqData.push_back(std::pair<LPVOID, DWORD>(data, dwLen));
									*pTgtData = data;
									*pdwLen = dwLen;
									ret = TRUE;


								}
								pImgFile->Close();
								delete(pImgFile);
							}
						}
					}
				}
			}
			pHttpFile->Close();
			delete(pHttpFile);
		}
		pHttpConn->Close();
		delete(pHttpConn);
	}
	return ret;
}

STDMETHODIMP_(void) CImpBlock::Serialize(CArchive &ar)
{
	if ( ar.IsStoring() )
	{
		ar << m_strServer << m_strPath << m_strImageName;
	}
	else
	{
		ar >> m_strServer >> m_strPath >> m_strImageName;
	}
}