#include "MinimalClock.h"

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



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

#include <iostream>

using namespace CompuCell3D;
using namespace std;
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
MinimalClock::MinimalClock():Steppable()
{
   xSize=ySize=0;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
MinimalClock::~MinimalClock()
{
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void MinimalClock::init(Simulator *simulator) {
  simPtr=simulator;
  potts = simulator->getPotts();
  automaton=potts->getAutomaton();
   
  ///getting cell inventory
  cellInventoryPtr=& potts->getCellInventory(); 
    
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void MinimalClock::start() {
  
  //detecting size of 2D cell array
  set<unsigned int> xCM_coords,yCM_coords;
  
  CellInventory::cellInventoryIterator cInvItr;
  ///loop over all the cells in the inventory
  Point3D com;
  CellG * cell;

   
   ASSERT_OR_THROW("No cells defined. Make sure that call to Minimal Clock Steppable appears AFTER call to cell field initializer",
   cellInventoryPtr->getCellInventorySize()
   );
   //detecting size of 2D cell array
   for(cInvItr=cellInventoryPtr->cellInventoryBegin() ; cInvItr !=cellInventoryPtr->cellInventoryEnd() ;++cInvItr ){
      
      cell=*cInvItr;
      xCM_coords.insert(cell->xCM);
      yCM_coords.insert(cell->yCM);
   }
   
   xSize=xCM_coords.size();
   ySize=yCM_coords.size();
   //cerr<<"xSize="<<xSize<<" ySize="<<ySize<<endl;
   
   ASSERT_OR_THROW("PIF FILE DOES NOT DEFINE RECTANGULAR ARRAY OF CELLS",
      xSize*ySize==cellInventoryPtr->getCellInventorySize());

   

  vector<unsigned int> xCMSortedVec(xCM_coords.begin(),xCM_coords.end());
  vector<unsigned int> yCMSortedVec(yCM_coords.begin(),yCM_coords.end());
  
  //allocating 2D arrays
  cell2DArray.assign(xCM_coords.size(),vector<CellG*>(yCM_coords.size(),0));
  state2DArray0.assign(xCM_coords.size(),vector<bool>(yCM_coords.size(),0));
  state2DArray1.assign(xCM_coords.size(),vector<bool>(yCM_coords.size(),0));

  unsigned int x=0,y=0;
  //filling arrays
  for(cInvItr=cellInventoryPtr->cellInventoryBegin() ; cInvItr !=cellInventoryPtr->cellInventoryEnd() ;++cInvItr ){
      
   cell=*cInvItr;
   x=position(xCMSortedVec,cell->xCM);
   y=position(yCMSortedVec,cell->yCM);
   cell2DArray[x][y]=cell;
   state2DArray0[x][y]=(cell->type==automaton->getTypeId("On") ? true : false);
   state2DArray1[x][y]=false;
  }
  
  
   
}

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

//A,B,C corresponfd to indeces x=0,1,2
// A(t+1) = NOT B(t)
// 
// B(t+1) = A(t) AND C(t)
// 
// C(t+1) = A(t) OR NOT B(t)

//old single cell imlementation 
//    for(unsigned int y = 0 ; y <ySize ; ++y){
//    
//       state2DArray1[0][y] = !(state2DArray0[1][y]);
//       state2DArray1[1][y] = state2DArray0[0][y] && state2DArray0[2][y];
//       state2DArray1[2][y] = state2DArray0[0][y] || !state2DArray0[1][y];
//       
//    }
//    //assigning new types to cells
//    for(unsigned int x = 0 ; x <xSize ; ++x)
//       for(unsigned int y = 0 ; y <ySize ; ++y){
//          cell2DArray[x][y]->type=(state2DArray1[x][y]?automaton->getTypeId("On") : automaton->getTypeId("Off") );
//          cerr<<"gene["<<x<<"]="<<(int)cell2DArray[x][y]->type<<endl;
//       }
//    
//    //assign back state2DArray1 to state2DArray0 so it is ready for the next step
//    sleep(1);
//    state2DArray0=state2DArray1;


   for(unsigned int y = 0 ; y <ySize ; ++y){

      if(y<ySize-1){
         state2DArray1[0][y] = !(state2DArray0[1][y]);
         state2DArray1[1][y] = state2DArray0[0][y] && state2DArray0[2][y];
         state2DArray1[2][y] = state2DArray0[0][y] || !state2DArray0[1][y];
      }else{
         //last element of the chain is matched fit first
         state2DArray1[0][y] = !(state2DArray0[1][y]);
         state2DArray1[1][y] = state2DArray0[0][y] && state2DArray0[2][y];
         state2DArray1[2][y] = state2DArray0[0][y] || !state2DArray0[1][y];
         
      }

   }
   //assigning new types to cells
   for(unsigned int x = 0 ; x <xSize ; ++x)
      for(unsigned int y = 0 ; y <ySize ; ++y){
         cell2DArray[x][y]->type=(state2DArray1[x][y]?automaton->getTypeId("On") : automaton->getTypeId("Off") );
         cerr<<"gene["<<x<<"]="<<(int)cell2DArray[x][y]->type<<endl;
      }

   //assign back state2DArray1 to state2DArray0 so it is ready for the next step
   state2DArray0=state2DArray1;


}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
unsigned int MinimalClock::position(std::vector<unsigned int> & cmVec, unsigned int cmPos){

   for(unsigned int i = 0 ; i < cmVec.size() ; ++i){
      if(cmVec[i]==cmPos)
         return i;
   }

   ASSERT_OR_THROW("Did not find center of mass position in the vector",false);

}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void MinimalClock::readXML(XMLPullParser &in) {
/*  in.skip(TEXT);
  while (in.check(START_ELEMENT)) {
    if (in.getName() == "DiffusionConstant") {
      diffConst = BasicString::parseDouble(in.matchSimple());

    }
    else if (in.getName() == "SecretionConstant") {
      secrConst = BasicString::parseDouble(in.matchSimple());

    }
    else if (in.getName() == "DecayConstant") {
      decayConst = BasicString::parseDouble(in.matchSimple());

    }
    else if (in.getName() == "FieldName") {
      concentrationFieldName = in.matchSimple();
      ///register concentration field to make it accesible from external viewers etc.
      simPtr->registerConcentrationField(concentrationFieldName , concentrationField );
    }
    
     else {
      throw BasicException(string("Unexpected element '") + in.getName() + 
            "'!", in.getLocation());
    }

    in.skip(TEXT);
  }
*/
}

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

