/*

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>

// jmeno, popis a cesta k bloku
const LPWSTR g_pName = L"Resize Canvas";
const LPWSTR g_pDesc = L"Resizes canvas of grayscale bitmap";
const LPWSTR g_pPath = L"Image Processing";
// 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"BMP"};
const WCHAR *g_pOutputNameArray[] = {L"BMP"};

// 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_bUseCentroid = TRUE;
	m_dwWidth = 32;
	m_dwHeight = 32;
}
// 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 bRet = TRUE;
	std::deque<std::pair<void*, DWORD> > dqLayers;
	
	DWORD dwLayers = pInputData->dwLayersArray[0];
	DWORD dwUsedLayers = 0;

	int nWidth_, nHeight_;
	BOOL bUseCentroid;

	EnterCriticalSection(&m_cs);
	nWidth_ = (int)m_dwWidth;
	nHeight_ = (int)m_dwHeight;
	bUseCentroid = m_bUseCentroid;
	LeaveCriticalSection(&m_cs);

	DWORD dwWidthReal_ = ((int)((nWidth_+3)/4))*4;
	for ( DWORD dwLayer = 0; dwLayer < dwLayers; dwLayer++ )
	{
		BYTE *pDataSrc = (BYTE*)pInputData->pDataArrayArray[0][dwLayer];
		BITMAPFILEHEADER *bfh = (BITMAPFILEHEADER*)pDataSrc;
		BITMAPINFOHEADER *bih = (BITMAPINFOHEADER*)&bfh[1];
		DWORD dwSrcLen = pInputData->dwDataLenArrayArray[0][dwLayer];
		if ( dwSrcLen > sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) &&
			bfh->bfType == 0x4D42 && 
			bfh->bfSize == dwSrcLen &&
			bih->biBitCount == 8 &&
			bih->biCompression == 0 &&
			bih->biPlanes == 1 )
		{
			DWORD dwWidthReal = ((int)((bih->biWidth+3)/4))*4;

			DWORD dwTgtLen = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*256 + dwWidthReal_ * nHeight_;
			BYTE* pTgtData = (BYTE*)malloc(dwTgtLen);
			BITMAPFILEHEADER* bfh_ = (BITMAPFILEHEADER*)pTgtData;
			ZeroMemory(bfh_, sizeof(BITMAPFILEHEADER));
			bfh_->bfType = 0x4D42;
			bfh_->bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*256;
			bfh_->bfSize = dwTgtLen;
			BITMAPINFOHEADER* bih_ = (BITMAPINFOHEADER*)&bfh_[1];
			ZeroMemory(bih_, sizeof(BITMAPINFOHEADER));
			bih_->biSize = sizeof(BITMAPINFOHEADER);
			bih_->biWidth = ((int)((nWidth_+3)/4))*4;
			bih_->biHeight = nHeight_;
			bih_->biPlanes = 1;
			bih_->biBitCount = 8;

			RGBQUAD* pSrcColors = (RGBQUAD*)&bih[1];
			RGBQUAD* pTgtColors = (RGBQUAD*)&bih_[1];

			memcpy(pTgtColors, pSrcColors, sizeof(RGBQUAD) * 256);

			BYTE *pSrcPixels = (BYTE*)&pSrcColors[256];
			BYTE *pTgtPixels = (BYTE*)&pTgtColors[256];

			int nWidth = bih->biWidth;
			
			int nHeight = bih->biHeight;

			float fCenterX, fCenterY;

			if ( bUseCentroid )
			{
				DWORD dwCenterX_ = 0, dwCenterY_ = 0;
				DWORD dwOffset = 0;
				DWORD dwPxCount = 0;
				for ( int y = 0; y < nHeight; y++ )
				{
					dwOffset = y*dwWidthReal;
					for ( int x = 0; x < nWidth; x++ )
					{
						if ( pSrcPixels[dwOffset] )
						{
							dwCenterX_ += x;
							dwCenterY_ += y;
							dwPxCount++;
						}
						dwOffset++;
					}
				}
				if ( dwPxCount )
				{
					fCenterX = (float)dwCenterX_ / (dwPxCount);
					fCenterY = (float)dwCenterY_ / (dwPxCount);
				}
				else
				{
					fCenterX = 0;
					fCenterY = 0;
				}
			}
			else
			{
				fCenterX = nWidth / 2.0f;
				fCenterY = nHeight / 2.0f;
			}
			ZeroMemory(pTgtPixels, dwWidthReal_*nHeight_);
			int nMinX_ = (int)((float)nWidth_/2 - fCenterX -0.5f);
			int nMinY_ = (int)((float)nHeight_/2 - fCenterY -0.5f);
			int nMaxX_ = nMinX_ + nWidth;
			int nMaxY_ = nMinY_ + nHeight;

			

			// nMinX_, nMinY_, nMaxX_, nMaxY - souradnice v cilovem obrazku, ktere budou naplneny daty ze zdroje

			//TRACE(L"w: %d, h: %d\r\n", )

			for ( int y = nMinY_; y < nMaxY_; y++ )
			{
//				TRACE("y=%d", y);

				int nOrigY = y-nMinY_;
				DWORD dwOffset = dwWidthReal * nOrigY;
				DWORD dwOffset_ = dwWidthReal_ * y + nMinX_;
				for ( int x = nMinX_; x < nMaxX_; x++ )
				{
//					TRACE(", %d", x);
					int nOrigX = x - nMinX_;
					if ( x >= 0 && y >= 0 && x < nWidth_ && y < nHeight_ )
					{	// nachazime se uvnitr ciloveho obrazku
						if ( nOrigX < nWidth && nOrigY < nHeight )
							if ( pSrcPixels[dwOffset] )
								pTgtPixels[dwOffset_] = pSrcPixels[dwOffset];
						//pTgtPixels[dwOffset_] = 0x01;
					}
					dwOffset++;
					dwOffset_++;
					
				}
//				TRACE("\r\n");
			}



			dqLayers.push_back(std::pair<LPVOID, DWORD>(pTgtData, dwTgtLen));
			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 bRet ? S_OK : S_FALSE;
}

STDMETHODIMP_(void) CImpBlock::Serialize(CArchive &ar)
{
	if ( ar.IsStoring() )
	{
		ar << m_bUseCentroid << m_dwWidth << m_dwHeight;
	}
	else
	{
		ar >> m_bUseCentroid >> m_dwWidth >> m_dwHeight;
	}
}