#ifndef MULTIGRID_PROCESS_INCLUDED
#define MULTIGRID_PROCESS_INCLUDED

#include "LaplacianMatrix/SocketedStreamingSolver.h"
#include "LaplacianMatrix/SocketedMultigrid/SocketData.h"

#define STREAMING_GRID_BUFFER_MULTIPLIER 2
#define PROCESS_PARTITION_FIX 1

template< class PData >
class ProcessPartition
{
public:
	class MyData
	{
	public:
		int width;
		std::vector< int > children;
		PData data;
	};
	int startDepth , endDepth;
	std::vector< MyData > processData;
	void setBounds( int idx , int& start , int& stop );
	int size( void ) const;
	void resize( int );
	MyData& operator[] ( const int& idx );
	const MyData& operator[] ( const int& idx ) const;
	static bool IsValidBandSize( int width , int height , int iters , int minSize );
};

template< class PData >
class ProcessPartitionHierarchy
{
public:
	std::vector< ProcessPartition< PData > > levels;
	int size( void ) const;
	void resize( int );
	ProcessPartition< PData >& operator[] ( const int& idx );
	const ProcessPartition< PData >& operator[] ( const int& idx ) const;
	template< class TData >
	bool Initialize( const ProcessPartition< TData >& initialPartition , int height , int iters , int depths , bool repeat , int minSize );
	int leaves( const int& depth , const int& offset ) const;
};

class ProcessingBlockData
{
public:
	ProcessData pData;
	Socket serverSocket , *syncSockets;
	DataStream *leftStream , *rightStream;
	StreamingGrid *outHighX , *outHighR , *inHighB , *outLowR , *inLowX;
	ProcessingBlockData( void )
	{
		outHighX = outHighR = inHighB = outLowR = inLowX = NULL;
		syncSockets = NULL;
		serverSocket = _INVALID_SOCKET_;
		leftStream = rightStream = NULL;
	}
	~ProcessingBlockData( void )
	{
#if 0
		if( outHighX ) delete outHighX , outHighX = NULL;
		if( outHighR ) delete outHighR , outHighR = NULL;
		if( inHighB )  delete inHighB  , inHighB  = NULL;
		if( outLowR )  delete outLowR  , outLowR  = NULL;
		if( inLowX )   delete inLowX   , inLowX   = NULL;
		if( leftStream )  delete leftStream  , leftStream  = NULL;
		if( rightStream ) delete rightStream , rightStream = NULL;
#endif
	}
};

template< int PixelChannels , int LabelChannels , class StorageType , class SyncType , class PixelType , class LabelType >
class MultigridThread
{
	Pointer( SolverInfo< PixelChannels > ) _solverInfo;

	SocketedStreamingDivergence< PixelChannels , LabelChannels , PixelType , LabelType , StorageType , SyncType >* _sDivergence;
	SocketedMultiGridRestrictionNode< PixelChannels >* _sRestriction;
	SocketedMultiGridStreamingSolver< PixelChannels , StorageType , SyncType >** _solvers;
	int _vCycles;
	bool _verbose , _outOfCore;
	int _periodicType;
	ProcessingBlockData* _blockData;
	int _blockCount;
	void _init(	ProcessingBlockData* blockData , int blockCount , const class GlobalData& globalData , bool showProgress , bool inCore , bool lowPixels , bool pixels , bool labels );
	MultiStreamIOServer* multiStreamIOServer;
public:
	StreamingGrid *lowPixels , *pixels , *labels;
	MultigridThread( void );
	~MultigridThread( void );

	void Initialize(
		const std::vector< std::pair< LabelData< LabelType , LabelChannels > , GradientAverage< PixelChannels > > >& gradientAverage ,
		MultiStreamIOServer* ioServer ,
		ProcessingBlockData* blockData , int blockCount ,
		const GlobalData& globalData , bool showProgress , bool inCore ,
		DotProductStencil& dotMajor , DotProductStencil& d2DotMajor , DotProductStencil& dotMinor , DotProductStencil& d2DotMinor );

	static THREAD_FUNCTION_OUT RunThread( THREAD_FUNCTION_IN );
};

#include "MultigridThread.inl"
#endif // MULTIGRID_PROCESS_INCLUDED