/*

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

// jmeno, popis a cesta k bloku
const LPWSTR g_pName = L"Treshold";
const LPWSTR g_pDesc = L"Tresholds image by tresh value on second input";
const LPWSTR g_pPath = L"Filter";
// jestli ma blok rohrani, odkomentujte prislusny radek
//#define __BLOCKHAVECONFIGWINDOW
//#define __BLOCKHAVEAUTOTRIGGER
// pocty vstupnich a vystupnich portu
const DWORD g_dwInPortCount = 2;
const DWORD g_dwOutPortCount = 1;
// nazvy vstupnich a vystupnich portu
const WCHAR *g_pInputNameArray[] = {L"GRAY IMG", L"PLT/TRESH"};
const WCHAR *g_pOutputNameArray[] = {L"BIN IMG"};

// 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();
	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;
}

UCHAR GetTreshValue(BYTE* pData, DWORD dwLen)
{
	UCHAR cRet = 0;
	if ( dwLen > sizeof(PLOTFILEHEADER) )
	{
		PLOTFILEHEADER* pfh = (PLOTFILEHEADER*)pData;
		pData += sizeof(PLOTFILEHEADER);
		BOOL bContinue = TRUE;
		while ( bContinue && memcmp(pfh->type, "plot", 4) == 0 )
		{
			PLOTHEADER *ph = (PLOTHEADER*)pData;
			while ( ph->dataType != DATATYPE_EOP )
			{
				if ( ph->dataType == DATATYPE_XVALUES && ph->dwDataLen == 1 && ph->itemType == ITEMTYPE_BYTE )
				{
					cRet = ((BYTE*)(&ph[1]))[0];
					bContinue = FALSE;
					break;
				}
				else
				{
					pData += sizeof(PLOTHEADER);
					pData += g_itemLen[ph->itemType] * ph->dwDataLen;
					ph = (PLOTHEADER*)pData;
				}
			}
			pfh = (PLOTFILEHEADER*)pData;
			pData += sizeof(PLOTFILEHEADER);
		}
	}
	return cRet;
}

STDMETHODIMP CImpBlock::ProcessData(const LPBLOCKDATA pInputData)
{
	BYTE* data = NULL;
//	float fr, fg, fb;

	std::deque<std::pair<LPVOID, DWORD> > dq;
	DWORD dwUsedLayers = 0;
	DWORD dwMinLayers = (pInputData->dwLayersArray[0] < pInputData->dwLayersArray[1]) ?
		pInputData->dwLayersArray[0] : pInputData->dwLayersArray[1];

	for ( DWORD dwLayer = 0; dwLayer < dwMinLayers; dwLayer++ )
	{	// pro kazdou vrstvu
		if ( pInputData->dwDataLenArrayArray[0][dwLayer] >= sizeof(BITMAPFILEHEADER) )
		{	// jestli je velikost vetsi nebo rovna velikosti headeru
			BITMAPFILEHEADER* bfh = (BITMAPFILEHEADER*)pInputData->pDataArrayArray[0][dwLayer];
			if ( bfh->bfType == 0x4d42 )
			{	// jedna-li se o BMP soubor
				BITMAPINFO* bi = (BITMAPINFO*)&((BYTE*)bfh)[sizeof(BITMAPFILEHEADER)];

				if ( bi->bmiHeader.biBitCount == 8 )
				{	// pouze na 8bit obrazku

					// nalezneme hodnotu prahu na druhem vstupu
					DWORD cTreshold;
					cTreshold = GetTreshValue((BYTE*)pInputData->pDataArrayArray[1][dwLayer], pInputData->dwDataLenArrayArray[1][dwLayer]);

					DWORD dwLen = bi->bmiHeader.biHeight * bi->bmiHeader.biWidth +
						sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFO) + 256*sizeof(RGBQUAD);
					data = (BYTE*)malloc(dwLen);
					UCHAR* ptrsrc = (&((BYTE*)bfh)[bfh->bfOffBits]);
					UCHAR* ptrtgt = &data[sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)];
					DWORD steps = bi->bmiHeader.biWidth * bi->bmiHeader.biHeight;

					// vytvorime hlavicku souboru bitmapy
					((BITMAPFILEHEADER*)data)->bfType = 0x4d42;
					((BITMAPFILEHEADER*)data)->bfReserved1 = 0x0000;
					((BITMAPFILEHEADER*)data)->bfReserved2 = 0x0000;
					((BITMAPFILEHEADER*)data)->bfSize = dwLen;
					((BITMAPFILEHEADER*)data)->bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + 256*sizeof(RGBQUAD);

					// vytvorime hlavicku bitmapy
					((BITMAPINFO*)&data[sizeof(BITMAPFILEHEADER)])->bmiHeader.biBitCount = 8;
					((BITMAPINFO*)&data[sizeof(BITMAPFILEHEADER)])->bmiHeader.biClrImportant = 0;
					((BITMAPINFO*)&data[sizeof(BITMAPFILEHEADER)])->bmiHeader.biClrUsed = 0;
					((BITMAPINFO*)&data[sizeof(BITMAPFILEHEADER)])->bmiHeader.biCompression = 0;
					((BITMAPINFO*)&data[sizeof(BITMAPFILEHEADER)])->bmiHeader.biHeight = bi->bmiHeader.biHeight;
					((BITMAPINFO*)&data[sizeof(BITMAPFILEHEADER)])->bmiHeader.biPlanes = 1;
					((BITMAPINFO*)&data[sizeof(BITMAPFILEHEADER)])->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
					((BITMAPINFO*)&data[sizeof(BITMAPFILEHEADER)])->bmiHeader.biSizeImage = 0;
					((BITMAPINFO*)&data[sizeof(BITMAPFILEHEADER)])->bmiHeader.biWidth = bi->bmiHeader.biWidth;
					((BITMAPINFO*)&data[sizeof(BITMAPFILEHEADER)])->bmiHeader.biXPelsPerMeter = 0;
					((BITMAPINFO*)&data[sizeof(BITMAPFILEHEADER)])->bmiHeader.biYPelsPerMeter = 0;

					// vytvorime paletu bitmapy
					for ( DWORD i = 0; i < 256; i++ )
					{
						*ptrtgt = (UCHAR)i; ptrtgt++;	// BLUE
						*ptrtgt = (UCHAR)i; ptrtgt++;	// GREEN
						*ptrtgt = (UCHAR)i; ptrtgt++;	// RED
						*ptrtgt = 0; ptrtgt++;			// RESERVED
					}

					for ( DWORD i = 0; i < steps; i++ )
					{
						//*ptrtgt = (UCHAR)(ptrsrc[2] * fr + ptrsrc[1] * fg + ptrsrc[0] * fb);
						//*ptrtgt = i / bi->bmiHeader.biWidth;
						*ptrtgt = (*ptrsrc >= cTreshold ) ? (0xff) : (0x00);
						ptrsrc++;
						ptrtgt++;
					}
					dwUsedLayers ++;
					dq.push_back(std::pair<LPVOID, DWORD>(data, dwLen));
				}
			}
		}
	}

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

	return S_OK;
}

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