/*************************************************************************
 *    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 "ClassRegistry.h"
#include "Simulator.h"
#include <CompuCell3D/MinimalClock.h>

#include <CompuCell3D/TargetVolumeSteppable.h>
#include <CompuCell3D/TargetVolumeSteppableBiofilm.h>
// #include <CompuCell3D/SBMLConverter.h>
//#include <CompuCell3D/ClockSteppable.h>
#include <CompuCell3D/CellVelocity.h>
// #include <CompuCell3D/CellInstantVelocity.h>
#include <CompuCell3D/UniformFieldInitializer.h>
#include <CompuCell3D/DictyFieldInitializer.h>
#include <CompuCell3D/DictyChemotaxisSteppable.h>
// #include <CompuCell3D/UniformFieldInitializerWithTracker.h>
#include <CompuCell3D/FlexibleDiffusionSolverFE.h>
#include <CompuCell3D/AdvectionDiffusionSolverFE.h>
#include <CompuCell3D/DiffusionSolverFE.h>
#include <CompuCell3D/ReactionDiffusionSolverFE_SavHog.h>
#include <CompuCell3D/ReactionDiffusionFile.h>
//#include <CompuCell3D/AdvectionDiffusionSolverFEInh.h>
#include <CompuCell3D/DiffusionSolverBiofilmFE.h>
//#include <CompuCell3D/DiffusionSolverAmoebaeFE.h>
#include <CompuCell3D/PIFInitializer.h>
#include <CompuCell3D/BlobFieldInitializer.h>
#include <CompuCell3D/AmoebaeFieldInitializer.h>
#include <CompuCell3D/BlobFieldInitializerBiofilm.h>
// #include <CompuCell3D/BlobFieldInitializerWithTracker.h>
#include <CompuCell3D/IO/OgleVizDumper.h>
#include <CompuCell3D/IO/TripleVizDumper.h>
#include <CompuCell3D/OutputData.h>
#include <CompuCell3D/Viz/DefaultFieldRenderer.h>
#include <CompuCell3D/Viz/AdditiveRenderer.h>

#include <CompuCell3D/Viz/FieldRenderer.h>
using namespace CompuCell3D;

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

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

#include <string>
using namespace std;

ClassRegistry::ClassRegistry(Simulator *simulator) : simulator(simulator) {

//    registerSteppable("SBMLConverter", new BasicClassFactory<Steppable,
//            SBMLConverter>);

   registerSteppable("MinimalClock", new BasicClassFactory<Steppable,
           MinimalClock>);

           
   registerSteppable("DiffusionSolverFE", new BasicClassFactory<Steppable,
           DiffusionSolverFE>);

   registerSteppable("FlexibleDiffusionSolverFE", new BasicClassFactory<Steppable,
           FlexibleDiffusionSolverFE>);

   registerSteppable("AdvectionDiffusionSolverFE", new BasicClassFactory<Steppable,
           AdvectionDiffusionSolverFE>);
           
           
   registerSteppable("ReactionDiffusionSolverFE_SavHog", new BasicClassFactory<Steppable,
           ReactionDiffusionSolverFE_SavHog>);
           
   registerSteppable("ReactionDiffusionFile", new BasicClassFactory<Steppable,
           ReactionDiffusionFile>);

//   registerSteppable("AdvectionDiffusionSolverFE", new BasicClassFactory<Steppable,
//           AdvectionDiffusionSolverFEInh>);
           
   registerSteppable("DiffusionSolverBiofilmFE", new BasicClassFactory<Steppable,
           DiffusionSolverBiofilmFE>);

/*   registerSteppable("DiffusionSolverAmoebaeFE", new BasicClassFactory<Steppable,
           DiffusionSolverAmoebaeFE>);*/
           
           
  registerSteppable("UniformInitializer", new BasicClassFactory<Steppable,
		    UniformFieldInitializer>);

                    
//   registerSteppable("UniformInitializerWithTracker", new BasicClassFactory<Steppable,
//                     UniformFieldInitializerWithTracker>);

  
  registerSteppable("DictyChemotaxisSteppable", new BasicClassFactory<Steppable,
          DictyChemotaxisSteppable>);

                        
  registerSteppable("DictyInitializer", new BasicClassFactory<Steppable,
          DictyFieldInitializer>);

          
  registerSteppable("PIFInitializer", new BasicClassFactory<Steppable,
                    PIFInitializer>);
  registerSteppable("BlobInitializer", new BasicClassFactory<Steppable,
                    BlobFieldInitializer>);

  registerSteppable("AmoebaeInitializer", new BasicClassFactory<Steppable,
                    AmoebaeFieldInitializer>);
                    
  registerSteppable("BlobInitializerBiofilm", new BasicClassFactory<Steppable,
                    BlobFieldInitializerBiofilm>);
                    
//   registerSteppable("Clock", new BasicClassFactory<Steppable,
//                     ClockSteppable>);

  registerSteppable("TargetVolume", new BasicClassFactory<Steppable,
                    TargetVolumeSteppable>);
                    
  registerSteppable("TargetVolumeBiofilm", new BasicClassFactory<Steppable,
                    TargetVolumeSteppableBiofilm>);
                                        
/*   registerSteppable("BlobInitializerWithTracker", new BasicClassFactory<Steppable,
                        BlobFieldInitializerWithTracker>);*/
                        
 registerSteppable("CellVelocity", new BasicClassFactory<Steppable,
                   CellVelocity>);

//  registerSteppable("CellInstantVelocity", new BasicClassFactory<Steppable,
//                    CellInstantVelocity>);

                                       
  registerSteppable("OgleDumper",
		    new BasicClassFactory<Steppable, OgleVizDumper>);
  registerSteppable("TripleDumper",
                    new BasicClassFactory<Steppable, TripleVizDumper>);
  registerSteppable("OutputData",
                    new BasicClassFactory<Steppable, OutputData>);
   registerRenderer("Default",
 		   new BasicClassFactory<FieldRenderer, DefaultFieldRenderer>);
   registerRenderer("Additive",
 		   new BasicClassFactory<FieldRenderer, AdditiveRenderer>);
}


FieldRenderer *ClassRegistry::getRenderer(string id) {
  BasicSmartPointer<FieldRenderer> renderer = activeRenderers[id];
  ASSERT_OR_THROW(string("Renderer '") + id + "' not found!", renderer.get());
  return renderer.get();
}

Steppable *ClassRegistry::getStepper(string id) {
  BasicSmartPointer<Steppable> stepper = activeSteppersMap[id];
  ASSERT_OR_THROW(string("Stepper '") + id + "' not found!", stepper.get());
  return stepper.get();
}

void ClassRegistry::extraInit(Simulator * simulator) {
  ActiveSteppers_t::iterator it;
  for (it = activeSteppers.begin(); it != activeSteppers.end(); it++)
    (*it)->extraInit(simulator);
}


void ClassRegistry::start() {
  ActiveSteppers_t::iterator it;
  for (it = activeSteppers.begin(); it != activeSteppers.end(); it++)
    (*it)->start();
}

void ClassRegistry::step(const unsigned int currentStep) {
  ActiveSteppers_t::iterator it;
  for (it = activeSteppers.begin(); it != activeSteppers.end(); it++) {
     if ((*it)->frequency && (currentStep % (*it)->frequency) == 0)
      (*it)->step(currentStep);
  }
}

void ClassRegistry::finish() {
  ActiveSteppers_t::iterator it;
  for (it = activeSteppers.begin(); it != activeSteppers.end(); it++)
    (*it)->finish();
}


void ClassRegistry::readXML(XMLPullParser &in) {
  in.skip(TEXT);
  
  while (in.check(START_ELEMENT)) {
    if (in.getName() == "FieldRenderer") {
      string type = in.getAttribute("Type").value;
      BasicSmartPointer<FieldRenderer> renderer =
	rendererRegistry.create(type);

      renderer->init(simulator);
      in.match(START_ELEMENT);
      renderer->readXML(in);
      in.match(END_ELEMENT);

      activeRenderers[type] = renderer;

    } else if (in.getName() == "Steppable") {
      string type = in.getAttribute("Type").value;
      BasicSmartPointer<Steppable> steppable = steppableRegistry.create(type);

      if (in.findAttribute("Frequency") != -1)
	steppable->frequency =
	  BasicString::parseUInteger(in.getAttribute("Frequency").value);

      steppable->init(simulator);
      in.match(START_ELEMENT);
      steppable->readXML(in);
      in.match(END_ELEMENT);

      activeSteppers.push_back(steppable);
      activeSteppersMap[type] = steppable;

    } else {
      THROW(string("Unexpected element '") + in.getName() + "'!");
    }

    in.skip(TEXT);
  }
}

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