/*

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 "..\\global\\headers.h"
#include <deque>

// jmeno, popis a cesta k bloku
const LPWSTR g_pName = L"Find Treshold";
const LPWSTR g_pDesc = L"Finds treshold by histogram";
const LPWSTR g_pPath = L"Analysis";
// jestli ma blok rohrani, odkomentujte prislusny radek
#define __BLOCKHAVECONFIGWINDOW
//#define __BLOCKHAVEAUTOTRIGGER
// pocty vstupnich a vystupnich portu
const DWORD g_dwInPortCount = 1;
const DWORD g_dwOutPortCount = 1;
// nazvy vstupnich a vystupnich portu
const WCHAR *g_pInputNameArray[] = {L"HIST"};
const WCHAR *g_pOutputNameArray[] = {L"TRESH"};

// 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_fTextSurface = 0.2f;
	m_bBlackTxtOnWhite = TRUE;
	m_bFindMin = FALSE;
}
// 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)
{
	BOOL bBlackTxtOnWhite;
	BOOL bFindMin;
	float fTextFurface;

	EnterCriticalSection(&m_cs);
	bBlackTxtOnWhite = m_bBlackTxtOnWhite;
	fTextFurface = m_fTextSurface;
	bFindMin = m_bFindMin;
	LeaveCriticalSection(&m_cs);

	DWORD dwLayers = 0;
	std::deque<std::pair<LPVOID, DWORD> > dqData;

	for ( DWORD dwLayer = 0; dwLayer < pInputData->dwLayersArray[0]; dwLayer++ )
	{
		BYTE *ptrsrc = (BYTE*)pInputData->pDataArrayArray[0][dwLayer];
		PLOTFILEHEADER *pfh = (PLOTFILEHEADER*)ptrsrc;
		CMemFile mf;
		CArchive ar(&mf, CArchive::store);
		while ( memcmp(pfh->type, "plot", 4) == 0 )
		{	// jedna se o plot file
			ar.Write("plot", 4);
			ptrsrc = (BYTE*)&pfh[1];
			PLOTHEADER *ph = (PLOTHEADER*)ptrsrc;

			PLOTHEADER *phx = NULL;
			PLOTHEADER *phy = NULL;
			DWORD dwLenX = 0;
			DWORD dwLenY = 0;

			while ( ph->dataType != DATATYPE_EOP )
			{
				ptrsrc = (BYTE*)&ph[1];
				DWORD dwLen = ph->dwDataLen * g_itemLen[ph->itemType];
				switch ( ph->dataType )
				{
				case DATATYPE_XVALUES:
					phx = ph;
					ptrsrc += dwLen;
					dwLenX = dwLen;
					break;
				case DATATYPE_YVALUES:
					phy = ph;
					ptrsrc += dwLen;
					dwLenY = dwLen;
					break;
				default:
					ar.Write(ph, sizeof(PLOTHEADER));
					ar.Write(ptrsrc, dwLen);
					ptrsrc += dwLen;
					break;
				}
				ph = (PLOTHEADER*)ptrsrc;
			}

			// zde se analyzuje histogram a zapise se pouze X hodnota
			// v miste, ktera odpovida prahu

			// vytvorime pamet na ulozeni hodnot
			// aby byla pro vsechny datove typy, vybereme typ double

			double *tmp = new double[phx->dwDataLen];
			LPVOID ptrsrcy = &phy[1];

			// nakopirujeme do pameti vsechny hodnoty a postupne
			// vytvorime soucet vsech hodhot

			double sum = 0;

			for ( DWORD i = 0; i < phy->dwDataLen; i++ )
			{
				switch ( phy->itemType )
				{
				case ITEMTYPE_BYTE:
					tmp[i] = ((BYTE*)ptrsrcy)[i];
					break;
				case ITEMTYPE_WORD:
					tmp[i] = ((WORD*)ptrsrcy)[i];
					break;
				case ITEMTYPE_DWORD:
					tmp[i] = ((DWORD*)ptrsrcy)[i];
					break;
				case ITEMTYPE_FLOAT:
					tmp[i] = ((FLOAT*)ptrsrcy)[i];
					break;
				case ITEMTYPE_DOUBLE:
					tmp[i] = ((DOUBLE*)ptrsrcy)[i];
					break;
				default:
					tmp[i] = 0;
					break;
				}
				sum += tmp[i];
			}

			double dtresh = sum * (bBlackTxtOnWhite ? fTextFurface : (1-fTextFurface));

			sum = 0;
			DWORD i;
			for ( i = 0; i < phy->dwDataLen; i++ )
			{
				sum += tmp[i];
				if ( sum > dtresh )
					break;
			}

			if ( bFindMin )
			{
				BOOL bQuit;

				do
				{
					bQuit = TRUE;
					for ( int j = 1; j < 10; j++ )
					{
						if ( i+j < phy->dwDataLen && tmp[i+j] < tmp[i] )
						{
							i = i+j;
							bQuit = FALSE;
							break;
						}
						if ( i-j >= 0 && tmp[i-j] < tmp[i] )
						{
							i = i-j;
							bQuit = FALSE;
							break;
						}
					}
				}while (!bQuit);
			}

			delete[](tmp);
//			ptrsrc += dwLen;

			// v "i" je nyni ulozen index prahu

			PLOTHEADER pheader;
			pheader.dataType = DATATYPE_XVALUES;
			pheader.dwDataLen = 1;
			pheader.itemType = phx->itemType;
			ar.Write(&pheader, sizeof(PLOTHEADER));

			switch ( phx->itemType )
			{
			case ITEMTYPE_BYTE:
				{
					BYTE x = (BYTE)i;
					ar.Write(&x, sizeof(BYTE));
				}
				break;
			case ITEMTYPE_WORD:
				{
					WORD x = (WORD)i;
					ar.Write(&x, sizeof(WORD));
				}
				break;
			case ITEMTYPE_DWORD:
				{
					DWORD x = (DWORD)i;
					ar.Write(&x, sizeof(DWORD));
				}
				break;
			case ITEMTYPE_FLOAT:
				{
					FLOAT x = (FLOAT)i;
					ar.Write(&x, sizeof(FLOAT));
				}
				break;
			case ITEMTYPE_DOUBLE:
				{
					DOUBLE x = (DOUBLE)i;
					ar.Write(&x, sizeof(DOUBLE));
				}
				break;
			}

			// zkopirujeme a ulozime data do fronty

			PLOTHEADER phEnd;
			phEnd.dataType = DATATYPE_EOP;
			phEnd.dwDataLen = 0;
			phEnd.itemType = ITEMTYPE_BYTE;

			PLOTFILEHEADER pfhEnd;
			ZeroMemory(&pfhEnd, sizeof(pfhEnd));

			ar.Write(&phEnd, sizeof(phEnd));
			ar.Write(&pfhEnd, sizeof(&pfhEnd));
			ar.Flush();
			mf.Flush();
			DWORD dwLen = (LONG)mf.GetLength();
			LPVOID data = malloc(dwLen);
			ar.Close();
			mf.SeekToBegin();
			mf.Read(data, dwLen);
			dqData.push_back(std::pair<LPVOID, DWORD>(data, dwLen));
			dwLayers++;
			pfh = (PLOTFILEHEADER*)(ptrsrc + sizeof(PLOTFILEHEADER));
		}
	}

	LockBlockData();
	DeleteBlockData();
	PrepareBlockData();
	NewBlockPortData(0, dwLayers);
	DWORD dwLayer = 0;
	while ( !dqData.empty() )
	{
		SetDataPtr(0, dwLayer, dqData.front().first, dqData.front().second);
		dqData.pop_front();
		dwLayer++;
	}
	UnlockBlockData();
	return S_OK;
}

STDMETHODIMP_(void) CImpBlock::Serialize(CArchive &ar)
{
	if ( ar.IsStoring() )
	{
		ar << m_bBlackTxtOnWhite << m_bFindMin << m_fTextSurface;
	}
	else
	{
		ar >> m_bBlackTxtOnWhite >> m_bFindMin >> m_fTextSurface;
	}
}