/* massXpert - the true massist's program.
   --------------------------------------
   Copyright(C) 2006,2007 Filippo Rusconi

   http://www.massxpert.org/massXpert

   This file is part of the massXpert project.

   The massxpert project is the successor to the "GNU polyxmass"
   project that is an official GNU project package(see
   www.gnu.org). The massXpert project is not endorsed by the GNU
   project, although it is released ---in its entirety--- under the
   GNU General Public License. A huge part of the code in massXpert
   is actually a C++ rewrite of code in GNU polyxmass. As such
   massXpert was started at the Centre National de la Recherche
   Scientifique(FRANCE), that granted me the formal authorization to
   publish it under this Free Software License.

   This software is free software; you can redistribute it and/or
   modify it under the terms of the GNU  General Public
   License version 3, as published by the Free Software Foundation.
   

   This software 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 software; if not, write to the

   Free Software Foundation, Inc.,

   51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*/


/////////////////////// Local includes
#include "chemicalGroup.hpp"


namespace massXpert
{

  ChemicalGroup::ChemicalGroup(QString name, float pka, 
				bool acidCharged, int polRule)
    : m_name(name), m_pka(pka), 
      m_acidCharged(acidCharged), m_polRule(polRule)
  {
    Q_ASSERT(m_pka > 0 && m_pka < 14);
  }


  ChemicalGroup::ChemicalGroup(const ChemicalGroup &other)
    : m_name(other.m_name), m_pka(other.m_pka), 
      m_acidCharged(other.m_acidCharged), m_polRule(other.m_polRule)
  {
  }


  ChemicalGroup::~ChemicalGroup()
  { 
    while(!m_ruleList.isEmpty())
      delete m_ruleList.takeFirst();
  }

  void 
  ChemicalGroup::setName(QString name)
  {
    m_name = name;
  }


  QString 
  ChemicalGroup::name() const
  {
    return m_name;
  }

  
  void 
  ChemicalGroup::setPka(float pka)
  {
    Q_ASSERT(pka > 0 && pka < 14);
  
    m_pka = pka;
  }


  float 
  ChemicalGroup::pka() const
  {
    return m_pka;
  }

  
  void 
  ChemicalGroup::setAcidCharged(bool acidCharged)
  {
    m_acidCharged = acidCharged;
  }

  bool 
  ChemicalGroup::isAcidCharged() const
  {
    return m_acidCharged;
  }

  
  void 
  ChemicalGroup::setPolRule(int polRule)
  {
    m_polRule = polRule;
  }


  int 
  ChemicalGroup::polRule() const
  {
    return m_polRule;
  }


  QList<ChemicalGroupRule *> &
  ChemicalGroup::ruleList()
  {
    return m_ruleList;
  }


  ChemicalGroupRule *
  ChemicalGroup::findRuleEntity(QString value, int *index) const
  {
    int ruleIndex = 0;
  
    if (!index)
      ruleIndex = 0;
    else 
      {
	if(*index < 0)
	  return 0;
	else if (*index > m_ruleList.size())
	  return 0;
	ruleIndex = *index;
      }
  
    if (value.isEmpty())
      return 0;
  
    for (int iter = ruleIndex; iter < m_ruleList.size(); ++iter)
      {
	ChemicalGroupRule *rule = m_ruleList.at(iter);
      
	if(rule->entity() == value)
	  {
	    if (index)
	      *index = iter;

	    return rule;
	  }
      }
  
    return 0;
  }


  ChemicalGroupRule *
  ChemicalGroup::findRuleName(QString value, int *index) const
  {
    int ruleIndex = 0;
  
    if (!index)
      ruleIndex = 0;
    else 
      {
	if(*index < 0)
	  return 0;
	else if (*index > m_ruleList.size())
	  return 0;
	ruleIndex = *index;
      }
  
    if (value.isEmpty())
      return 0;
  
    for (int iter = ruleIndex; iter < m_ruleList.size(); ++iter)
      {
	ChemicalGroupRule *rule = m_ruleList.at(iter);
      
	if(rule->name() == value)
	  {
	    if (index)
	      *index = iter;
      
	    return rule;
	  }
      }
  
    return 0;
  }


  ChemicalGroupRule *
  ChemicalGroup::findRule(QString entity, QString name, int *index) const
  {
    int ruleIndex = 0;
  
    if (!index)
      ruleIndex = 0;
    else 
      {
	if(*index < 0)
	  return 0;
	else if (*index > m_ruleList.size())
	  return 0;
	ruleIndex = *index;
      }
  
    if (entity.isEmpty() || name.isEmpty())
      return 0;
  
    for (int iter = ruleIndex; iter < m_ruleList.size(); ++iter)
      {
	ChemicalGroupRule *rule = m_ruleList.at(iter);
      
	if(rule->entity() == entity && rule->name() == name)
	  {
	    if (index)
	      *index = iter;
      
	    return rule;
	  }
      }
  
    return 0;
  }


  bool 
  ChemicalGroup::renderXmlMnmElement(const QDomElement &element)
  {
    QDomElement child;

    // In an acidobasic definition file, the following xml structure
    // is encountered:

    // <acidobasicdata>			  
    //   <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>                                

    // The relevant DTD lines are:

    // <!ELEMENT monomer(code,mnmchemgroup*)>
    // <!ELEMENT mnmchemgroup(name,pka,acidcharged,polrule,chemgrouprule*)>
    
    // And the element the parameter points to is:

    //  <chemgroup>				  

    // Which means that element.tagName() == "chemgroup" and that we'll
    // have to go one step down to the first child of the current node
    // in order to get to the <name> element.

    if (element.tagName() != "mnmchemgroup")
      return false;
    
    child = element.firstChildElement("name");
  
    if (child.isNull())
      return false;
  
    m_name = child.text();
  
    child = child.nextSiblingElement();
  
    if (child.isNull() || child.tagName() != "pka")
      return false;
  
    bool ok = false;
    m_pka = child.text().toFloat(&ok);
  
    if (!m_pka && !ok)
      return false;

    if (m_pka <= 0 || m_pka >= 14)
      return false;
  
    child = child.nextSiblingElement();
  
    if (child.isNull() || child.tagName() != "acidcharged")
      return false;
  
    if (child.text() != "FALSE" && child.text() != "TRUE")
      return false;
  
    m_acidCharged =(child.text() == "FALSE" ? false : true);
  
    // And now the polrule element. There should be one, here, in fact,
    // because we are dealing with a monomer, and not a modification.
  
    child = child.nextSiblingElement();
  
    if (child.isNull() || child.tagName() != "polrule")
      return false;
  
    if (child.text() == "never_trapped")
      m_polRule = MXP_CHEMGROUP_NEVER_TRAPPED;
    else if (child.text() == "left_trapped")
      m_polRule = MXP_CHEMGROUP_LEFT_TRAPPED;
    else if (child.text() == "right_trapped")
      m_polRule = MXP_CHEMGROUP_RIGHT_TRAPPED;
    else
      return false;
    
    // And finally the chemical group rules... There might be zero, one
    // or more.

    QDomElement childChemGroupRule = child.nextSiblingElement("chemgrouprule");
  
    while(!childChemGroupRule.isNull())
      {
	ChemicalGroupRule *rule = new ChemicalGroupRule();
      
	if(!rule->renderXmlElement(childChemGroupRule))
	  {
	    delete rule;
	    return false;
	  }
      
	m_ruleList.append(rule);
      
	childChemGroupRule = childChemGroupRule.nextSiblingElement();
      }
  
    return true;
  }


  bool 
  ChemicalGroup::renderXmlMdfElement(const QDomElement &element)
  {
    QDomElement child;

    // In an acidobasic definition file, the following xml structure
    // is encountered:

    // <acidobasicdata>			  
    // <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>                                  

    // The relevant DTD lines are:

    // <!ELEMENT modif(name,mdfchemgroup*)>
    // <!ELEMENT mdfchemgroup(name,pka,acidcharged)>

    // And the element the parameter points to is:

    //  <chemgroup>				  

    // Which means that element.tagName() == "chemgroup" and that we'll
    // have to go one step down to the first child of the current node
    // in order to get to the <name> element.

    if (element.tagName() != "mdfchemgroup")
      return false;
    
    child = element.firstChildElement("name");
  
    if (child.isNull())
      return false;
  
    m_name = child.text();
  
    child = child.nextSiblingElement();
  
    if (child.isNull() || child.tagName() != "pka")
      return false;
  
    bool ok = false;
    m_pka = child.text().toFloat(&ok);
  
    if (!m_pka && !ok)
      return false;

    if (m_pka <= 0 || m_pka >= 14)
      return false;
  
    child = child.nextSiblingElement();
  
    if (child.isNull() || child.tagName() != "acidcharged")
      return false;
  
    if (child.text() != "FALSE" && child.text() != "TRUE")
      return false;
  
    m_acidCharged =(child.text() == "FALSE" ? false : true);
  
    return true;
  }


  //////////////////////// ChemicalGroupProp ////////////////////////

  ChemicalGroupProp::ChemicalGroupProp(const QString &name,
					ChemicalGroup *data)
  {
    if (!name.isEmpty())
      m_name = name;
    else
      m_name = QString();
  
    mpa_data = static_cast<void *>(data);
  }


  ChemicalGroupProp::~ChemicalGroupProp()
  {
    deleteData();
  }


  //! Deletes the data in this double property.
  /*! The data is destroyed as a double is destroyed.
   */
  void
  ChemicalGroupProp::deleteData()
  {
    if (mpa_data)
      {
	delete static_cast<ChemicalGroup *>(mpa_data);
	mpa_data = 0;
      }
  }


  void * 
  ChemicalGroupProp::clone() const
  {
    ChemicalGroupProp *prop = 0;
    
    if (mpa_data)
      prop = new ChemicalGroupProp(m_name,
				    static_cast<ChemicalGroup *>(mpa_data));
    
    return static_cast<void *>(prop);
  }
  

  void  
  ChemicalGroupProp::cloneOut(void *other) const
  {
    Q_ASSERT(other);
    
    ChemicalGroupProp *otherProp = static_cast<ChemicalGroupProp *>(other);
    
    if (otherProp->mpa_data)
      otherProp->deleteData();
    
    otherProp->m_name = m_name;
    
    ChemicalGroup *chemicalGroup = 0;
    
    if (mpa_data)
      {
	chemicalGroup = static_cast<ChemicalGroup *>(mpa_data);
	
	otherProp->mpa_data = 
	  static_cast<void *>(new ChemicalGroup(*chemicalGroup));
      }
    else
      otherProp->mpa_data = 0;
    
  }
  

  void  
  ChemicalGroupProp::cloneIn(const void *other)
  {
     Q_ASSERT(other);
    
    const ChemicalGroupProp *otherProp = 
      static_cast<const ChemicalGroupProp *>(other);

    if (mpa_data)
      deleteData();
    
    m_name = otherProp->m_name;
    
    ChemicalGroup *chemicalGroup = 0;
    
    if (otherProp->mpa_data)
      {
	chemicalGroup = static_cast<ChemicalGroup *>(otherProp->mpa_data);
	
	mpa_data = static_cast<void *>(new ChemicalGroup(*chemicalGroup));
      }
    else
      mpa_data = 0;   

  }
  

  bool 
  ChemicalGroupProp::renderXmlElement(const QDomElement &element, int version)
  {
    if (element.isNull())
      ;
  
    return false;
  }


  QString *
  ChemicalGroupProp::formatXmlElement(int offset, const QString &indent)
  {
    offset = offset;

    if (indent.isEmpty())
      ;
    
    return 0;
  }

} // namespace massXpert
