
#include "TumorSimulationParametersXMLFile.h"

#include "muFile.h"

#include "itksys/SystemTools.hxx"

#include <fstream>
#include <sstream>
#include <string>

#include <stdlib.h>

TumorSimulationParametersXMLFileReader
::TumorSimulationParametersXMLFileReader()
{
  m_CurrentString = "";
  m_PObject = 0;
}

TumorSimulationParametersXMLFileReader
::~TumorSimulationParametersXMLFileReader()
{

}

int
TumorSimulationParametersXMLFileReader
::CanReadFile(const char* name)
{
  if(!itksys::SystemTools::FileExists(name) ||
     itksys::SystemTools::FileIsDirectory(name) ||
     itksys::SystemTools::FileLength(name) == 0)
    return 0;
  return 1;
}

void
TumorSimulationParametersXMLFileReader
::StartElement(const char* name, const char** atts)
{
  m_CurrentString = "";

  if(itksys::SystemTools::Strucmp(name,"tumor-simulation-parameters") == 0)
  {
    m_PObject = TumorSimulationParameters::New();
  }

  if(itksys::SystemTools::Strucmp(name,"disable-qhull") == 0)
  {
    if (!m_PObject.IsNull())
      m_PObject->UseQHullOff();
  }
}

void
TumorSimulationParametersXMLFileReader
::EndElement(const char* name)
{
  if(itksys::SystemTools::Strucmp(name,"tumor-simulation-parameters") == 0)
  {
    m_OutputObject = &(*m_PObject);
  }
  else if(itksys::SystemTools::Strucmp(name,"dataset-name") == 0)
  {
    m_PObject->SetDatasetName(m_CurrentString);
  }
  else if(itksys::SystemTools::Strucmp(name,"input-directory") == 0)
  {
    m_PObject->SetInputDirectory(m_CurrentString);
  }
  else if(itksys::SystemTools::Strucmp(name,"output-directory") == 0)
  {
    m_PObject->SetOutputDirectory(m_CurrentString);
  }
  else if(itksys::SystemTools::Strucmp(name,"deformation-seed") == 0)
  {
    m_PObject->SetDeformationSeedFileName(m_CurrentString);
  }
  else if(itksys::SystemTools::Strucmp(name,"contrast-enhancement-type") == 0)
  {
    m_PObject->SetContrastEnhancementType(m_CurrentString);
  }
  else if(itksys::SystemTools::Strucmp(name,"deformation-iterations") == 0)
  {
    long iter = atol(m_CurrentString.c_str());
    m_PObject->SetDeformationIterations(iter);
  }
  else if(itksys::SystemTools::Strucmp(name,"infiltration-iterations") == 0)
  {
    long iter = atol(m_CurrentString.c_str());
    m_PObject->SetInfiltrationIterations(iter);
  }
  else if(itksys::SystemTools::Strucmp(name,"infiltration-body-force-iterations") == 0)
  {
    long iter = atol(m_CurrentString.c_str());
    m_PObject->SetInfiltrationBodyForceIterations(iter);
  }
  else if(itksys::SystemTools::Strucmp(name,"deformation-initial-pressure") == 0)
  {
    double d = atof(m_CurrentString.c_str());
    m_PObject->SetDeformationInitialPressure(d);
  }
  else if(itksys::SystemTools::Strucmp(name,"deformation-kappa") == 0)
  {
    double d = atof(m_CurrentString.c_str());
    m_PObject->SetDeformationKappa(d);
  }
  else if(itksys::SystemTools::Strucmp(name,"deformation-damping") == 0)
  {
    double d = atof(m_CurrentString.c_str());
    m_PObject->SetDeformationDamping(d);
  }
  else if(itksys::SystemTools::Strucmp(name,"infiltration-time-step") == 0)
  {
    double d = atof(m_CurrentString.c_str());
    m_PObject->SetInfiltrationTimeStep(d);
  }
  else if(itksys::SystemTools::Strucmp(name,"infiltration-early-time") == 0)
  {
    double d = atof(m_CurrentString.c_str());
    m_PObject->SetInfiltrationEarlyTime(d);
  }
  else if(itksys::SystemTools::Strucmp(name,"infiltration-body-force-coefficient") == 0)
  {
    double d = atof(m_CurrentString.c_str());
    m_PObject->SetInfiltrationBodyForceCoefficient(d);
  }
  else if(itksys::SystemTools::Strucmp(name,"infiltration-body-force-damping") == 0)
  {
    double d = atof(m_CurrentString.c_str());
    m_PObject->SetInfiltrationBodyForceDamping(d);
  }
  else if(itksys::SystemTools::Strucmp(name,"brain-young-modulus") == 0)
  {
    double d = atof(m_CurrentString.c_str());
    m_PObject->SetBrainYoungModulus(d);
  }
  else if(itksys::SystemTools::Strucmp(name,"brain-poisson-ratio") == 0)
  {
    double d = atof(m_CurrentString.c_str());
    m_PObject->SetBrainPoissonRatio(d);
  }
  else if(itksys::SystemTools::Strucmp(name,"falx-young-modulus") == 0)
  {
    double d = atof(m_CurrentString.c_str());
    m_PObject->SetFalxYoungModulus(d);
  }
  else if(itksys::SystemTools::Strucmp(name,"falx-poisson-ratio") == 0)
  {
    double d = atof(m_CurrentString.c_str());
    m_PObject->SetFalxPoissonRatio(d);
  }
  else if(itksys::SystemTools::Strucmp(name,"infiltration-reaction-coefficient") == 0)
  {
    double d = atof(m_CurrentString.c_str());
    m_PObject->SetInfiltrationReactionCoefficient(d);
  }
  else if(itksys::SystemTools::Strucmp(name,"white-matter-tensor-multiplier") == 0)
  {
    double d = atof(m_CurrentString.c_str());
    m_PObject->SetWhiteMatterTensorMultiplier(d);
  }
  else if(itksys::SystemTools::Strucmp(name,"gray-matter-tensor-multiplier") == 0)
  {
    double d = atof(m_CurrentString.c_str());
    m_PObject->SetGrayMatterTensorMultiplier(d);
  }
  else if(itksys::SystemTools::Strucmp(name,"gad-noise-stddev") == 0)
  {
    double d = atof(m_CurrentString.c_str());
    m_PObject->SetGadNoiseStddev(d);
  }
  else if(itksys::SystemTools::Strucmp(name,"t1-noise-stddev") == 0)
  {
    double d = atof(m_CurrentString.c_str());
    m_PObject->SetT1NoiseStddev(d);
  }
  else if(itksys::SystemTools::Strucmp(name,"t2-noise-stddev") == 0)
  {
    double d = atof(m_CurrentString.c_str());
    m_PObject->SetT2NoiseStddev(d);
  }
  else if(itksys::SystemTools::Strucmp(name,"flair-noise-stddev") == 0)
  {
    double d = atof(m_CurrentString.c_str());
    m_PObject->SetFLAIRNoiseStddev(d);
  }
  else if(itksys::SystemTools::Strucmp(name,"gad-max-bias-degree") == 0)
  {
    long deg = atol(m_CurrentString.c_str());
    m_PObject->SetGadMaxBiasDegree(deg);
  }
  else if(itksys::SystemTools::Strucmp(name,"t1-max-bias-degree") == 0)
  {
    long deg = atol(m_CurrentString.c_str());
    m_PObject->SetT1MaxBiasDegree(deg);
  }
  else if(itksys::SystemTools::Strucmp(name,"t2-max-bias-degree") == 0)
  {
    long deg = atol(m_CurrentString.c_str());
    m_PObject->SetT2MaxBiasDegree(deg);
  }
  else if(itksys::SystemTools::Strucmp(name,"flair-max-bias-degree") == 0)
  {
    long deg = atol(m_CurrentString.c_str());
    m_PObject->SetFLAIRMaxBiasDegree(deg);
  }
  else if(itksys::SystemTools::Strucmp(name,"disable-background") == 0)
  {
    unsigned int i = atoi(m_CurrentString.c_str());
    m_PObject->SetDrawBackground(i == 0);
  }
  else if(itksys::SystemTools::Strucmp(name,"deformation-solver-iterations") == 0)
  {
    long iter = atol(m_CurrentString.c_str());
    m_PObject->SetDeformationSolverIterations(iter);
  }
  else if(itksys::SystemTools::Strucmp(name,"infiltration-solver-iterations") == 0)
  {
    long iter = atol(m_CurrentString.c_str());
    m_PObject->SetInfiltrationSolverIterations(iter);
  }
  else if(itksys::SystemTools::Strucmp(name,"number-of-threads") == 0)
  {
    long n = atol(m_CurrentString.c_str());
    m_PObject->SetNumberOfThreads(n);
  }
}

void
TumorSimulationParametersXMLFileReader
::CharacterDataHandler(const char* inData, int inLength)
{
  for (int i = 0; i < inLength; i++)
    m_CurrentString += inData[i];
}

TumorSimulationParametersXMLFileWriter
::TumorSimulationParametersXMLFileWriter()
{

}

TumorSimulationParametersXMLFileWriter
::~TumorSimulationParametersXMLFileWriter()
{

}

int
TumorSimulationParametersXMLFileWriter
::CanWriteFile(const char* name)
{
  return true;
}

// Support function
template <typename T>
static void
WriteField(TumorSimulationParametersXMLFileWriter* writer, const char* attname,
  T value, std::ofstream& output)
{
  writer->WriteStartElement(attname, output);
  output << value;
  writer->WriteEndElement(attname, output);
  output << std::endl;
}

int
TumorSimulationParametersXMLFileWriter
::WriteFile()
{
  if (m_InputObject == 0)
    itkExceptionMacro(<< "No object to write");

  if (!m_InputObject->CheckValues())
    itkExceptionMacro(<< "Invalid values");

  if (m_Filename.length() == 0)
    itkExceptionMacro(<< "No file name specified");

  std::ofstream output(m_Filename.c_str());
  if (output.fail())
    itkExceptionMacro(<< "Can not open " << m_Filename);

  // Header
  WriteStartElement("?xml version=\"1.0\"?",output);
  output << std::endl;
  //WriteStartElement("!doctype tumor-simulation-parameters",output);
  //output << std::endl;

  WriteStartElement("tumor-simulation-parameters", output);
  output << std::endl;

  TumorSimulationParameters::Pointer p = m_InputObject;

  WriteField<std::string>(this, "dataset-name", p->GetDatasetName(), output);

  WriteField<std::string>(this, "input-directory", p->GetInputDirectory(), output);
  WriteField<std::string>(this, "output-directory", p->GetOutputDirectory(), output);
  WriteField<std::string>(this, "deformation-seed", p->GetDeformationSeedFileName(), output);

  WriteField<long>(this, "deformation-iterations", p->GetDeformationIterations(), output);
  WriteField<long>(this, "infiltration-iterations", p->GetInfiltrationIterations(), output);
  WriteField<long>(this, "infiltration-body-force-iterations", p->GetInfiltrationBodyForceIterations(), output);

  WriteField<double>(this, "deformation-initial-pressure", p->GetDeformationInitialPressure(), output);
  WriteField<double>(this, "deformation-kappa", p->GetDeformationKappa(), output);
  WriteField<double>(this, "deformation-damping", p->GetDeformationDamping(), output);
  WriteField<double>(this, "infiltration-time-step", p->GetInfiltrationTimeStep(), output);
  WriteField<double>(this, "infiltration-early-time", p->GetInfiltrationEarlyTime(), output);
  WriteField<double>(this, "infiltration-body-force-coefficient", p->GetInfiltrationBodyForceCoefficient(), output);
  WriteField<double>(this, "infiltration-body-force-damping", p->GetInfiltrationBodyForceDamping(), output);

  WriteField<std::string>(this, "contrast-enhancement-type", p->GetContrastEnhancementType(), output);

  if (!p->GetUseQHull())
    WriteField<std::string>(this, "disable-qhull", "", output);

  WriteField<double>(this, "brain-young-modulus", p->GetBrainYoungModulus(), output);
  WriteField<double>(this, "brain-poisson-ratio", p->GetBrainPoissonRatio(), output);
  WriteField<double>(this, "falx-young-modulus", p->GetFalxYoungModulus(), output);
  WriteField<double>(this, "falx-poisson-ratio", p->GetFalxPoissonRatio(), output);

  WriteField<double>(this, "infiltration-reaction-coefficient", p->GetInfiltrationReactionCoefficient(), output);
  WriteField<double>(this, "white-matter-tensor-multiplier", p->GetWhiteMatterTensorMultiplier(), output);
  WriteField<double>(this, "gray-matter-tensor-multiplier", p->GetGrayMatterTensorMultiplier(), output);

  WriteField<double>(this, "gad-noise-stddev", p->GetGadNoiseStddev(), output);
  WriteField<double>(this, "t1-noise-stddev", p->GetT1NoiseStddev(), output);
  WriteField<double>(this, "t2-noise-stddev", p->GetT2NoiseStddev(), output);
  WriteField<double>(this, "flair-noise-stddev", p->GetFLAIRNoiseStddev(), output);

  WriteField<unsigned int>(this, "gad-max-bias-degree", p->GetGadMaxBiasDegree(), output);
  WriteField<unsigned int>(this, "t1-max-bias-degree", p->GetT1MaxBiasDegree(), output);
  WriteField<unsigned int>(this, "t2-max-bias-degree", p->GetT2MaxBiasDegree(), output);
  WriteField<unsigned int>(this, "flair-max-bias-degree", p->GetFLAIRMaxBiasDegree(), output);

  WriteField<unsigned int>(this, "disable-background", !p->GetDrawBackground(), output);

  WriteField<long>(this, "deformation-solver-iterations", p->GetDeformationSolverIterations(), output);
  WriteField<long>(this, "infiltration-solver-iterations", p->GetInfiltrationSolverIterations(), output);

  WriteField<long>(this, "number-of-threads", p->GetNumberOfThreads(), output);

  // Finish
  WriteEndElement("tumor-simulation-parameters", output);
  output << std::endl;
  output.close();

  return 0;
}

// Definition of some convenience functions
TumorSimulationParameters::Pointer
readTumorSimulationParametersXML(const char* fn)
{
  TumorSimulationParametersXMLFileReader::Pointer reader =
    TumorSimulationParametersXMLFileReader::New();
/*
  try
  {
    reader->SetFilename(fn);
    reader->GenerateOutputInformation();
  }
  catch (...)
  {
    return 0;
  }
*/
  reader->SetFilename(fn);
  reader->GenerateOutputInformation();
  return reader->GetOutputObject();
}

bool
writeTumorSimulationParametersXML(const char* fn, TumorSimulationParameters* p)
{
  if (p == 0)
    return false;
  if (!p->CheckValues())
    return false;

  TumorSimulationParametersXMLFileWriter::Pointer writer =
    TumorSimulationParametersXMLFileWriter::New();

  // Enforce XML file extension
  std::string outfn = fn;
  std::string ext = mu::get_ext(fn);
  if (ext.compare("xml") != 0)
    outfn += std::string(".xml");

  writer->SetFilename(outfn.c_str());
  writer->SetObject(p);
  writer->WriteFile();

  return true;
}

