/*************************************************************************
 *    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 "BlobFieldInitializerBiofilm.h"
#include <CompuCell3D/Potts3D/CellInventory.h>

// #include <CompuCell3D/plugins/Concentration/ConcentrationPlugin.h>
// #include <CompuCell3D/Simulator.h>

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

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

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

#include <BasicUtils/BasicClassGroup.h>

#include <string>

#include <math.h>

#include <iostream>
using namespace std;


BlobFieldInitializerBiofilm::BlobFieldInitializerBiofilm() : BlobFieldInitializer()
  {}

BlobFieldInitializerBiofilm::BlobFieldInitializerBiofilm(int gap, int width) : BlobFieldInitializer(gap,width)
{
}

void BlobFieldInitializerBiofilm::initializeCellTypes(){

   
   
   cellInventoryPtr=& potts->getCellInventory();
   
   ///initialize concentration accessor
/*  ConcentrationPlugin * concentrationPlugin=(ConcentrationPlugin*)(Simulator::pluginManager.get("Concentration"));
  BasicClassAccessor<Concentration> * concentrationAccessorPtr=concentrationPlugin->getConcentrationAccessorPtr();*/
  

   ///will initialize cell type here depending on the position of the cells
   CellInventory::cellInventoryIterator cInvItr;
   ///loop over all the cells in the inventory
   CellG * cell;

   for(cInvItr=cellInventoryPtr->cellInventoryBegin() ; cInvItr !=cellInventoryPtr->cellInventoryEnd() ;++cInvItr ){

      cell=*cInvItr;
      cell->type=1;
      
      //cerr<<"this is an initial concnentration "<<concentrationAccessorPtr->get(cell->extraAttribPtr)->concentration<<endl;

   }

}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
float BlobFieldInitializerBiofilm::distance(Point3D a, Point3D b) const{
  
  return sqrt((float)(a.x - b.x) * (a.x - b.x) +
              (float)(a.y - b.y) * (a.y - b.y) +
              (float)(a.z - b.z) * (a.z - b.z));
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void BlobFieldInitializerBiofilm::createCellField(){
  // TODO: Chage this code so it write the 0 spins too.  This will make it
  //       possible to re-initialize a previously used field.
  
  /// I am changing here so that now I will work with cellFieldG - the field of CellG
  /// - this way CompuCell will have more functionality

  
  int size = gap + width;

  Field3D<CellG *> *cellFieldG = potts->getCellFieldG();
  ASSERT_OR_THROW("initField() Cell field G cannot be null!", cellFieldG);
  
  
   
  
  Dim3D dim = cellFieldG->getDim();
  Dim3D itDim=getBlobDimensions(dim,size);
  
  ASSERT_OR_THROW("All lattice adges must be equal", dim.x==dim.y);
  ASSERT_OR_THROW("All lattice adges must be equal", dim.x==dim.z);
  ASSERT_OR_THROW("All lattice adges must be equal", dim.y==dim.z);
   
  ASSERT_OR_THROW("2*Radius is bigger that 0.9*field_size ", 2*radius<0.9*dim.x);
   
//   itDim.x = dim.x / size;
//   if (dim.x % size) itDim.x += 1;
//   itDim.y = dim.y / size;
//   if (dim.y % size) itDim.y += 1;
//   itDim.z = dim.z / size;
//   if (dim.z % size) itDim.z += 1;

  Point3D pt;
  Point3D cellPt;
  Point3D center;
  CellG *cell;
  
   
  center.x = dim.x / 2;
  center.y = dim.y / 2;
  center.z = dim.z / 2;
  
  

  for (int z = 0; z < dim.z; z++)
    for (int y = 0; y < dim.y; y++)
      for (int x = 0; x < dim.x; x++) {
          pt.x = x * size;
          pt.y = y * size;
          pt.z = z * size;
   
        if(distance(pt,center) < radius) {
   
   ///cout<<"********************CREATE CELL***********************"<<endl;     
   
          cell = potts->createCellG(pt);
   
          int counter=0;
          for (cellPt.z = pt.z; cellPt.z < pt.z + width &&
                 cellPt.z < dim.z; cellPt.z++)
            for (cellPt.y = pt.y; cellPt.y < pt.y + width &&
                   cellPt.y < dim.y; cellPt.y++)
              for (cellPt.x = pt.x; cellPt.x < pt.x + width &&
                     cellPt.x < dim.x; cellPt.x++){
                     //cellFieldG->set(cellPt, cell); //assigns cell to field
                     
                      cellFieldG->set(cellPt, cell); //assigns cell to field
                   }
                   //cout<<"COUNTER="<<counter<<endl;
        }      
      }
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void BlobFieldInitializerBiofilm::start() {
   cerr<<"INITIALIZE CELL FIELD"<<endl;
   //BlobFieldInitializer::start();
   createCellField();
   initializeCellTypes();
      
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void BlobFieldInitializerBiofilm::readXML(XMLPullParser &in) {
  in.skip(TEXT);

  while (in.check(START_ELEMENT)) {
    if (in.getName() == "Gap") {
      gap = BasicString::parseUInteger(in.matchSimple());

    }
    else if (in.getName() == "Width") {
      width = BasicString::parseUInteger(in.matchSimple());

    }
    else if (in.getName() == "Radius") {
      int tmpRadius = BasicString::parseUInteger(in.matchSimple());
      radius=(unsigned int)sqrt((double)(tmpRadius*tmpRadius)); ///just in case someone puts negative radius
//       cerr<<"THIS IS RADIUS: "<<radius<<endl;
//       exit(0);

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

    in.skip(TEXT);
  }
}
