/*

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"

#pragma comment(lib, "gdiplus.lib")

#include <olectl.h>
#include "imp_block.h"
//#include "imp_config.h"
#include <deque>

using namespace Gdiplus;

// jmeno, popis a cesta k bloku
const LPWSTR g_pName = L"IMG2BMP";
const LPWSTR g_pDesc = L"Converts image to 24bit BMP";
const LPWSTR g_pPath = L"Conversion";
// 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"GIF"};
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

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

	UINT num;
	UINT size;
	GetImageEncodersSize(&num, &size);
	m_pImgCodecInfo = (ImageCodecInfo*)malloc(size);
	GetImageEncoders(num, size, m_pImgCodecInfo);
	m_unImageCodecIndex = -1;

 	for ( UINT i = 0; i < num; i++ )
	{
		if ( (CString)m_pImgCodecInfo[i].MimeType == L"image/bmp" )
		{
			m_unImageCodecIndex = i;
			break;
		}
	}

	return S_OK;
}

STDMETHODIMP CImpBlock::ExitInstance()
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState( ))
	free(m_pImgCodecInfo);
	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)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState( ))


	EnterCriticalSection(&m_cs);
	LeaveCriticalSection(&m_cs);

	std::deque<std::pair<LPVOID, DWORD> > dqData;

	DWORD dwUsedLayers = 0;

	for ( DWORD dwLayer = 0; dwLayer < pInputData->dwLayersArray[0]; dwLayer++ )
	{	// pro kazdou vrstvu
		LPVOID srcdata = pInputData->pDataArrayArray[0][dwLayer];
		DWORD dwSrcLen = pInputData->dwDataLenArrayArray[0][dwLayer];
//		if ( dwSrcLen > 6 && 
//			memcmp(srcdata, "GIF", 3) == 0 )
//		{	// podle hlavicky jsou data GIF
			DWORD dwLen = dwSrcLen;
			LPVOID data;
			ConvertGif2Bmp((BYTE*)srcdata, &data, &dwLen);
			dqData.push_back(std::pair<VOID*, DWORD>(data, dwLen));
			dwUsedLayers++;
//		}
	}

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

	return S_OK;
}

BOOL CImpBlock::ConvertGif2Bmp(BYTE* pSource, LPVOID *ppTgt, DWORD /*inout*/*pdwLen)
{
	if ( -1 != m_unImageCodecIndex )
	{
		IStream *pIStream, *pIStream2;
		CreateStreamOnHGlobal(NULL, TRUE, &pIStream);
		ULARGE_INTEGER ulnSize;
		LARGE_INTEGER lnOffset;
		ulnSize.QuadPart = *pdwLen;
		lnOffset.QuadPart = 0;
		if ( S_OK == pIStream->SetSize(ulnSize) )
		{
			if ( S_OK == pIStream->Seek(lnOffset, STREAM_SEEK_SET, NULL) )
			{
				ULONG mysize;
				pIStream->Write(pSource, *pdwLen, &mysize);
			}
		}
		CreateStreamOnHGlobal(NULL, TRUE, &pIStream2);
		Image im(pIStream);

		Bitmap *theResizedImage = new Bitmap(im.GetWidth(), im.GetHeight());
		Graphics theResizer(theResizedImage);
		theResizer.SetInterpolationMode(InterpolationModeNearestNeighbor);
		theResizer.SetSmoothingMode(SmoothingModeNone);
		theResizer.DrawImage(&im, Rect(0, 0, im.GetWidth(), im.GetHeight()));

		theResizedImage->Save(pIStream2,  &m_pImgCodecInfo[m_unImageCodecIndex].Clsid);

		lnOffset.QuadPart = 0;
		pIStream2->Seek(lnOffset, STREAM_SEEK_END, &ulnSize);
		pIStream2->Seek(lnOffset, STREAM_SEEK_SET, NULL);

		ULONG size = (ULONG)ulnSize.QuadPart;
		*ppTgt = malloc(size);
		*pdwLen = size;

		pIStream2->Read(*ppTgt, size, NULL);

		delete(theResizedImage);
		pIStream2->Release();
		pIStream->Release();
	}
	return TRUE;
}

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