#include "DictyChemotaxisSteppable.h"
#include <CompuCell3D/Simulator.h>

#include <CompuCell3D/Field3D/Point3D.h>
#include <CompuCell3D/Automaton/Automaton.h>
#include <CompuCell3D/Potts3D/Potts3D.h>
#include <CompuCell3D/Potts3D/CellInventory.h>
#include <CompuCell3D/plugins/SimpleClock/SimpleClockPlugin.h>
#include <BasicUtils/BasicClassAccessor.h>
#include <CompuCell3D/DiffusableVector.h>
#include <CompuCell3D/Field3D/Field3D.h>

#include <CompuCell3D/ClassRegistry.h>
#include <CompuCell3D/Field3D/Field3D.h>
#include <XMLCereal/XMLPullParser.h>
#include <XMLCereal/XMLSerializer.h>


using namespace CompuCell3D;
using namespace std;

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
DictyChemotaxisSteppable::DictyChemotaxisSteppable():potts(0),cellInventoryPtr(0)
{
    clockReloadValue=0;
    chemotactUntil=0;
    chetmotaxisActivationThreshold=0.0;

}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void DictyChemotaxisSteppable::init(Simulator *_simulator){

   simulator=_simulator;
   potts = simulator->getPotts();
   cellFieldG = potts->getCellFieldG();
   
   cellInventoryPtr=& potts->getCellInventory();
   SimpleClockPlugin * simpleClockPlugin = (SimpleClockPlugin*)(Simulator::pluginManager.get("SimpleClock"));
   simpleClockAccessorPtr = simpleClockPlugin->getSimpleClockAccessorPtr();
   chemotactingCellsCounter=0;
   ignoreFirstSteps=0;
}

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


void DictyChemotaxisSteppable::extraInit(Simulator *_simulator){

   ClassRegistry *classRegistry=simulator->getClassRegistry();
   Steppable * steppable=classRegistry->getStepper(chemicalFieldSource);

   field=((DiffusableVector<float> *) steppable)->getConcentrationField(chemicalFieldName);
        
  ASSERT_OR_THROW("No chemical field has been loaded!", field);
  cerr<<"GOT FIELD INTO CHEMOTAXIS STEPPABLE: "<<field<<endl;

  fieldDim=field->getDim();

}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void DictyChemotaxisSteppable::start(){
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void DictyChemotaxisSteppable::step(const unsigned int currentStep){
   
   if(currentStep < ignoreFirstSteps)
      return;
   
   Point3D pt;
   CellG * currentCellPtr;
   int * currentClockPtr;
   char * clockFlagPtr;
   float currentConcentration;
   
   for (pt.z = 0; pt.z < fieldDim.z; ++pt.z)
      for (pt.y = 0; pt.y < fieldDim.y; ++pt.y)
         for (pt.x = 0; pt.x < fieldDim.x; ++pt.x){

            currentCellPtr=cellFieldG->get(pt);
            if(!currentCellPtr) continue;
            
            currentClockPtr = &(simpleClockAccessorPtr->get(currentCellPtr->extraAttribPtr)->clock);///to avoid costly too many costly accesses
            clockFlagPtr = &(simpleClockAccessorPtr->get(currentCellPtr->extraAttribPtr)->flag);
            currentConcentration=field->get(pt);
            
            if(currentConcentration > chetmotaxisActivationThreshold && *currentClockPtr==0){
               //simpleClockAccessorPtr->get(currentCellPtr->extraAttribPtr)->clock = clockReloadValue;
               *currentClockPtr=clockReloadValue; ///will reload the clock if any pixel belonging to a cel has concentration > activeThreshold
               *clockFlagPtr = 1;
               cerr<<endl<<endl;
               //cerr<<"\t\treloading clock and activating chemotaxis"<<endl;
               cerr<<endl<<endl;
               ++chemotactingCellsCounter;
            }
                                    
         }

   ///decrementing clock
   CellInventory::cellInventoryIterator cInvItr;
   for(cInvItr=cellInventoryPtr->cellInventoryBegin() ; cInvItr !=cellInventoryPtr->cellInventoryEnd() ;++cInvItr ){

      currentCellPtr=*cInvItr;
      currentClockPtr = &(simpleClockAccessorPtr->get(currentCellPtr->extraAttribPtr)->clock);
      clockFlagPtr = &simpleClockAccessorPtr->get(currentCellPtr->extraAttribPtr)->flag;
      
      if(*currentClockPtr>=1){
         --(*currentClockPtr);///decrementring clock but only until zero - non negative clock values allowed here
      }
      
      if( *currentClockPtr < chemotactUntil && *clockFlagPtr){
         *clockFlagPtr=0;
         //cerr<<"Deactivating chemotaxis"<<endl;
         --chemotactingCellsCounter;
      }

   }
   
//          cerr<<"\n\n \t\t THERE ARE "<<chemotactingCellsCounter<<" CELLS CHEMOTACTING\n"<<endl;
}

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

void DictyChemotaxisSteppable::readXML(XMLPullParser &in){

  in.skip(TEXT);
   bool gotChemotactUntil=false;
    
  while (in.check(START_ELEMENT)) {
   if (in.getName() == "ClockReloadValue") {
      clockReloadValue = BasicString::parseUInteger(in.matchSimple());

    }

    else if (in.getName() == "ChemotactUntil") {
      chemotactUntil = BasicString::parseUInteger(in.matchSimple());
    }
    
    else if (in.getName() == "ChetmotaxisActivationThreshold") {
      chetmotaxisActivationThreshold = BasicString::parseDouble(in.matchSimple());
    }

    else if (in.getName() == "IgnoreFirstSteps") {
      ignoreFirstSteps = BasicString::parseUInteger(in.matchSimple());
    }
                
    else if (in.getName() == "ChemicalField") {

      
      chemicalFieldSource = in.getAttribute("Source").value;
      chemicalFieldName = in.matchSimple();
         
     
    }
        
   else {
      throw BasicException(string("Unexpected element '") + in.getName() +
                           "'!", in.getLocation());
    }

    in.skip(TEXT);
  }

  ASSERT_OR_THROW("ChemotactUntil has to be smaller than Clock Reload Value!",chemotactUntil<clockReloadValue);
}
