#include <sstream>

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

//------------------------------------------------------------------------
void GeneratePositionsOfChromocentersInMetaphasePlate
	(const i3d::Image3d<bool> &cellMask,
	 const i3d::Vector3d<float> &centrosomeA,
	 const i3d::Vector3d<float> &centrosomeB,
	 const size_t chCount,
	 std::vector<Dot>& chromocentersAligned);
//-------------------------------------------------------------------

//
// 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>::DoMetaphase(const size_t noFrames)
{
	 REPORT("\n   *****   *****   cell ID=" << ID << ", STARTING Metaphase at timePoint=" << 
			  timePoint); 

	char doingSynchronization=0; //0 = no; 1 = yes, do sync

	// ---------- local working masks ----------
	//local working copy the cell mask
	i3d::Image3d<bool> maskCell;
	maskCell.SetResolution(scheduler.sceneRes);
	maskCell.SetOffset(scheduler.sceneMasks[timePoint]->GetOffset());
	RenderBPListIntoNewMask(maskCell,true,
	  scmCellBPList,scmCellBPCentre,scmCellOuterBPNumber,
	  noFrames, cellLookDistance);
	//no nucleus and nucleoli in this phase

	// ---------- helper flow fields ----------
	FlowField<float> slsFF;
	InitFlowField(maskCell,slsFF);

	FlowField<float> FF;
	InitFlowField(maskCell,FF);

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

	// -- 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
	std::vector<Dot> metaphasePlate;

	GeneratePositionsOfChromocentersInMetaphasePlate(maskCell,
							centrosomeA->pos,
							centrosomeB->pos,
							chrCount,
							metaphasePlate);


	std::vector< std::vector<Dot> /**/> movement;

	GenerateMovementOfChromocenters(chrCentres,
							metaphasePlate,
							noFrames+1,
							movement);

	//the main cycle
	for (size_t T=0; T < noFrames; ++T) 
	{
		REPORT("\n   *****   *****   cell ID=" << ID << 
				 ", doing Metaphase at timePoint=" << timePoint
		       << ((doingSynchronization)?" with sync":" without sync"));

		//
		// internals
		//

		//no change of cell shape here

		//just movement of dots:
		//over all chromosomes
		for (size_t i=0; i < chrCount; ++i) 
		{
			ChrMoveWithBrownBM(i,0.05f,maskCell,distanceMap,1);
			//ChrMoveWithGravityBM(i,0.25f,maskCell),distanceMap,1;

			i3d::Vector3d<float> shift = 
					  movement[T+1][i].pos - movement[T][i].pos;

			ChrMoveByVectorBM(i,shift,maskCell,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);
		ScmRemoveCellFromMask(*scheduler.sceneMasks[timePoint+1]);
		ScmRenderCellIntoMask(*scheduler.sceneMasks[timePoint+1]);
		  //no nucleus in this phase, no 2nd parameter here

		//after the last frame, metadata must be synchronized with the scene
		if ((T == noFrames-1) && (doingSynchronization == 0)) {
			doingSynchronization=1;

			//changed request for synchronization
			DEBUG_REPORT("Synchronization got turned ON.");

			ConcatenateFlowFields(slsFF,FF);

			i3d::Image3d<float> *tmp;
			tmp=FF.x; FF.x=slsFF.x; slsFF.x=tmp;
			tmp=FF.y; FF.y=slsFF.y; slsFF.y=tmp;
			tmp=FF.z; FF.z=slsFF.z; slsFF.z=tmp;
			//there will be no change back to async mode,
			//hence the slsFF will never be used again
			//slsFF.x->GetVoxelData()=0.f;
			//slsFF.y->GetVoxelData()=0.f;
			//slsFF.z->GetVoxelData()=0.f;
		}


		if (doingSynchronization) {
			//update also the local masks
			//cell:
			maskCell.GetVoxelData()=0;
			RenderBPListIntoMask(maskCell,true,
				scmCellBPList,scmCellBPCentre,scmCellOuterBPNumber);

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

			//update dots in the masks
			ChrMoveByFlowBM(FF, maskCell, distanceMap);
			ChrCentrosomesMoveByFlowBM(FF, maskCell, distanceMap);

			ChrUpdateStats();

			//draw the phantom image
			ChrRenderIntoPhantoms(*scheduler.scenePhantoms[timePoint+1]);
#ifdef GTGEN_WITH_TERRITORIES
			ChrRenderIntoTerritories(*scheduler.sceneTerritories[timePoint+1]);
#endif
		} else {
			//do not touch the local mask
			//but rather remember the new changes
			ConcatenateFlowFields(slsFF,FF);

			ChrUpdateStats();

			//draw the phantom image to where we should appear
			ChrRenderIntoPhantoms(*scheduler.scenePhantoms[timePoint+1],slsFF);
#ifdef GTGEN_WITH_TERRITORIES
			ChrRenderIntoTerritories(*scheduler.sceneTerritories[timePoint+1],slsFF);
#endif
		}

		++timePoint;
	}

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

//------------------------------------------------------------------------
void GeneratePositionsOfChromocentersInMetaphasePlate
	(const i3d::Image3d<bool> &cellMask,
	 const i3d::Vector3d<float> &centrosomeA,
	 const i3d::Vector3d<float> &centrosomeB,
	 const size_t chCount,
	 std::vector<Dot>& chromocentersAligned)
{
	 DEBUG_REPORT("Creating metaphase plate");
	 std::vector< i3d::Vector3d<float> > mp; // metaphase plate
	 mp.clear();

	 // resolution of the image
	 i3d::Resolution res = cellMask.GetResolution();
	 i3d::Offset off = cellMask.GetOffset();
	 i3d::Vector3d<float> res_vec = res.GetRes();
	 float finest_res = std::max(std::max(res_vec.x, res_vec.y), res_vec.z);

	 //cellMask.SaveImage("maska.ics");
	 DEBUG_REPORT("CentrosomeA: " << centrosomeA);
	 DEBUG_REPORT("CentrosomeB: " << centrosomeB);

	 // centrosomes are used to define metaphase plate
	 i3d::Vector3d<float> center = (centrosomeA + centrosomeB)/2.0f;
	 i3d::Vector3d<float> norm_vec = centrosomeA - centrosomeB;

	 // norm vector of metaphase plate (plane)
	 norm_vec /= i3d::Norm(norm_vec);

	 DEBUG_REPORT("normalized connecting line (norm_vec): " << norm_vec);

	 // Finally, we will generate the metaphase plate (all its voxels):
	 // equation of plane in 3D: ax + by + cz + d = 0
	 float a = norm_vec.x;
	 float b = norm_vec.y;
	 float c = norm_vec.z;
	 float d = -(a * center.x + 
					 b * center.y +
					 c * center.z);

	 /*echo("a: " << a);
	 echo("b: " << b);
	 echo("c: " << c);
	 echo("d: " << d);*/

#ifdef GTGEN_DEBUG
	 size_t detectedCounter=0;
#endif

	 // go through all the image voxels, test if they belong to cell mask.
	 // If so, test, if they belong to metaphase plate (equatorial plate)
	 for (size_t i=0; i<cellMask.GetImageSize(); i++)
	 {
		  if (cellMask.GetVoxel(i))
		  {
				i3d::Vector3d<size_t> i_pos = cellMask.GetPos(i);
				i3d::Vector3d<float> f_pos = PixelsToMicrons(i_pos, res) + off;

#ifdef GTGEN_DEBUG
				++detectedCounter;
#endif
				// rasterize the plane with the subpixel (1/2 pixel) precision
				if (fabs(a * f_pos.x + b * f_pos.y + c * f_pos.z + d) <= 
					 (0.5f / finest_res))
				{
					 mp.push_back(f_pos);
				}
		  }
	 }

#ifdef GTGEN_DEBUG
	 REPORT("detected " << mp.size() << " points on the cutting plane, "
	              << chCount << " points is needed");
	 REPORT("discovered " << detectedCounter << " voxels in the mask");
#endif

	 // randomly select 'chCount' positions from the metaphase plate
	 chromocentersAligned.clear();

	 for (size_t i = 0; i < chCount; i++)
	 {
		  //int random_selection = rand() % mp.size();
		  int random_selection = (int)GetRandomUniform(0,mp.size()-1);
		  chromocentersAligned.push_back(mp[random_selection]);
		  mp.erase(mp.begin() + random_selection);
	 }

	 mp.clear();

	 DEBUG_REPORT("Metaphase plate created.");
}

//----------------------------------------------------------------------
// 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>;
