#include "TargetVolumeSteppable.h"

#include <CompuCell3D/Simulator.h>
#include <CompuCell3D/Automaton/Automaton.h>
#include <CompuCell3D/Potts3D/Potts3D.h>
#include <CompuCell3D/Potts3D/CellInventory.h>


#include <XMLCereal/XMLPullParser.h>
#include <XMLCereal/XMLSerializer.h>
#include <BasicUtils/BasicString.h>
#include <BasicUtils/BasicException.h>
#include <string>

#include <CompuCell3D/DiffusionSolverFE.h>
#include <CompuCell3D/ClassRegistry.h>

using namespace CompuCell3D;
using namespace std;
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
TargetVolumeSteppable::TargetVolumeSteppable()
 : Steppable()
{
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
TargetVolumeSteppable::~TargetVolumeSteppable()
{
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void TargetVolumeSteppable::init(Simulator *simulator) {
   ///the following line makes inheritinace of this class very inconvenient
  diffusionSolverPtr=(DiffusionSolverFE*)simulator->getClassRegistry()->getStepper("DiffusionSolverFE");
   
  potts = simulator->getPotts();
  cellInventoryPtr=& potts->getCellInventory();
  
  ///setting pointer to target volume setters and updaters
  ///If you define your own custom setter or updarter you will have to set them here
  setInitTargetVolume=&TargetVolumeSteppable::simpleInitTargetVolume;
  
  updateTargetVolume=&TargetVolumeSteppable::simpleUpdateTargetVolume;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void TargetVolumeSteppable::start() {
   
   ///will initialize cell clock here depending on the position of the cells
   CellInventory::cellInventoryIterator cInvItr;
   ///loop over all the cells in the inventory
   Point3D com;
   CellG * cell;
   
   
   for(cInvItr=cellInventoryPtr->cellInventoryBegin() ; cInvItr !=cellInventoryPtr->cellInventoryEnd() ;++cInvItr ){
      
      cell=*cInvItr;
      (this->*setInitTargetVolume)(cell);
      
      
   }

}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void TargetVolumeSteppable::simpleInitTargetVolume(CellG* _cell) {
   Point3D com;
   float currentConcentration=0.0;
   
   ///CHANGE HERE
   Field3DImpl<float> * concentrationField = diffusionSolverPtr->getConcentrationField();

   com.x=_cell->xCM/ _cell->volume;
   com.y=_cell->yCM/ _cell->volume;
   com.z=_cell->zCM/ _cell->volume;

   currentConcentration = concentrationField->get(com);///concentration in COM
   
   ///target volume may depend on concentratin in COM; Fell free to change it 
   _cell->targetVolume=initialTargetVolume;
   //cerr<<"simpleinit vol="<<volNode.get(_cell)->targetVolume<<endl;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void TargetVolumeSteppable::step(const unsigned int _currentStep) {

   currentStep=_currentStep;
   ///will initialize cell clock here depending on the position of the cells
   CellInventory::cellInventoryIterator cInvItr;
   ///loop over all the cells in the inventory
   Point3D com;
   CellG * cell;
   
   
   for(cInvItr=cellInventoryPtr->cellInventoryBegin() ; cInvItr !=cellInventoryPtr->cellInventoryEnd() ;++cInvItr ){
      
      cell=*cInvItr;
      (this->*updateTargetVolume)(cell);
      
      
   }
   
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void TargetVolumeSteppable::simpleUpdateTargetVolume(CellG * _cell){
   ///Here you may put your code which will calculate updated target Volume
   /// before setting a volume check if the cell is a newly created one (i.e. has targetVolume=0.0)

   Point3D com;
   float currentConcentration=0.0;
   
   ///CHANGE HERE
   Field3DImpl<float> * concentrationField = diffusionSolverPtr->getConcentrationField();

   com.x=_cell->xCM/ _cell->volume;
   com.y=_cell->yCM/ _cell->volume;
   com.z=_cell->zCM/ _cell->volume;

   currentConcentration = concentrationField->get(com);///concentration in COM
   
   ///target volume may depend on concentratin in COM; Fell free to change it 

   _cell->targetVolume=initialTargetVolume+currentStep;
   //cerr<<"updated vol="<<volNode.get(_cell)->targetVolume<<endl;
}




//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void TargetVolumeSteppable::readXML(XMLPullParser &in) {
  in.skip(TEXT);
  while (in.check(START_ELEMENT)) {
    if (in.getName() == "InitialTargetVolume") {
      initialTargetVolume = BasicString::parseDouble(in.matchSimple());

    } else {
      throw BasicException(string("Unexpected element '") + in.getName() + 
            "'!", in.getLocation());
    }

    in.skip(TEXT);
  }

}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

