/*

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

typedef struct
{
	LPVOID pData;
	DWORD dwLen;
	DWORD dwXPos;
	DWORD dwYPos;
} OBJECTDATA, *LPOBJECTDATA;

typedef std::deque<OBJECTDATA> OBJECTDQ, *LPOBJECTDQ;

BOOL GetObjects(BYTE* pData, OBJECTDQ &os, DWORD dwMinPPL, DWORD dwMaxPPL);

// jmeno, popis a cesta k bloku
const LPWSTR g_pName = L"Get Letters";
const LPWSTR g_pDesc = L"Alalyses layers and best-fit layer segments";
const LPWSTR g_pPath = L"centrum.cz/Segmentation";
// 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"DATA"};
const WCHAR *g_pOutputNameArray[] = {L"DATA"};

// 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_dwMinLetterCount = 4;
	m_dwMaxLetterCount = 5;
	m_dwMinPPL = 100;
	m_dwMaxPPL = 3000;
}
// 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)
{
	DWORD dwUsedLayers = 0;
	std::deque<std::pair<void*, DWORD> > dqLayers;

	DWORD dwLayers = pInputData->dwLayersArray[0];

	DWORD dwMinPPL, dwMaxPPL;
	DWORD dwMinLetterCount, dwMaxLetterCount;

	EnterCriticalSection(&m_cs);
	dwMinPPL = m_dwMinPPL;
	dwMaxPPL = m_dwMaxPPL;
	dwMinLetterCount = m_dwMinLetterCount;
	dwMaxLetterCount = m_dwMaxLetterCount;
	LeaveCriticalSection(&m_cs);

	std::map<DWORD, OBJECTDQ> mapObjects;

	for ( DWORD dwLayer = 0; dwLayer < dwLayers; dwLayer++ )
	{
		
		OBJECTDQ os;
		GetObjects((BYTE*)pInputData->pDataArrayArray[0][dwLayer], os, dwMinPPL, dwMaxPPL);
		mapObjects.insert(std::pair<DWORD, OBJECTDQ>(dwLayer, os));
		if ( os.size() >= dwMinLetterCount && os.size() <= dwMaxLetterCount )
		{
			std::map<DWORD, std::pair<LPVOID, DWORD> > mp;
			for ( std::deque<OBJECTDATA>::iterator it = os.begin(); it != os.end(); it++ )
			{
				mp.insert(std::pair<DWORD, std::pair<LPVOID, DWORD> >(it->dwXPos, std::pair<LPVOID, DWORD>(it->pData, it->dwLen)));
			}
			for ( std::map<DWORD, std::pair<LPVOID, DWORD> >::iterator it = mp.begin(); it != mp.end(); it++ )
			{
				dwUsedLayers++;
				dqLayers.push_back(std::pair<LPVOID, DWORD>(it->second.first, it->second.second));
			}
		}
		else
		{
			while (!os.empty())
			{
				free(os.begin()->pData);
				os.pop_front();
			}
		}
	}
	

	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_dwMinLetterCount << m_dwMaxLetterCount << m_dwMinPPL << m_dwMaxPPL;
	}
	else
	{
		ar >> m_dwMinLetterCount >> m_dwMaxLetterCount >> m_dwMinPPL >> m_dwMaxPPL;
	}
}

BOOL GetObjects(BYTE* pData, OBJECTDQ &os, DWORD dwMinPPL, DWORD dwMaxPPL)
{
	BITMAPFILEHEADER* bfh = (BITMAPFILEHEADER*)pData;
	BITMAPINFOHEADER* bih = (BITMAPINFOHEADER*)&bfh[1];
	RGBQUAD* pColors = (RGBQUAD*)&bih[1];
	if ( bfh->bfType != 0x4D42 ||
		bih->biBitCount != 8 ||
		bih->biCompression != 0 ||
		bih->biPlanes != 1 )
		return FALSE;

	int nWidth = bih->biWidth;
	int nHeight = bih->biHeight;
	DWORD dwHeaderLen = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) +
		sizeof(RGBQUAD) * 256;

	int nMinX, nMaxX, nMinY, nMaxY;

	BYTE* pSrcData = (BYTE*)&((RGBQUAD*)(&bih[1]))[256];

	BYTE* pPxCopy = (BYTE*)malloc(nHeight * nWidth);
	memcpy(pPxCopy, pSrcData, nWidth * nHeight);

	DWORD dwOffset = 0;
	for ( WORD y = 0; y < (WORD)nHeight; y++ )
	{
		for ( WORD x = 0; x < (WORD)nWidth; x++ )
		{
			if ( pPxCopy[dwOffset] )
			{	// nalezen pixel
				DWORD dwPPL = 0;
				nMinX = x;
				nMaxX = x;
				nMinY = y;
				nMaxY = y;
				BYTE* pPxObject = (BYTE*)malloc(nHeight * nWidth);
				ZeroMemory(pPxObject, nHeight * nWidth);
				std::deque<std::pair<WORD, WORD> > dqPos;

				pPxObject[y * nWidth + x] = 0xff;				// oznacim pixel objektu v novem obrazku
				pPxCopy[y * nWidth + x] = 0x00;				// smazu pixel objektu v puvodnim obrazku

				dqPos.push_back(std::pair<WORD, WORD>(x, y));	// pridam souradnice pixelu do fronty
				while ( !dqPos.empty() )						// dokud je neco ve fronte
				{
					std::pair<int, int> pos;
					pos = *dqPos.begin();
					int x_ = pos.first;
					int y_ = pos.second;
					dqPos.pop_front();
					
					if ( x_ < nMinX )
						nMinX = x_;
					if ( x_ > nMaxX )
						nMaxX = x_;
					if ( y_ < nMinY )
						nMinY = y_;
					if ( y_ > nMaxY )
						nMaxY = y_;

					// zkontroluj okoli bodu [8mi okoli]
					// vpravo
					if ( x_+1 < nWidth						 &&	pPxCopy[y_*nWidth + x_ + 1] )			
					{
						pPxObject[y_ * nWidth + x_+1] = 0xff;
						pPxCopy[y_ * nWidth + x_+1] = 0x00;
						dqPos.push_back(std::pair<WORD, WORD>(x_+1, y_));
						dwPPL++;
					}
					// vpravo dole
					if ( x_+1 < nWidth   &&	y_+1 < nHeight   &&	pPxCopy[(y_+1)*nWidth + x_ + 1] )		
					{
						pPxObject[(y_+1) * nWidth + x_+1] = 0xff;
						pPxCopy[(y_+1) * nWidth + x_+1] = 0x00;
						dqPos.push_back(std::pair<WORD, WORD>(x_+1, y_+1));
						dwPPL++;
					}
					// dole
					if (					y_+1 < nHeight	 &&	pPxCopy[(y_+1)*nWidth + x_] )			
					{
						pPxObject[(y_+1) * nWidth + x_] = 0xff;
						pPxCopy[(y_+1) * nWidth + x_] = 0x00;
						dqPos.push_back(std::pair<WORD, WORD>(x_, y_+1));
						dwPPL++;
					}
					// vlevo dole
					if ( x_ > 0 		 &&	y_+1 < nHeight   &&	pPxCopy[(y_+1)*nWidth + x_ - 1] )
					{
						pPxObject[(y_+1) * nWidth + x_-1] = 0xff;
						pPxCopy[(y_+1) * nWidth + x_-1] = 0x00;
						dqPos.push_back(std::pair<WORD, WORD>(x_-1, y_+1));
						dwPPL++;
					}
					// vlevo
					if ( x_ > 0 							 &&	pPxCopy[y_*nWidth + x_ - 1] )
					{
						pPxObject[y_ * nWidth + x_-1] = 0xff;
						pPxCopy[y_ * nWidth + x_-1] = 0x00;
						dqPos.push_back(std::pair<WORD, WORD>(x_-1, y_));
						dwPPL++;
					}
					// vlevo nahore
					if ( x_ > 0 		 &&	y_ > 0			 &&	pPxCopy[(y_-1)*nWidth + x_ - 1] )
					{
						pPxObject[(y_-1) * nWidth + x_-1] = 0xff;
						pPxCopy[(y_-1) * nWidth + x_-1] = 0x00;
						dqPos.push_back(std::pair<WORD, WORD>(x_-1, y_-1));
						dwPPL++;
					}
					// nahore
					if (					y_ > 0			 &&	pPxCopy[(y_-1)*nWidth + x_] )			
					{
						pPxObject[(y_-1) * nWidth + x_] = 0xff;
						pPxCopy[(y_-1) * nWidth + x_] = 0x00;
						dqPos.push_back(std::pair<WORD, WORD>(x_, y_-1));
						dwPPL++;
					}
					// vpravo nahore
					if ( x_+1 < nWidth   &&	y_ > 0			 &&	pPxCopy[(y_-1)*nWidth +x_ + 1] )
					{
						pPxObject[(y_-1) * nWidth + x_+1] = 0xff;
						pPxCopy[(y_-1) * nWidth + x_+1] = 0x00;
						dqPos.push_back(std::pair<WORD, WORD>(x_+1, y_-1));
						dwPPL++;
					}
				}
				if ( dwPPL >= dwMinPPL && dwPPL <= dwMaxPPL )
				{
					int nWidth_ = nMaxX - nMinX + 1;
					int nHeight_ = nMaxY - nMinY + 1;
					DWORD dwLen = dwHeaderLen + ((nWidth_ + 3) / 4 ) * 4 * nHeight_;
					BYTE* pLetter = (BYTE*)malloc(dwLen);
					BITMAPFILEHEADER* bfh_ = (BITMAPFILEHEADER*)pLetter;
					ZeroMemory(bfh_, sizeof(BITMAPFILEHEADER));
					bfh_->bfType = 0x4D42;
					bfh_->bfSize = dwLen;
					bfh_->bfOffBits = dwHeaderLen;
					BITMAPINFOHEADER* bih_ = (BITMAPINFOHEADER*)&bfh_[1];
					ZeroMemory(bih_, sizeof(BITMAPINFOHEADER));
					bih_->biSize = sizeof(BITMAPINFOHEADER);
					bih_->biWidth = nWidth_;
					bih_->biHeight = nHeight_;
					bih_->biPlanes = 1;
					bih_->biBitCount = 8;
					RGBQUAD* pColors_ = (RGBQUAD*)&bih_[1];

					memcpy(pColors_, pColors, sizeof(RGBQUAD)*256);
					BYTE* pTgtData = (BYTE*)&pColors_[256];

					DWORD dwOffset_ = 0;
					for ( int y_ = 0; y_ < nHeight_; y_++ )
					{
						dwOffset_ = y_ * ((int)((nWidth_+3)/4))*4;
						for ( int x_ = 0; x_ < nWidth_; x_++ )
						{
							pTgtData[dwOffset_] = pPxObject[(y_ + nMinY) * nWidth + x_ + nMinX];
							if ( pTgtData[dwOffset_] != 0x00 && pTgtData[dwOffset_] != 0xff )
								Sleep(1);
							dwOffset_ ++;
						}
					}
					OBJECTDATA o;
					o.dwXPos = nMinX;
					o.dwYPos = nMinY;
					o.dwLen = dwLen;
					o.pData = pLetter;
					os.push_back(o);
				}
				free(pPxObject);
			}
			dwOffset++;
		}
	}

	


	free(pPxCopy);

	return TRUE;
}