/*

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"Save file";
const LPWSTR g_pDesc = L"Save data on input of block and route them to output";
const LPWSTR g_pPath = L"General";
// 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"DATA", L".RAW/DWid"};
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_dwId = 0;
	m_bDoNothing = FALSE;
	m_bCheckOverwrite = FALSE;
}
// 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;
	
	BOOL bCheckOverwrite;
	BOOL bDoNothing;
	DWORD dwId;
	BOOL bId = FALSE;
	CString strPath;
	CString strName, strNameOrig;
	DWORD dwUsedLayers = 0;
	BOOL bExtId = FALSE;

	EnterCriticalSection(&m_cs);
	bDoNothing = m_bDoNothing;
	dwId = m_dwId;
	strPath = m_strPath;
	strName = m_strName;
	bCheckOverwrite = m_bCheckOverwrite;
	LeaveCriticalSection(&m_cs);
	
	strNameOrig = strName;

	if ( pInputData->dwLayersArray[1] == 1 &&
		pInputData->dwDataLenArrayArray[1][0] == sizeof(DWORD) )
	{
		bExtId = TRUE;
		memcpy(&dwId, pInputData->pDataArrayArray[1][0], sizeof(DWORD));
	}

	CString strId;
	strId.Format(L"%08x", dwId);
	if ( strName.Replace(L"%id%", strId) )
		bId = TRUE;

	DWORD dwLayerCount = pInputData->dwLayersArray[0];
	for ( DWORD dwLayer = 0; dwLayer < dwLayerCount; dwLayer++ )
	{
		CString strNameFinal;
		strNameFinal = strName;
		CString strLayer;
		strLayer.Format(L"%d", dwLayer);
		strNameFinal.Replace(L"%layer%", strLayer);
		CString strFileNameFinal = strNameFinal;
		strNameFinal = strPath + L"\\" + strNameFinal;
		HANDLE hFile;
		switch ( bCheckOverwrite )
		{
		case FALSE: // zaptat se, jestli se ma prepsat...  DODELAT!!!
			do
			{
				hFile = CreateFile(strNameFinal, GENERIC_WRITE, FILE_SHARE_WRITE,
					NULL, CREATE_NEW, 0, NULL);
				DWORD dwErr = GetLastError();
				if ( INVALID_HANDLE_VALUE == hFile )
				{
					if ( dwErr == ERROR_FILE_EXISTS && !bDoNothing )
					{
						CString str;
						if ( bId )
						{
							str.Format(L"File '%s' exists. \r\nDo you want to overwrite file[YES], \r\nfind file with free 'id'[NO], or Cancel Process?", strFileNameFinal);
							switch ( MessageBox(NULL, str, L"Save File Question", MB_YESNOCANCEL) )
							{
							case IDYES:
								hFile = CreateFile(strNameFinal, GENERIC_WRITE, FILE_SHARE_WRITE,
									NULL, CREATE_ALWAYS, 0, NULL);
								break;
							case IDNO:
								do
								{
									dwId++;
									strName = strNameOrig;
									strId.Format(L"%08x", dwId);
									strNameFinal = strName;
									strNameFinal.Replace(L"%id%", strId);
									strNameFinal = strPath + L"\\" + strNameFinal;
									hFile = CreateFile(strNameFinal, GENERIC_WRITE, FILE_SHARE_WRITE,
										NULL, CREATE_NEW, 0, NULL);
									dwErr = GetLastError();
								} while ( hFile == INVALID_HANDLE_VALUE && dwErr == ERROR_FILE_EXISTS );
								break;
							case IDCANCEL:
								bRet = FALSE;
								break;
							}
						}
					}
					else
					{
						break;
					}
					if ( dwErr && dwErr != ERROR_FILE_EXISTS )
					{
						CString str;
						str.Format(L"Saving file %s cannot be completted, code %d", dwErr);
						MessageBox(NULL, str, L"Save File Error", MB_OK);
						bRet = FALSE;
					}
					if ( !bRet )
						break;
				}
			}while ( INVALID_HANDLE_VALUE == hFile );

//			if ( INVALID_HANDLE == hFile && GetLastError() == )
			break;
		default:	// vytvorit vynucene
			hFile = CreateFile(strNameFinal, GENERIC_WRITE, FILE_SHARE_WRITE,
				NULL, CREATE_ALWAYS, 0, NULL);
			break;
		}
		if ( INVALID_HANDLE_VALUE != hFile  )
		{	// podarilo se soubor vytvorit
			CFile file(hFile);
			DWORD dwLen = pInputData->dwDataLenArrayArray[0][dwLayer];
			VOID* pSrc = pInputData->pDataArrayArray[0][dwLayer];
			file.Write(pSrc, dwLen);
			file.Close();
//			CloseHandle(hFile);
			VOID* pData = malloc(dwLen);
			memcpy(pData, pSrc, dwLen);
			dqLayers.push_back(std::pair<LPVOID, DWORD>(pData, 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();
	
	if ( !bExtId )
	{
		EnterCriticalSection(&m_cs);
		m_dwId = ++dwId;
		LeaveCriticalSection(&m_cs);
		if ( m_pxConfig )
			((CImpBlockConfig*)m_pxConfig)->UpdateStatus();
	}

	return bRet ? S_OK : S_FALSE;
}

STDMETHODIMP_(void) CImpBlock::Serialize(CArchive &ar)
{
	if ( ar.IsStoring() )
	{
		ar << m_strPath << m_strName << m_bCheckOverwrite << m_bDoNothing << m_dwId;
	}
	else
	{
		ar >> m_strPath >> m_strName >> m_bCheckOverwrite >> m_bDoNothing >> m_dwId;
	}
}