// BlockItem.cpp : implementation file
//

#include "stdafx.h"
#include "xpeluc01.h"
#include "BlockItem.h"
#include "SchemeDoc.h"

IMPLEMENT_SERIAL(CBlockItem, CObject, 1)

// CBlockItem

CBlockItem::CBlockItem()
{
	m_fX = 0.5;
	m_fY = 0.5;
	m_dwInputPortCount = 0;
	m_dwOutputPortCount = 0;
	m_pConnectorTargetBlockArray = NULL;
	m_pConnectorTargetPortArray = NULL;
	m_pDocument = NULL;
	m_pParentBlock = NULL;
	m_dwLastProcessId = -1;
	m_dLastProcessTime = 0;
	m_refCount = 1;
	InitializeCriticalSection(&m_csProcess);
}

CBlockItem::~CBlockItem()
{
	//TRACE("Destructing CBlockItem instance\r\n");
	DeleteCriticalSection(&m_csProcess);
	if ( m_pConnectorTargetBlockArray )
		delete[] (m_pConnectorTargetBlockArray);
	if ( m_pConnectorTargetPortArray )
		delete[] (m_pConnectorTargetPortArray);
}

ULONG CBlockItem::AddRef()
{
	return InterlockedIncrement(&m_refCount);
}

ULONG CBlockItem::Release()
{
	ULONG ret;
	if ( 0 == (ret = InterlockedDecrement(&m_refCount)) )
	{
		this->ExitInstance();
		delete(this);
		return 0;
	}
	return ret;
}

CBlockItem* CBlockItem::Clone() const
{
	CBlockItem* ret;
	CMemFile file;
	CArchive ar1(&file, CArchive::store);
	ar1 << (CObject*)this;
	ar1.Close();
	file.SeekToBegin();
	CArchive ar2(&file, CArchive::load);
	ar2 >> (CObject*&)ret;
	ar2.Close();
	file.Close();
	return ret;
}

CString CBlockItem::GetName() const
{
	return L"[noname]";
}

CString CBlockItem::GetDescription() const
{
	return L"Unknown block type...";
}

CString CBlockItem::GetStatus() const
{
	CString ret;
	ret.Format(L"Last time: %fms", m_dLastProcessTime);
	return ret;
	//return L"[no status]";
}

CString CBlockItem::GetInputPortName(DWORD dwPort) const
{
	return L"I";
}

CString CBlockItem::GetOutputPortName(DWORD dwPort) const
{
	return L"O";
}

BOOL CBlockItem::InitInstance()
{
	return TRUE;
}

BOOL CBlockItem::ExitInstance()
{
	return TRUE;
}

BOOL CBlockItem::Start()
{
	return TRUE;
}

BOOL CBlockItem::Stop()
{
	return TRUE;
}

DWORD CBlockItem::GetInputCount() const
{
	return m_dwInputPortCount;
}

DWORD CBlockItem::GetOutputCount() const
{
	return m_dwOutputPortCount;
}

VOID CBlockItem::SetPos(const POINTF *pPoint)
{
	m_fX = pPoint->x;
	m_fY = pPoint->y;
}

VOID CBlockItem::GetPos(POINTF *pPoint) const
{
	pPoint->x = m_fX;
	pPoint->y = m_fY;
}

// CBlockItem member functions

void CBlockItem::Serialize(CArchive& ar)
{
	CObject::Serialize(ar);
	if (ar.IsStoring())
	{	// storing code
		ar << m_fX << m_fY;
		BYTE state = 0x00;
		if ( m_pDocument && m_pDocument->m_setRun.find(this) != m_pDocument->m_setRun.end() )
			state |= 0x01;
		if ( m_pDocument && m_pDocument->m_setOwnTrigger.find(this) != m_pDocument->m_setOwnTrigger.end() )
			state |= 0x02;
		ar << state;
	}
	else
	{	// loading code
		BYTE state;
		ar >> m_fX >> m_fY;
		ar >> state;
		if ( state & 0x01 )
			m_pDocument->m_setRun.insert(this);
		if ( state & 0x02 )
			m_pDocument->m_setOwnTrigger.insert(this);

	}
}

BOOL CBlockItem::SetInputCount(const DWORD dwPorts)
{
	EnterCriticalSection(&m_csProcess);
	CBlockItem **pNewBlkArray = new CBlockItem*[dwPorts];
	DWORD *pNewPortArray = new DWORD[dwPorts];

	DWORD dwMinPortCount = ( dwPorts < m_dwInputPortCount ) ? dwPorts : m_dwInputPortCount;

	for ( DWORD i = 0; i < dwMinPortCount; i++ )
	{
		pNewBlkArray[i] = m_pConnectorTargetBlockArray[i];
		pNewPortArray[i] = m_pConnectorTargetPortArray[i];
	}

	for ( DWORD i = dwMinPortCount; i < dwPorts; i++ )
	{
		pNewBlkArray[i] = NULL;
		pNewPortArray[i] = -1;
	}

	if ( m_pConnectorTargetBlockArray )
		delete[] (m_pConnectorTargetBlockArray);
	if ( m_pConnectorTargetPortArray )
		delete[] (m_pConnectorTargetPortArray);

	m_pConnectorTargetBlockArray = pNewBlkArray;
	m_pConnectorTargetPortArray = pNewPortArray;
	m_dwInputPortCount = dwPorts;
	LeaveCriticalSection(&m_csProcess);
	return TRUE;
}

BOOL CBlockItem::SetOutputCount(const DWORD dwPorts)
{
	EnterCriticalSection(&m_csProcess);
	m_dwOutputPortCount = dwPorts;
	LeaveCriticalSection(&m_csProcess);
	return TRUE;
}

BOOL CBlockItem::GetModifiedFlag()
{
	return FALSE;
}

VOID CBlockItem::SetModifiedFlag(BOOL bModified)
{
}

BOOL CBlockItem::SetInConnectorTarget(DWORD dwPort, CBlockItem *pTargetBlock, DWORD dwTargetPort)
{
	EnterCriticalSection(&m_csProcess);
	ASSERT(dwPort < m_dwInputPortCount);
//	ASSERT(dwTargetPort < pTargetBlock->m_dwOutputPortCount);
	m_pConnectorTargetBlockArray[dwPort] = pTargetBlock;
	m_pConnectorTargetPortArray[dwPort] = dwTargetPort;
	LeaveCriticalSection(&m_csProcess);
	return TRUE;
}

BOOL CBlockItem::GetInConnectorTarget(DWORD dwPort, CBlockItem **ppTargetBlock, DWORD *pdwTargetPort, BOOL bAddRef)
{
	BOOL bRet = TRUE;
	EnterCriticalSection(&m_csProcess);
	ASSERT(dwPort < m_dwInputPortCount);
	if ( !m_pConnectorTargetBlockArray[dwPort] )
	{
		*ppTargetBlock = NULL;
		bRet = FALSE;
	}
	else
	{
		*ppTargetBlock = m_pConnectorTargetBlockArray[dwPort];
		*pdwTargetPort = m_pConnectorTargetPortArray[dwPort];
	}
	if ( bAddRef && *ppTargetBlock )
		(*ppTargetBlock)->AddRef();
	LeaveCriticalSection(&m_csProcess);
	return bRet;
}

BOOL CBlockItem::DeleteReferencesToBlock(CBlockItem* ptr)
{
	EnterCriticalSection(&m_csProcess);
	for ( DWORD i = 0; i < m_dwInputPortCount; i++ )
	{
		if ( m_pConnectorTargetBlockArray[i] == ptr )
		{
			m_pConnectorTargetBlockArray[i] = NULL;
			m_pConnectorTargetPortArray[i] = -1;
		}
	}
	LeaveCriticalSection(&m_csProcess);
	return TRUE;
}

BOOL CBlockItem::SetDocPtr(CSchemeDoc* ptr)
{
	m_pDocument = ptr;
	return TRUE;
}

BOOL CBlockItem::ShowProperties(BOOL bTest)
{
	if ( bTest )
		return FALSE;
	return FALSE;
}

BOOL CBlockItem::SetParentBlock(CBlockItem_Virtual *ptr)
{
	m_pParentBlock = ptr;
	return TRUE;
}

CBlockItem_Virtual* CBlockItem::GetParentBlock() const
{
	return m_pParentBlock;
}

BOOL CBlockItem::Process(DWORD dwProcessId, REVDATAMAP &rRevDataMap)
{
	if ( dwProcessId == m_dwLastProcessId ) // Blok byl jiz zpracovan
		return TRUE;
	if ( !ProcessRev(dwProcessId, rRevDataMap) ) // pridat zpetna data
		return FALSE;

	// vypocitame vystup bloku zapojenych na vstup
	DWORD dwInputPorts = GetInputCount();
	for ( DWORD i = 0; i < dwInputPorts; i++ )
	{
		CBlockItem* pTgtBlock;
		DWORD dwTgtPort;
		if ( GetInConnectorTarget(i, &pTgtBlock, &dwTgtPort, TRUE) && pTgtBlock )
		{
			BOOL bRet = pTgtBlock->Process(dwProcessId, rRevDataMap);
			pTgtBlock->Release();
			if ( !bRet )
			{
				ClearRevMap(rRevDataMap);
				return FALSE;
			}
		}
	}
	// vytvorime pole s daty pro blok
	BLOCKDATA inputData;
	if ( dwInputPorts )
	{
		inputData.pDataArrayArray = new void**[dwInputPorts];
		ZeroMemory(inputData.pDataArrayArray, sizeof(void**)*dwInputPorts);
		inputData.dwLayersArray = new DWORD[dwInputPorts];
		inputData.dwDataLenArrayArray = new DWORD*[dwInputPorts];
//		inputData.pCriticalSectionArray = new CRITICAL_SECTION*[dwInputPorts];
//		ZeroMemory(inputData.pCriticalSectionArray, sizeof(CRITICAL_SECTION*) * dwInputPorts);
		ZeroMemory(inputData.dwLayersArray, sizeof(DWORD) * dwInputPorts);
		ZeroMemory(inputData.dwDataLenArrayArray, sizeof(DWORD*) * dwInputPorts);
		for ( DWORD i = 0; i < dwInputPorts; i++ )
		{	// pro vsechny vstupni porty
			CBlockItem* pTgtBlock;
			DWORD dwTgtPort;
			if ( GetInConnectorTarget(i, &pTgtBlock, &dwTgtPort, TRUE) && pTgtBlock )
			{	// najdi blok pripojeny k portu
				LPCRITICAL_SECTION pCs = pTgtBlock->GetOutputCriticalSectionPtr(dwTgtPort);
				if ( pCs )
				{
					EnterCriticalSection(pCs);
					PORTDATA portData;
					portData = pTgtBlock->GetOutputPortData(dwTgtPort);
					if ( portData.pDataArray )
					{
						DWORD dwLayers;
						if ( dwLayers = portData.dwLayers )
						{	// jestli na bloku - portu zapojenem do tohoto bloku jsou data
							inputData.pDataArrayArray[i] = new void*[dwLayers];
							inputData.dwLayersArray[i] = dwLayers;
							inputData.dwDataLenArrayArray[i] = portData.dwDataLenArray;
							memcpy(inputData.pDataArrayArray[i], portData.pDataArray, sizeof(void*)*dwLayers);
	//							inputData.pCriticalSectionArray[i] = pData->pCriticalSectionArray[dwTgtPort];
						}
					}
					LeaveCriticalSection(pCs);
				}
				pTgtBlock->Release();
			}
		}
	}
	else
	{
		inputData.pDataArrayArray = NULL;
//		inputData.pCriticalSectionArray = NULL;
		inputData.dwLayersArray = NULL;
		inputData.dwDataLenArrayArray = NULL;
	}
	LARGE_INTEGER liF, liTStart, liTEnd;
	QueryPerformanceFrequency(&liF);
	QueryPerformanceCounter(&liTStart);


	//////////////////////////////////////////////////////////////////
/*	TRACE(L"Processing block %s", this->GetName());
	for ( DWORD i = 0; i < GetOutputCount(); i++ )
	{
		TRACE(L", CS[%d]=%x", i, GetOutputData()->pCriticalSectionArray[i]);
	}
	TRACE(L"\r\n");*/
	//////////////////////////////////////////////////////////////////

	BOOL bOK = ProcessData(&inputData/*, rRevDataMap*/);
	ClearRevMap(rRevDataMap);
	QueryPerformanceCounter(&liTEnd);
	m_dLastProcessTime = ( (double)(liTEnd.QuadPart - liTStart.QuadPart) / liF.QuadPart * 1000.0 );
	// uvolnime pamet
	for ( DWORD i = 0; i < dwInputPorts; i++ )
	{
		if ( inputData.pDataArrayArray[i] )
			delete[](inputData.pDataArrayArray[i]);
	}
	delete[](inputData.dwLayersArray);
	delete[](inputData.pDataArrayArray);
	delete[](inputData.dwDataLenArrayArray);
//	delete[](inputData.pCriticalSectionArray);

	m_dwLastProcessId = dwProcessId;

	//m_pDocument->UpdateViewer_Safe(this);
	return bOK;
}

BOOL CBlockItem::ProcessRev(DWORD dwProcessId, REVDATAMAP &rRevDataMap)
{
	return TRUE;
}

BOOL CBlockItem::ClearRevMap(REVDATAMAP &rRevDataMapClean)
{
	return TRUE;
}

BOOL CBlockItem::ProcessData(LPBLOCKDATA pInputData)
{
	return TRUE;
}

const PORTDATA CBlockItem::GetOutputPortData(DWORD dwPort) const
{
	PORTDATA pd;
	pd.dwDataLenArray = NULL;
	pd.dwLayers = 0;
	pd.pDataArray = NULL;
	return pd;
}

LPCRITICAL_SECTION CBlockItem::GetOutputCriticalSectionPtr(DWORD dwPort) const
{
	return NULL;
}

double CBlockItem::GetLastProcessingTime() const
{
	return m_dLastProcessTime;
}