/*************************************************************************
 *    CompuCell - A software framework for multimodel simulations of     *
 * biocomplexity problems Copyright (C) 2003 University of Notre Dame,   *
 *                             Indiana                                   *
 *                                                                       *
 * This program is free software; IF YOU AGREE TO CITE USE OF CompuCell  *
 *  IN ALL RELATED RESEARCH PUBLICATIONS according to the terms of the   *
 *  CompuCell GNU General Public License RIDER you can redistribute it   *
 * and/or modify it under the terms of the GNU General Public License as *
 *  published by the Free Software Foundation; either version 2 of the   *
 *         License, or (at your option) any later version.               *
 *                                                                       *
 * This program is distributed in the hope that it will be useful, but   *
 *      WITHOUT ANY WARRANTY; without even the implied warranty of       *
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU    *
 *             General Public License for more details.                  *
 *                                                                       *
 *  You should have received a copy of the GNU General Public License    *
 *     along with this program; if not, write to the Free Software       *
 *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.        *
 *************************************************************************/

#include "MitosisPlugin.h"

#include <CompuCell3D/Simulator.h>
#include <CompuCell3D/Field3D/Field3D.h>
#include <CompuCell3D/Potts3D/Potts3D.h>
#include <CompuCell3D/Potts3D/Cell.h>
#include <CompuCell3D/Automaton/Automaton.h>
using namespace CompuCell3D;

#include <XMLCereal/XMLPullParser.h>
#include <XMLCereal/XMLSerializer.h>

#include <BasicUtils/BasicString.h>
#include <BasicUtils/BasicException.h>

#include <iostream>
using namespace std;

MitosisPlugin::MitosisPlugin() {}

MitosisPlugin::~MitosisPlugin() {}

void MitosisPlugin::init(Simulator *simulator) {

  potts = simulator->getPotts();


  simulator->getPotts()->registerCellGChangeWatcher(this);
  simulator->getPotts()->registerStepper(this);
  turnOn();
}


void MitosisPlugin::field3DChange(const Point3D &pt, CellG *newCell,
				  CellG *oldCell) {

  // Note: We cannot mitosis the cell here because  updating the field
  // from this function would cause further field3DChange calls which
  // would be out of order for any change listeners who  happened to
  // be after this one in the list.
  if (newCell){
      
      if(newCell->volume>= doublingVolume){
         split = true;
         splitPt = pt;
      }
  }

}

void MitosisPlugin::turnOn() {on = true;}
void MitosisPlugin::turnOff() {on = false;}

void MitosisPlugin::step() {
   bool didMitosis=doMitosis();
   if(didMitosis){
      updateAttributes();
   }
   
}

void MitosisPlugin::updateAttributes(){

   ///copying type and target volume of the parent cell to the new cell
   childCell->type = parentCell->type;
   childCell->targetVolume = parentCell->targetVolume;
}

bool MitosisPlugin::doMitosis(){
   
  bool didMitosis=false;
  if (split && on) {
    split = false;

    didMitosis=true;
    
    Field3D<CellG *> *cellFieldG = potts->getCellFieldG();

    CellG *cell = cellFieldG->get(splitPt);
    parentCell=cell;///parent cell ptr set
    
    if(!cell){
      return false; ///mitosis abbandoned if necessary wi will be done in the next MC step; this is protection against initial cell "shrinking"
    } 
    
    //ASSERT_OR_THROW("Cell should not be NULL at mitosis point!", cell);
    
    
    int volume = cell->volume;
    int newVol = 0;
    int targetVol = volume / 2;
    
    BasicArray<Point3D> *tmp;
    BasicArray<Point3D> *ary0 = &arrays[0];
    BasicArray<Point3D> *ary1 = &arrays[1];
    ary0->clear();
    ary1->clear();

    // Put the first point in the array
    ary0->put(splitPt);

    CellG *splitCell = 0;

    // Do a breadth first search for approximately half of the  cell's
    // volume.
    while (ary0->getSize() > 0 && newVol < targetVol) {
      unsigned int token = 0;
      double distance = 0;
      Point3D n;

      // Loop over all the points from the last round.
      for (unsigned int i = 0; i < ary0->getSize(); i++) {
   while (distance <= 1.5) {
     n = cellFieldG->getNeighbor(ary0->get(i), token, distance);

     // Add to split cell
     if (cellFieldG->get(n) == cell) {
       newVol++;
       ary1->put(n);

       if (splitCell){
         cellFieldG->set(n, splitCell);
       }else{
         splitCell = potts->createCellG(n);
         childCell=splitCell;///child cell ptr set

         unsigned char old_type,new_type;
         unsigned char old_typeG,new_typeG;

       }
         
       if (newVol >= targetVol) break;
     }
   }

   if (newVol >= targetVol) break;
      }

      // Swap arrays;
      tmp = ary0;
      ary0 = ary1;
      ary1 = tmp;
      ary1->clear();
    }
  }
  return didMitosis;
}
void MitosisPlugin::readXML(XMLPullParser &in) {
  in.skip(TEXT);

  while (in.check(START_ELEMENT)) {
    if (in.getName() == "DoublingVolume") {
      doublingVolume = BasicString::parseUInteger(in.matchSimple());
      ASSERT_OR_THROW("DoublingVolume must be > 1!", doublingVolume > 1);
      
    } else {
      THROW(string("Unexpected element '") + in.getName() + "'!");
    }
    
    in.skip(TEXT);
  }
}

void MitosisPlugin::writeXML(XMLSerializer &out) {
}



















