#include <sstream>

#include "cell.h"
#include "toolbox/flowfields.h"
#include "toolbox/bp_lists.h"

//
// look into cell_00_Samplephase.cpp to see full version of the
// template for an atomic cell phase function like this one
//

template <class MV, class PV>
void Cell<MV, PV>::DoG2Phase(const size_t noFrames)
{
	 REPORT("\n   *****   *****   cell ID=" << ID << ", STARTING G2-phase at timePoint=" << 
			  timePoint); 

	// ---------- local working masks ----------
	//local working copy of the cell mask - not needed
	//only nucleus
	i3d::Image3d<MV> maskNucleus;

	maskNucleus.SetResolution(scheduler.sceneRes);
	maskNucleus.SetOffset(scheduler.sceneMasks[timePoint]->GetOffset());
	//but the size of _nucleus_ mask image should contain the whole _cell_
	AllocateAndZeroNewMask(maskNucleus, scmCellBPList, noFrames, cellLookDistance);

	//prepare working copy of cell nucleus with nucleoli
	RenderBPListIntoMask(maskNucleus,(MV)1,
	  scmNucleusBPList,scmNucleusBPCentre,scmNucleusOuterBPNumber);
	RenderBPListIntoMask(maskNucleus,(MV)2,
	  scmNucleoli1BPList,scmNucleoli1BPCentre,scmNucleoli1OuterBPNumber);
	RenderBPListIntoMask(maskNucleus,(MV)3,
	  scmNucleoli2BPList,scmNucleoli2BPCentre,scmNucleoli2OuterBPNumber);
	//now in the maskNucleus image: 0 means outside,
	//1 nucleus, 2 and 3 each nucleolus


	i3d::Image3d<float> *distanceMap = NULL;

	// ---------- helper flow fields ----------
	FlowField<float> FF;
	InitFlowField(maskNucleus,FF);

	// ---------- actions a phase needs to do before the frame generating cycle ----------
	//prepare the activity plan to spread the phase duties over the given number of frames

	//the main cycle
	for (size_t T=0; T < noFrames; ++T) {
		REPORT("\n   *****   *****   cell ID=" << ID << ", doing G2-phase at timePoint=" << timePoint \
		       << " with sync")

		//
		// internals
		//

		//no change of cell shape here

		//just movement of dots:
		//over all chromosomes
		for (size_t i=0; i < chrCount; ++i) 
		{
			ChrMoveWithBrown(i,0.07f,maskNucleus,distanceMap,1);
			//ChrMoveWithGravity(i,0.25f,maskNucleus,distanceMap,1);
		}


		//
		// externals
		//

		//reset the current flow field
		FF.x->GetVoxelData()=0.f;
		FF.y->GetVoxelData()=0.f;
		if (FF.z) FF.z->GetVoxelData()=0.f;

		ScmUniversalMovement(FF);


		//
		// a footer common to all phase functions:
		//

		//move the boundary points and draw cell into sceneMasks
		MoveBPListAndCentre(scmCellBPList,scmCellBPCentre,FF);
		MoveBPListAndCentre(scmNucleusBPList,scmNucleusBPCentre,FF);
		MoveBPListAndCentre(scmNucleoli1BPList,scmNucleoli1BPCentre,FF);
		MoveBPListAndCentre(scmNucleoli2BPList,scmNucleoli2BPCentre,FF);
		ScmRemoveCellFromMask(*scheduler.sceneMasks[timePoint+1]);
		ScmRenderCellIntoMask(*scheduler.sceneMasks[timePoint+1]
#ifdef GTGEN_WITH_NUCLEIMASKS
			,scheduler.sceneNuclei[timePoint+1]
#endif
		);


		//update also the local masks
		//no need to update cell now, it is not used in the cycle

		//nucleous with nucleoli:
		maskNucleus.Clear();
		RenderBPListIntoMask(maskNucleus,(MV)1,
		  scmNucleusBPList,scmNucleusBPCentre,scmNucleusOuterBPNumber);
		RenderBPListIntoMask(maskNucleus,(MV)2,
		  scmNucleoli1BPList,scmNucleoli1BPCentre,scmNucleoli1OuterBPNumber);
		RenderBPListIntoMask(maskNucleus,(MV)3,
		  scmNucleoli2BPList,scmNucleoli2BPCentre,scmNucleoli2OuterBPNumber);

		if (distanceMap)
		{
			 delete distanceMap; 
			 distanceMap = NULL; // force recomputing distance map
		}

		//update dots in the masks
		ChrMoveByFlow(FF, maskNucleus, distanceMap,(MV)1);

		ChrUpdateStats();

		//draw the phantom image
		ChrRenderIntoPhantoms(*scheduler.scenePhantoms[timePoint+1]);
#ifdef GTGEN_WITH_TERRITORIES
		ChrRenderIntoTerritories(*scheduler.sceneTerritories[timePoint+1]);
#endif

		++timePoint;
	}

	//cleaning here if necessary
	
	if (distanceMap)
		 delete distanceMap;

	if (centrosomeA) delete centrosomeA;
	if (centrosomeB) delete centrosomeB;

	// create centrosomes
	centrosomeA = new Dot;
	centrosomeB = new Dot;

	//render cell mask for the GenerateCentrosomes() function
	//into a new fresh only-this purpose image
	i3d::Image3d<bool> maskCell;
	RenderBPListIntoNewMask(maskCell,true,
		scmCellBPList,scmCellBPCentre,scmCellOuterBPNumber,0, cellLookDistance);

	GenerateCentrosomes(maskCell, centrosomeA->pos, centrosomeB->pos);

	//since cell is doing non-rigid, boundary points move appart
	//we rather re-sample them again
	scmCellOuterBPNumber=
	  ResetBoundaryPoints(scmCellBPList,scmCellBPCentre,*scheduler.sceneMasks[timePoint],ID);
	//note that here we must use current timePoint, the one already finished

	//changing boundary points, we need to re-init the hints image
	scheduler.PnGetMeNonrigidHints(scmCellBPList,scmCellOuterBPNumber,scmPnHints);

	scmNucleusOuterBPNumber=
	  ResetBoundaryPoints(scmNucleusBPList,scmNucleusBPCentre,maskNucleus);
	scmNucleoli1OuterBPNumber=
	  ResetBoundaryPoints(scmNucleoli1BPList,scmNucleoli1BPCentre,maskNucleus,(MV)2);
	scmNucleoli2OuterBPNumber=
	  ResetBoundaryPoints(scmNucleoli2BPList,scmNucleoli2BPCentre,maskNucleus,(MV)3);
}


//----------------------------------------------------------------------
// explicit instantiations to force explicit build of the functions 
// in this file because otherwise the linker couldn't find them
//----------------------------------------------------------------------
template class Cell<i3d::GRAY8,  i3d::GRAY16>;
template class Cell<i3d::GRAY16, i3d::GRAY16>;
