/*

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

// jmeno, popis a cesta k bloku
const LPWSTR g_pName = L"Make Hist Ranges";
const LPWSTR g_pDesc = L"Produces multilayer signal with various intensity bands of source signal";
const LPWSTR g_pPath = L"centrum.cz/Image Processing";
// 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"1L/GBMP", L"HIST"};
const WCHAR *g_pOutputNameArray[] = {L"ML/BBMP"};

// 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_fTxtSfcMin = 0.01f;
	m_fTxtSfcMax = 0.15f;
}
// 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)
{

	std::deque<std::pair<void*, DWORD> > dqLayers;
	
	DWORD dwUsedLayers = 0;
	DWORD *pdwYvalues = NULL;
	float fTxtSfcMin, fTxtSfcMax;
	EnterCriticalSection(&m_cs);
	fTxtSfcMin = m_fTxtSfcMin;
	fTxtSfcMax = m_fTxtSfcMax;
	LeaveCriticalSection(&m_cs);

	// test, zda ma vstupni signal prave jednu vrstvu na obou portech

	if ( pInputData->dwLayersArray[0] != 1 ||
		pInputData->dwLayersArray[1] != 1 )
		return S_FALSE;

	BYTE *pSrcImg = (BYTE*)pInputData->pDataArrayArray[0][0];
	BYTE *pSrcHist = (BYTE*)pInputData->pDataArrayArray[1][0];

	BITMAPFILEHEADER* bfh = (BITMAPFILEHEADER*)pSrcImg;
	BITMAPINFO* bi = (BITMAPINFO*)&bfh[1];
	BYTE* pPixelsSrc = (BYTE*)(&((RGBQUAD*)(&bi[1]))[256]);

	if ( pInputData->dwDataLenArrayArray[0][0] < sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFO) || 
		bfh->bfType != 0x4D42 || 
		bi->bmiHeader.biBitCount != 8 ||
		bi->bmiHeader.biCompression != 0 || 
		bi->bmiHeader.biPlanes != 1 )
		return S_FALSE;

	DWORD dwSum = bi->bmiHeader.biWidth * bi->bmiHeader.biHeight;
	DWORD dwWidth = bi->bmiHeader.biWidth;
	DWORD dwHeight = bi->bmiHeader.biHeight;

	while ( memcmp(((PLOTFILEHEADER*)pSrcHist)->type, "plot", 4) == 0 )
	{	// nasel se plot
		pSrcHist += sizeof(PLOTFILEHEADER);
		PLOTHEADER *ph = (PLOTHEADER*)pSrcHist;
		while ( ph->dataType != DATATYPE_EOP )
		{
			pSrcHist += sizeof(PLOTHEADER);
			if ( ph->dataType == DATATYPE_YVALUES &&
				ph->dwDataLen == 256 && ph->itemType == ITEMTYPE_DWORD )
				pdwYvalues = (DWORD*)pSrcHist;
			pSrcHist += g_itemLen[ph->itemType] * ph->dwDataLen;
			ph = (PLOTHEADER*)pSrcHist;
		}
		if ( pdwYvalues )
			break;
	}

	for ( DWORD p = 0; p < 256; p++ )
	{
		if ( pdwYvalues[p] < (DWORD)(fTxtSfcMax * dwSum) &&
			pdwYvalues[p] > fTxtSfcMin )
		{
			BYTE* data;
			DWORD dwLen = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) +
				sizeof(RGBQUAD) * 256 + dwWidth * dwHeight;
			data = (BYTE*)malloc(dwLen);

			// naplneni dat

			BITMAPFILEHEADER* bfh = (BITMAPFILEHEADER*)data;
			ZeroMemory(bfh, sizeof(BITMAPFILEHEADER));
			bfh->bfType = 0x4D42;
			bfh->bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + 256*sizeof(RGBQUAD);
			
			BITMAPINFOHEADER* bih = (BITMAPINFOHEADER*)&bfh[1];
			ZeroMemory(bih, sizeof(BITMAPINFOHEADER));
			bih->biSize = sizeof(BITMAPINFOHEADER);
			bih->biWidth = dwWidth;
			bih->biHeight = dwHeight;
			bih->biBitCount = 8;
			bih->biPlanes = 1;
			
			RGBQUAD *pQuad = (RGBQUAD*)&bih[1];
			pQuad[0].rgbRed = 0x00;
			pQuad[0].rgbGreen = 0x00;
			pQuad[0].rgbBlue = 0x00;
			pQuad[0].rgbReserved = 0x00;

			for ( DWORD i = 1; i < 255; i++ )
			{
				pQuad[i].rgbRed = 0xff;
				pQuad[i].rgbGreen = 0x00;
				pQuad[i].rgbBlue = 0x00;
				pQuad[i].rgbReserved = 0x00;
			} // Tyto barvy by se nemeli v obrazku objevit. Proto je nabarvime cervene!

			pQuad[255].rgbRed = 0xff;
			pQuad[255].rgbGreen = 0xff;
			pQuad[255].rgbBlue = 0xff;
			pQuad[255].rgbReserved = 0x00;

			BYTE* pPixels = (BYTE*)&pQuad[256];

			for ( DWORD j = 0; j < dwSum; j++ )
			{
				if ( pPixelsSrc[j] == (BYTE)p )
					pPixels[j] = 0xff;
				else
					pPixels[j] = 0x00;
			}

			dqLayers.push_back(std::pair<LPVOID, DWORD>(data, dwLen));
			dwUsedLayers++;
		}
	}

	LockBlockData();
	DeleteBlockData();
	PrepareBlockData();
	NewBlockPortData(0, dwUsedLayers);
	dwUsedLayers = 0;
	while ( !dqLayers.empty() )
	{
		SetDataPtr(0, dwUsedLayers++, (*dqLayers.begin()).first, (*dqLayers.begin()).second);
		dqLayers.pop_front();
		
	}
	UnlockBlockData();

	return S_OK;
}

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