/*************************************************************************
 *    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 "PIFInitializer.h"
#include "Automaton/Automaton.h"
#include <CompuCell3D/Simulator.h>
#include <CompuCell3D/Potts3D/Cell.h>
#include <CompuCell3D/Potts3D/Potts3D.h>
#include <CompuCell3D/Field3D/Point3D.h>
#include <CompuCell3D/Field3D/Dim3D.h>
using namespace CompuCell3D;

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

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

#include <string>
#include <map>
using namespace std;


PIFInitializer::PIFInitializer() :
  potts(0), pifname("") {}

PIFInitializer::PIFInitializer(string filename) :
  potts(0), pifname(filename) {}

void PIFInitializer::init(Simulator *simulator) {
  potts = simulator->getPotts();
}

void PIFInitializer::start() {

  std::ifstream pif(pifname.c_str(), ios::in);
  ASSERT_OR_THROW(string("Could not open\n"+pifname+"\nMake sure it exists and is in correct directory"),pif.good());
  Field3D<CellG *> *cellFieldG = potts->getCellFieldG();
  ASSERT_OR_THROW("initField() Cell field cannot be null!", cellFieldG);

  Dim3D dim = cellFieldG->getDim();

  int spin;
  std::string celltype;
  int xLow, xHigh, yLow, yHigh, zLow, zHigh;
  std::map<int, Point3D> spinMap; // Used to check if a cell of the same spin is
                             // listed twice.
  
  Point3D cellPt;
  CellG* cell;

  
  while (!pif.eof())
  {

     pif >> spin;
     
     pif >> celltype;

     pif >> xLow;

     ASSERT_OR_THROW("PIF reader: xLow out of bounds", xLow >= 0 && xLow < dim.x);
     pif >> xHigh;
     ASSERT_OR_THROW("PIF reader: xHigh out of bounds.", xHigh >= 0 && xHigh < dim.x);
     ASSERT_OR_THROW("PIF reader: xHigh is smaller than xLow.", xHigh >= xLow); 
     pif >> yLow;
     ASSERT_OR_THROW("PIF reader: yLow out of bounds.", yLow >= 0 && yLow < dim.y);
     pif >> yHigh;   
     ASSERT_OR_THROW("PIF reader: yHigh out of bounds.", yHigh >= 0 && yHigh < dim.y);
     ASSERT_OR_THROW("PIF reader: yHigh is smaller than yLow.", yHigh >= yLow);
     pif >> zLow;
     ASSERT_OR_THROW("PIF reader: zLow out of bounds.", zLow >= 0 && zLow < dim.z);
     pif >> zHigh;
     ASSERT_OR_THROW("PIF reader: zHigh out of bounds.", zHigh >= 0 && zHigh < dim.z);
     ASSERT_OR_THROW("PIF reader: zHigh is smaller than xLow.", zHigh >= zLow);
       

     if (spinMap.count(spin) != 0) // Spin multiply listed
     {
        for (cellPt.z = zLow; cellPt.z <= zHigh; cellPt.z++)
           for (cellPt.y = yLow; cellPt.y <= yHigh; cellPt.y++)
               for (cellPt.x = xLow; cellPt.x <= xHigh; cellPt.x++)
                   cellFieldG->set(cellPt, cellFieldG->get(spinMap[spin]));
     }
     else // First time for this spin, we need to create a new cell
     {
        spinMap[spin] = Point3D(xLow, yLow, zLow);
        cell = potts->createCellG(Point3D(xLow, yLow, zLow));
        
        for (cellPt.z = zLow; cellPt.z <= zHigh; cellPt.z++)
            for (cellPt.y = yLow; cellPt.y <= yHigh; cellPt.y++)
                for (cellPt.x = xLow; cellPt.x <= xHigh; cellPt.x++)
                    cellFieldG->set(cellPt, cell);
                    
        cell->type=potts->getAutomaton()->getTypeId(celltype);
        
     }
  }
  
} 

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

  while (in.check(START_ELEMENT)) {
    if (in.getName() == "PIFName") {
      pifname = in.matchSimple();
    } 
    else {
      throw BasicException(string("Unexpected element '") + in.getName() + 
			   "'!", in.getLocation());
    }

    in.skip(TEXT);
  }
}

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