#ifndef DIAGBLOCKDIAGMATRIX_H
#define DIAGBLOCKDIAGMATRIX_H
#include "EnforcePhase.h"

namespace Dmrg
{

template <typename BlockDiagonalMatrixType>
class DiagBlockDiagMatrix
{

	typedef typename BlockDiagonalMatrixType::BuildingBlockType BuildingBlockType;
	typedef typename BuildingBlockType::value_type ComplexOrRealType;
	typedef typename BlockDiagonalMatrixType::VectorRealType VectorRealType;

	class LoopForDiag
	{

		typedef PsimagLite::Concurrency ConcurrencyType;

	public:

		LoopForDiag(BlockDiagonalMatrixType& C1,
		    VectorRealType& eigs1,
		    char option1)
		    : C(C1)
		    , eigs(eigs1)
		    , option(option1)
		    , eigsForGather(C.blocks())
		    , weights(C.blocks())
		{

			for (SizeType m = 0; m < C.blocks(); m++) {
				eigsForGather[m].resize(C.offsetsRows(m + 1) - C.offsetsRows(m));
				weights[m] = C.offsetsRows(m + 1) - C.offsetsRows(m);
			}

			assert(C.rows() == C.cols());
			eigs.resize(C.rows());
		}

		SizeType tasks() const { return C.blocks(); }

		void doTask(SizeType taskNumber, SizeType)
		{
			assert(C.rows() == C.cols());
			SizeType m = taskNumber;
			VectorRealType eigsTmp;
			C.diagAndEnforcePhase(m, eigsTmp, option);
			for (SizeType j = C.offsetsRows(m); j < C.offsetsRows(m + 1); ++j)
				eigsForGather[m][j - C.offsetsRows(m)] = eigsTmp[j - C.offsetsRows(m)];
		}

		void gather()
		{
			assert(C.rows() == C.cols());
			for (SizeType m = 0; m < C.blocks(); ++m) {
				for (SizeType j = C.offsetsRows(m); j < C.offsetsRows(m + 1); ++j)
					eigs[j] = eigsForGather[m][j - C.offsetsRows(m)];
			}
		}

	private:

		BlockDiagonalMatrixType& C;
		VectorRealType& eigs;
		char option;
		typename PsimagLite::Vector<VectorRealType>::Type eigsForGather;
		typename PsimagLite::Vector<SizeType>::Type weights;
	};

public:

	// Parallel version of the diagonalization of a block diagonal matrix
	// Note: In reality, Parallelization is disabled here because a LAPACK call
	//        is needed and LAPACK is not necessarily thread safe.
	// This function is NOT called by useSvd
	static void diagonalise(BlockDiagonalMatrixType& C,
	    VectorRealType& eigs,
	    char option)
	{
		typedef PsimagLite::NoPthreadsNg<LoopForDiag> ParallelizerType;
		typedef PsimagLite::Concurrency ConcurrencyType;
		SizeType savedNpthreads = ConcurrencyType::codeSectionParams.npthreads;
		ConcurrencyType::codeSectionParams.npthreads = 1;
		ParallelizerType threadObject(ConcurrencyType::codeSectionParams);

		LoopForDiag helper(C, eigs, option);

		threadObject.loopCreate(helper); // FIXME: needs weights

		helper.gather();

		ConcurrencyType::codeSectionParams.npthreads = savedNpthreads;
	}
}; // class DiagBlockDiagMatrix

} // namespace Dmrg
#endif // DIAGBLOCKDIAGMATRIX_H
