/* BEGIN software license
 *
 * msXpertSuite - mass spectrometry software suite
 * -----------------------------------------------
 * Copyright(C) 2009,...,2018 Filippo Rusconi
 *
 * http://www.msxpertsuite.org
 *
 * This file is part of the msXpertSuite project.
 *
 * The msXpertSuite project is the successor of the massXpert project. This
 * project now includes various independent modules:
 *
 * - massXpert, model polymer chemistries and simulate mass spectrometric data;
 * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner;
 *
 * This program is free software: 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 3 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, see <http://www.gnu.org/licenses/>.
 *
 * END software license
 */


/////////////////////// Local includes
#include <massxpert/nongui/PkaPhPiDataParser.hpp>
#include <massxpert/nongui/ChemicalGroup.hpp>


namespace msXpSmassXpert
{

PkaPhPiDataParser::PkaPhPiDataParser(const PolChemDef *polChemDef,
                                     QString filePath)
  : mp_polChemDef(polChemDef), m_filePath(filePath)
{
  Q_ASSERT(mp_polChemDef);
}


PkaPhPiDataParser::~PkaPhPiDataParser()
{
}


void
PkaPhPiDataParser::setFilePath(const QString &filePath)
{
  m_filePath = filePath;
}


const QString &
PkaPhPiDataParser::filePath()
{
  return m_filePath;
}


bool
PkaPhPiDataParser::renderXmlFile(QList<Monomer *> *monomerList,
                                 QList<Modif *> *modifList)
{
  Q_ASSERT(monomerList);
  Q_ASSERT(modifList);

  // General structure of the file:
  //
  // <pkaphpidata>
  //   <monomers>
  //     <monomer>
  //       <code>A</code>
  //       <mnmchemgroup>
  //         <name>N-term NH2</name>
  // 	<pka>9.6</pka>
  // 	<acidcharged>TRUE</acidcharged>
  // 	<polrule>left_trapped</polrule>
  // 	<chemgrouprule>
  // 	  <entity>LE_PLM_MODIF</entity>
  // 	  <name>Acetylation</name>
  // 	  <outcome>LOST</outcome>
  // 	</chemgrouprule>
  //       </mnmchemgroup>
  //       <mnmchemgroup>
  //         <name>C-term COOH</name>
  // 	<pka>2.35</pka>
  // 	<acidcharged>FALSE</acidcharged>
  // 	<polrule>right_trapped</polrule>
  //       </mnmchemgroup>
  //    </monomer>
  // ...
  //
  //   <modifs>
  //     <modif>
  //       <name>Phosphorylation</name>
  //       <mdfchemgroup>
  //         <name>none_set</name>
  // 	<pka>12</pka>
  // 	<acidcharged>FALSE</acidcharged>
  //       </mdfchemgroup>
  //       <mdfchemgroup>
  //         <name>none_set</name>
  // 	<pka>7</pka>
  // 	<acidcharged>FALSE</acidcharged>
  //       </mdfchemgroup>
  //     </modif>
  //   </modifs>
  // </pkaphpidata>
  //
  // The DTD stipulates that:
  //
  // <!ELEMENT pkaphpidata(monomers,modifs*)>
  // <!ELEMENT monomers(monomer*)>
  // <!ELEMENT modifs(modif*)>
  // <!ELEMENT monomer(code,mnmchemgroup*)>
  // <!ELEMENT modif(name,mdfchemgroup*)>


  QDomDocument doc("pkaPhPiData");
  QDomElement element;
  QDomElement child;
  QDomElement indentedChild;

  QFile file(m_filePath);

  if(!file.open(QIODevice::ReadOnly))
    return false;

  if(!doc.setContent(&file))
    {
      file.close();
      return false;
    }

  file.close();

  element = doc.documentElement();

  if(element.tagName() != "pkaphpidata")
    {
      qDebug() << __FILE__ << __LINE__ << "pKa-pH-pI data file is erroneous\n";
      return false;
    }

  // The first child element must be <monomers>.

  child = element.firstChildElement();
  if(child.tagName() != "monomers")
    {
      qDebug() << __FILE__ << __LINE__ << "pKa-pH-pI data file is erroneous\n";
      return false;
    }

  // Parse the <monomer> elements.

  indentedChild = child.firstChildElement();
  while(!indentedChild.isNull())
    {
      if(indentedChild.tagName() != "monomer")
        return false;

      QDomElement superIndentedElement = indentedChild.firstChildElement();

      if(superIndentedElement.tagName() != "code")
        return false;

      Monomer *monomer =
        new Monomer(mp_polChemDef, "NOT_SET", superIndentedElement.text());

      // All the <mnmchemgroup> elements, if any.

      superIndentedElement = superIndentedElement.nextSiblingElement();
      while(!superIndentedElement.isNull())
        {
          if(superIndentedElement.tagName() != "mnmchemgroup")
            {
              delete monomer;
              return false;
            }

          ChemicalGroup *chemGroup = new ChemicalGroup("NOT_SET");

          if(!chemGroup->renderXmlMnmElement(superIndentedElement))
            {
              delete monomer;
              delete chemGroup;
              return false;
            }

          ChemicalGroupProp *prop =
            new ChemicalGroupProp("CHEMICAL_GROUP", chemGroup);

          monomer->appendProp(prop);

          superIndentedElement = superIndentedElement.nextSiblingElement();
        }

      monomerList->append(monomer);

      indentedChild = indentedChild.nextSiblingElement();
    }

#if 0

    qDebug() << __FILE__ << __LINE__
	      << "Debug output of all the monomers parsed:";

    for (int iter = 0; iter < monomerList->size(); ++iter)
      {
	Monomer *monomer = monomerList->at(iter);
	qDebug() << __FILE__ << __LINE__
		  << "Monomer:" << monomer->name();

	for(int jter = 0; jter < monomer->propList()->size(); ++jter)
	  {
	    Prop *prop = monomer->propList()->at(jter);

	    if (prop->name() == "CHEMICAL_GROUP")
	      {
		const ChemicalGroup *chemGroup =
		  static_cast<const ChemicalGroup *>(prop->data());

		qDebug() << __FILE__ << __LINE__
			  << "Chemical group:"
			  << chemGroup->name() << chemGroup->pka();
	      }
	  }
      }

#endif

  // And now parse the <modifs> elements, if any, this time, as
  // this element is not compulsory.

  child = child.nextSiblingElement();
  if(child.isNull())
    return true;

  if(child.tagName() != "modifs")
    {
      qDebug() << __FILE__ << __LINE__ << "pKa-pH-pI data file is erroneous\n";
      return false;
    }

  // Parse the <modif> elements.

  indentedChild = child.firstChildElement();
  while(!indentedChild.isNull())
    {
      if(indentedChild.tagName() != "modif")
        return false;

      QDomElement superIndentedElement = indentedChild.firstChildElement();

      if(superIndentedElement.tagName() != "name")
        return false;

      Modif *modif =
        new Modif(mp_polChemDef, superIndentedElement.text(), "H0");

      // All the <mdfchemgroup> elements, if any.

      superIndentedElement = superIndentedElement.nextSiblingElement();
      while(!superIndentedElement.isNull())
        {
          if(superIndentedElement.tagName() != "mdfchemgroup")
            {
              delete modif;
              return false;
            }

          ChemicalGroup *chemGroup = new ChemicalGroup("NOT_SET");

          if(!chemGroup->renderXmlMdfElement(superIndentedElement))
            {
              delete modif;
              delete chemGroup;
              return false;
            }

          ChemicalGroupProp *prop =
            new ChemicalGroupProp("CHEMICAL_GROUP", chemGroup);

          modif->appendProp(prop);

          superIndentedElement = superIndentedElement.nextSiblingElement();
        }

      modifList->append(modif);

      indentedChild = indentedChild.nextSiblingElement();
    }

#if 0

    qDebug() << __FILE__ << __LINE__
	      << "Debug output of all the modifs parsed:";

    for (int iter = 0; iter < modifList->size(); ++iter)
      {
	Modif *modif = modifList->at(iter);

	//       qDebug() << __FILE__ << __LINE__
	// 		<< "Modif:" << modif->name();

	for(int jter = 0; jter < modif->propList()->size(); ++jter)
	  {
	    Prop *prop = modif->propList()->at(jter);

	    if (prop->name() == "CHEMICAL_GROUP")
	      {
		const ChemicalGroup *chemGroup =
		  static_cast<const ChemicalGroup *>(prop->data());

		qDebug() << __FILE__ << __LINE__
			  << "Chemical group:"
			  << chemGroup->name() << chemGroup->pka();
	      }
	  }
      }

#endif

  return true;
}

} // namespace msXpSmassXpert
