#include "ReactionDiffusionFile.h"

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



#include <XMLCereal/XMLPullParser.h>
#include <XMLCereal/XMLSerializer.h>
#include <BasicUtils/BasicString.h>
#include <BasicUtils/BasicException.h>
#include <BasicUtils/BasicRandomNumberGenerator.h>
#include <string>
#include <cmath>
#include <iostream>
#include <netinet/in.h>

using namespace CompuCell3D;
using namespace std;
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
ReactionDiffusionFile::ReactionDiffusionFile()
{}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
ReactionDiffusionFile::~ReactionDiffusionFile()
{}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ReactionDiffusionFile::init(Simulator *simulator) {
   simPtr = simulator;
   ///getting field ptr from Potts3D
   fieldDim=simPtr->getPotts()->getCellFieldG()->getDim();
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ReactionDiffusionFile::start() {
    ifstream stream(fileName.c_str());
   
    char typeStr[3];
    stream.read(typeStr, 2);
    typeStr[2] = '\0';

    char dims = 0;
    uint32_t x = 0;
    uint32_t y = 0;
    uint32_t z = 0;
    stream.read(&dims, 1);
    stream.read((char *)&x, sizeof(uint32_t));
    stream.read((char *)&y, sizeof(uint32_t));
    stream.read((char *)&z, sizeof(uint32_t));
    Dim3D readDim(ntohl(x), ntohl(y), ntohl(z));
    
    bool lByteOrder = false;
    bool sByteOrder = false;
    if (typeStr[1] == 'l' || typeStr[1] == 'i') lByteOrder = true;
    if (typeStr[1] == 's') sByteOrder = true;
    uint32_t l;
    uint16_t s;

    float c;
    Point3D pt;
    cout << "READING";
    for (pt.z = 0; pt.z < readDim.z; pt.z++)
      for (pt.y = 0; pt.y < readDim.y; pt.y++)
	for (pt.x = 0; pt.x < readDim.x; pt.x++) {
	  cout << ".";
          ASSERT_OR_THROW("Field3D<T> Error reading file!",
			  !stream.eof());

	  if (lByteOrder) {
	    stream.read((char *)&l, sizeof(float));
	    c = (float)ntohl(l);

	  } else if (sByteOrder) {
	    stream.read((char *)&s, sizeof(float));
	    c = (float)ntohs(s);

	  } else {
	    stream.read((char *)&c, sizeof(float));
	  }
          concentrationFieldVector[0]->set(pt, c);
        }
    cout << "DONE." << endl;
    cout << endl;
   stream.close();
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ReactionDiffusionFile::step(const unsigned int _currentStep) {}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ReactionDiffusionFile::readXML(XMLPullParser &in) {
  cout << "RDF:readXML" << endl;
  in.skip(TEXT);
  string concentrationFieldName;
  unsigned int concentrationFieldCounter=0;

    
  while (in.check(START_ELEMENT)) {
    if (in.getName() == "FileName") {
      fileName = in.matchSimple();
    }
    else if (in.getName() == "NumberOfFields") {
       numberOfFields = BasicString::parseUInteger(in.matchSimple());
       cout << "POTTSDIM: " << simPtr->getPotts()->getCellFieldG()->getDim() << endl;
       allocateDiffusableFieldVector(3, simPtr->getPotts()->getCellFieldG()->getDim());/// c,r, scratch fields
    }
    else if (in.getName() == "FieldName") {
      concentrationFieldName = in.matchSimple();
      concentrationFieldNameVector[concentrationFieldCounter] = concentrationFieldName;
      ///register concentration field to make it accesible from external viewers etc.

      ASSERT_OR_THROW("You are trying to define more field that you declared!",concentrationFieldCounter<=numberOfFields);
      simPtr->registerConcentrationField(concentrationFieldName , concentrationFieldVector[concentrationFieldCounter]);
      ++concentrationFieldCounter;
    }
    
     else {
      throw BasicException(string("Unexpected element '") + in.getName() + 
            "'!", in.getLocation());
    }

    in.skip(TEXT);
  }
  cout << "End RDF:readXML" << endl;
}

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