/*

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 "Question.h"
#//include "imp_autotrigger.h"
#include <deque>

// jmeno, popis a cesta k bloku
const LPWSTR g_pName = L"Kohonen Network";
const LPWSTR g_pDesc = L"Kohonen Network";
const LPWSTR g_pPath = L"AI";
// 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"VECTOR", L".BMP+", L".RAW/WCHref"};
const WCHAR *g_pOutputNameArray[] = {L"RESULT"};

// 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

	m_bLearnMode = TRUE;
}
// 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();
	return S_OK;
}

STDMETHODIMP CImpBlock::ExitInstance()
{
	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())

	std::deque<std::pair<void*, DWORD> > dqLayers;

	BOOL bLearnMode;
	BOOL bOk = TRUE;

	DWORD dwLayers = pInputData->dwLayersArray[0];

	// vytvoreni kopie parametru

	EnterCriticalSection(&m_cs);
	bLearnMode = m_bLearnMode;
	LeaveCriticalSection(&m_cs);

	WCHAR* pRef = NULL;

	CString strOutput;

	// nacteni referencniho vstupu

	if ( pInputData->dwLayersArray[2] == 1 &&
		pInputData->dwDataLenArrayArray[2][0] == sizeof(WCHAR) * dwLayers)
	{	// na referencnim vstupu je stejny pocet znaku, jako na vstupu vektoru priznaku
		pRef = (WCHAR*)pInputData->pDataArrayArray[2][0];
	}

	// zpracovani dat

	for ( DWORD dwLayer = 0; dwLayer < dwLayers; dwLayer++ )
	{
		if ( memcmp(pInputData->pDataArrayArray[0][dwLayer], "VCTd", 4) == 0 )
		{	// data jsou typu VECTOR double
			double *pData = (double*)&((BYTE*)pInputData->pDataArrayArray[0][dwLayer])[4];
			DWORD dwVectorLength = (pInputData->dwDataLenArrayArray[0][dwLayer] - 4) / sizeof(double);
			if ( m_netKohonen.GetVectorLength() != dwVectorLength )
			{	// vector neni stejne dlouhy, jako vector v siti
				switch ( MessageBox(NULL, L"Vector size is different, reset NN?", L"Kohenen - Question", MB_YESNO | MB_TASKMODAL | MB_ICONQUESTION) )
				{
				case IDYES:
					m_netKohonen.SetVectorLength(dwVectorLength);
					break;
				case IDNO:
					bOk = FALSE;
					break;
				}
				if ( !bOk )
					break;
			}
			WCHAR wcResult = -1;

			if ( bLearnMode )
			{
				if ( !pRef )
				{
					m_netKohonen.GetOutput(pData, &wcResult);
					CQuestion qd;
					BITMAPFILEHEADER *bfh = (BITMAPFILEHEADER*)pInputData->pDataArrayArray[1][dwLayer];
					if ( dwLayer < pInputData->dwLayersArray[1] &&
						pInputData->dwDataLenArrayArray[1][dwLayer] > sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) &&
						bfh->bfType == 0x4D42 )
					{
						BITMAPINFOHEADER* bih = (BITMAPINFOHEADER*)&bfh[1];
						RGBQUAD *pColors = (RGBQUAD*)&bih[1];
						BYTE *pBits = (BYTE*)&pColors[256];
						qd.m_pBitmap = (BYTE*)bfh;
						qd.m_dwBitmapLen = pInputData->dwDataLenArrayArray[1][dwLayer];
					}
					qd.m_wcRes = wcResult;
					switch ( qd.DoModal() )
					{
					case IDOK:
						if ( qd.m_wcRes != wcResult )
						{
							m_netKohonen.Add(qd.m_wcRes, pData);
							SetModifiedFlag(TRUE);
							wcResult = qd.m_wcRes;
						}
						break;
					case IDCANCEL:
						bLearnMode = FALSE;
						break;
					}
				}
				else
				{	// mame k dispozici referencni vstup
					m_netKohonen.GetOutput(pData, &wcResult);
					if ( pRef[dwLayer] != wcResult )
					{
						m_netKohonen.Add(pRef[dwLayer], pData);
						SetModifiedFlag(TRUE);
						wcResult = pRef[dwLayer];
					}
				}
			}
			m_netKohonen.GetOutput(pData, &wcResult);
			if ( wcResult != -1 )
				strOutput = strOutput + wcResult;
			if ( !bOk )
				break;
		}
	}

	DWORD dwLen = strOutput.GetLength() * sizeof(WCHAR);
	BYTE *pData = (BYTE*)malloc(dwLen);
	memcpy(pData, strOutput.GetString(), dwLen);

	LockBlockData();
	DeleteBlockData();
	PrepareBlockData();
	NewBlockPortData(0, 1);
	SetDataPtr(0, 0, pData, dwLen);
	UnlockBlockData();

	if ( m_pxConfig )
		((CImpBlockConfig*)m_pxConfig)->UpdateStatus();

	return S_OK;
}

STDMETHODIMP_(void) CImpBlock::Serialize(CArchive &ar)
{
	if ( ar.IsStoring() )
	{
		ar << m_bLearnMode;
	}
	else
	{
		ar >> m_bLearnMode;
	}
	m_netKohonen.Serialize(ar);
}