/* 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.
*/


/////////////////////// Qt includes
#include <QMessageBox>


/////////////////////// Local includes
#include "compositionsDlg.hpp"
#include "application.hpp"
#include "coordinates.hpp"

namespace massXpert
{

  CompositionsDlg::CompositionsDlg(QWidget *parent,
				    Polymer *polymer,
				    CalcOptions *calcOptions,
				    IonizeRule *ionizeRule)
    : QDialog(parent),
      mp_polymer(polymer), 
      mp_calcOptions(calcOptions),
      mp_ionizeRule(ionizeRule)
  {
    Q_ASSERT(parent);
    Q_ASSERT(mp_polymer && mp_calcOptions && mp_ionizeRule);
  
    m_ui.setupUi(this);
  
    mp_editorWnd = static_cast<SequenceEditorWnd *>(parent);
    
    updateSelectionData();
        
    setupTreeView();

    // The results-exporting menus. ////////////////////////////////

    QStringList comboBoxItemList;

    comboBoxItemList 
      << tr("To Clipboard") 
      << tr("To File")
      << tr("Select File");
  
    m_ui.exportResultsComboBox->addItems(comboBoxItemList);
  
    connect(m_ui.exportResultsComboBox,
	     SIGNAL(activated(int)),
	     this,
	     SLOT(exportResults(int)));

    mpa_resultsString = new QString();
  
    //////////////////////////////////// The results-exporting menus.
  

    QSettings settings 
     (static_cast<Application *>(qApp)->configSettingsFilePath(), 
       QSettings::IniFormat);
  
    settings.beginGroup("compositions_dlg");

    restoreGeometry(settings.value("geometry").toByteArray());

    m_ui.splitter->restoreState(settings.value("splitter").toByteArray());
  
    settings.endGroup();


    connect(m_ui.updateSelectionDataPushButton,
	     SIGNAL(clicked()),
	     this,
	     SLOT(updateSelectionData()));

    connect(m_ui.monomericPushButton,
	     SIGNAL(clicked()),
	     this,
	     SLOT(monomericComposition()));

    connect(m_ui.elementalPushButton, 
	     SIGNAL(clicked()),
	     this, 
	     SLOT(elementalComposition()));
  }


  CompositionsDlg::~CompositionsDlg()
  {
    delete mpa_compositionTreeViewModel;
  
    delete mpa_compositionProxyModel;

    delete mpa_resultsString;
    
    freeMonomerList();
  }


  void 
  CompositionsDlg::closeEvent(QCloseEvent *event)
  {
    if (event)
      printf("%s", "");
  
    QSettings settings 
     (static_cast<Application *>(qApp)->configSettingsFilePath(), 
       QSettings::IniFormat);
  
    settings.beginGroup("compositions_dlg");

    settings.setValue("geometry", saveGeometry());

    settings.setValue("splitter", m_ui.splitter->saveState());
  
    settings.endGroup();
  }


  SequenceEditorWnd *
  CompositionsDlg::editorWnd()
  {
    return mp_editorWnd;
  }


  void
  CompositionsDlg::updateSelectionData()
  {
    // The selection might exist as a list of region selections.
    
    if (mp_editorWnd->mpa_editorGraphicsView->
	selectionIndices(&m_coordinateList))
      {
	m_ui.selectionCoordinatesLineEdit->
	  setText(m_coordinateList.positionsAsText());
	
	m_ui.selectedSequenceRadioButton->setChecked(true);
      }
    else
      {
	m_ui.selectionCoordinatesLineEdit->setText("");
	
	m_ui.wholeSequenceRadioButton->setChecked(true);
      }
    
    m_ui.monomericPushButton->setFocus();
  }
  

  bool
  CompositionsDlg::fetchValidateInputData()
  {
    if (!m_ui.selectedSequenceRadioButton->isChecked())
      {
	m_coordinateList.setCoordinates(Coordinates(0, 
						      mp_polymer->size() - 1));
      }
    
    // Make sure the sequence still has a number of residues
    // compatible with the current m_coordinateList. this because the
    // sequence might be edited between opening this dialog window and
    // actually using it.

    int polymerSize = mp_polymer->size();
    
    for (int iter = 0; iter < m_coordinateList.size(); ++iter)
      {
	// New coordinates instance we are iterating into.
	Coordinates *coordinates = m_coordinateList.at(iter);
		
	if(coordinates->start() >= polymerSize ||
	    coordinates->end() >= polymerSize)
	  {
	    QMessageBox::warning(0, 
				  tr("massXpert - Compositions"),
				  tr("Selection data are no more valid.\n"
				      "Please update these data."),
				  QMessageBox::Ok);
	    return false;
	  }
      }
    
    // We also want to know if the cross-links should be taken into account.
    
    if (mp_calcOptions->monomerEntities() & MXT_MONOMER_CHEMENT_CROSS_LINK)
      {
	// If the whole sequence is dealt with, then by definition all
	// the cross-links are encompassed by the sequence.
	
	if(!m_ui.selectedSequenceRadioButton->isChecked())
	  {
	    m_ui.incompleteCrossLinkWarningLabel->setText 
	     (tr("Incomplete cross-links: %1").
	       arg(0));
	  }
	else
	  {
	    // We have to count the incomplete cross-links.

	    const CrossLinkList &crossLinkList = mp_polymer->crossLinkList();
	  
	    int crossLinkPartial = 0;
	  
	    for (int iter = 0; iter < crossLinkList.size(); ++iter)
	      {
		CrossLink *crossLink = crossLinkList.at(iter);
	  
		int ret = 
		  crossLink->encompassedBy(m_coordinateList);
	  
		if(ret == MXP_CROSS_LINK_ENCOMPASSED_FULL)
		  {
		    // 		qDebug() << __FILE__ << __LINE__
		    // 			  << "CrossLink at iter:" << iter
		    // 			  << "is fully encompassed";
		  }
		else if (ret == MXP_CROSS_LINK_ENCOMPASSED_PARTIAL)
		  {
		    // 		qDebug() << __FILE__ << __LINE__
		    // 			  << "CrossLink at iter:" << iter
		    // 			  << "is partially encompassed";
	      
		    ++crossLinkPartial;
		  }
		else
		  {
		    // 		qDebug() << __FILE__ << __LINE__
		    // 			  << "CrossLink at iter:" << iter
		    // 			  << "is not encompassed at all";
		  }
	      }

	    m_ui.incompleteCrossLinkWarningLabel->setText 
	     (tr("Incomplete cross-links: %1").
	       arg(crossLinkPartial));
	  }
      }
    else
      {
	m_ui.incompleteCrossLinkWarningLabel->
	  setText(tr("Not accounting for cross-links"));
      }
    
    return true;
  }


  void 
  CompositionsDlg::monomericComposition()
  {
    if (!fetchValidateInputData())
      {
	QMessageBox::warning(0, 
			      tr("massXpert - Compositions"),
			      tr("Failed validating input data."),
			      QMessageBox::Ok);
	return;
      }

    Monomer *fakeMonomer = 0;
  
    // Empty the treeview. This will also remove all the monomer items
    // in the m_monomerList.
    mpa_compositionTreeViewModel->removeAll();
  
    setCursor(Qt::WaitCursor);
  
    // Iterate in the sequence and count all the occurrences of the
    // different monomer that comprise the sequence.

    if (mpa_compositionTreeViewModel)
      {
	delete mpa_compositionTreeViewModel;
	mpa_compositionTreeViewModel = 0;
      }
  
    if (mpa_compositionProxyModel)
      {
	delete mpa_compositionProxyModel;
	mpa_compositionProxyModel = 0;
      }

    for (int iter = 0; iter < m_coordinateList.size(); ++iter)
      {
	// New coordinates instance we are iterating into.
	Coordinates *coordinates = m_coordinateList.at(iter);
	
	for(int jter = coordinates->start() ; 
	     jter < coordinates->end() + 1; ++jter)
	  {
	    bool processed = false;

	    const Monomer *iterMonomer = mp_polymer->at(jter);
	    Q_ASSERT(iterMonomer);
      
	    // Check if monomer by same name is not already in our list of
	    // fake monomers.

	    for (int kter = 0; kter < m_monomerList.size(); ++kter)
	      {
		fakeMonomer = m_monomerList.at(kter);
		Q_ASSERT(fakeMonomer);
	  
		if(iterMonomer->name() == fakeMonomer->name())
		  {
		    Prop *prop = fakeMonomer->prop("MONOMER_COUNT");
		
		    // The fake monomer MUST have a count prop !
		    Q_ASSERT(prop);
	      
		    int *count = 
		      new int(*static_cast<const int *>(prop->data()));

		    ++(*count);
		    prop->setData(count);
	      	      
		    if (iterMonomer->isModified())
		      {
			// The monomer in the sequence is modified. Let the
			// fake monomer know it.
			Prop *prop = fakeMonomer->prop("MODIF_COUNT");
		  
			// The fake monomer might not have a modif prop.
			if(prop)
			  {
			    int *count = 
			      new int(*static_cast<const int *>(prop->data()));
			    ++(*count);
			    prop->setData(count);
			  }
			else
			  {
			    IntProp *prop = new IntProp("MODIF_COUNT", 1);
			    fakeMonomer->appendProp(prop);
			  }
		      }
	      
		    processed = true;
	      
		    break;
		  }
	      }
	    // End of 
	    // for (int kter = 0; kter < m_monomerList.size(); ++kter)
	      
	    // Did we find a fake monomer, thus processing the currently
	    // iterated sequence monomer or not ? If not we still have to do
	    // all the work.
      
	    if (!processed)
	      {
		fakeMonomer = iterMonomer->clone();
	  
		IntProp *prop = new IntProp("MONOMER_COUNT", 1);
		fakeMonomer->appendProp(prop);
	  
		if(iterMonomer->isModified())
		  {
		    // The monomer in the sequence is modified. Let the
		    // fake monomer know it.
		      
		    IntProp *prop = new IntProp("MODIF_COUNT", 1);
		    fakeMonomer->appendProp(prop);
		  }
	  
		m_monomerList.append(fakeMonomer);
	      }
	  }
	// End of
	// for (int iter = startIndex ; iter < endIndex + 1; ++iter)

	setupTreeView();
  
	prepareResultsTxtString(MXP_TARGET_MONOMERIC);
  
	setCursor(Qt::ArrowCursor);
      }
  }
  

  void 
  CompositionsDlg::elementalComposition()
  {
    // For each monomer in the sequence, get the formula and account it.

    if (!fetchValidateInputData())
      {
	QMessageBox::warning(0, 
			      tr("massXpert - Compositions"),
			      tr("Failed validating input data."),
			      QMessageBox::Ok);
	return;
      }

    const PolChemDef *polChemDef = mp_polymer->polChemDef();
    const QList <Atom *> &atomRefList = polChemDef->atomList();    
    Formula formula;
  
    setCursor(Qt::WaitCursor);
  
    for (int iter = 0; iter < m_coordinateList.size(); ++iter)
      {
	// New coordinates instance we are iterating into.
	Coordinates *coordinates = m_coordinateList.at(iter);
	
	for(int jter = coordinates->start() ; 
	     jter < coordinates->end() + 1; ++jter)
	  {
	    const Monomer *iterMonomer = mp_polymer->at(jter);
	    Q_ASSERT(iterMonomer);
      
	    // Set the formula of the new monomer in the same formula
	    // instance.
	    formula.setFormula(iterMonomer->formula());

	    // Incrementally account for the new formula in the same
	    // atomcount list in the formula.
	    if (formula.accountAtoms(atomRefList, 1) == false)
	      {
		setCursor(Qt::ArrowCursor);
	  
		QMessageBox::warning 
		 (0, 
		   tr("massXpert - Compositions"),
		   tr("Failed accounting one monomer formula."),
		   QMessageBox::Ok);
	  
		return;
	      }

	    if (mp_calcOptions->monomerEntities() & MXT_MONOMER_CHEMENT_MODIF)
	      {
		if(iterMonomer->isModified())
		  {
		    for (int jter = 0; 
			 jter < iterMonomer->modifList()->size();
			 ++jter)
		      {
			Modif *modif = iterMonomer->modifList()->at(jter);

			formula.setFormula(modif->formula());
		  
			// Incrementally account for the new formula in the same
			// atomcount list in the formula.
			if(formula.accountAtoms(atomRefList, 1) == false)
			  {
			    setCursor(Qt::ArrowCursor);
		      
			    QMessageBox::warning 
			     (0, 
			       tr("massXpert - Compositions"),
			       tr("Failed accounting one monomer modification."),
			       QMessageBox::Ok);
		      
			    return;
			  }
		      }
		  }
	      }
	  }
	// End of for (int jter = m_startIndex ; jter < m_endIndex + 1;
	// ++jter)
      }
    
//     qDebug() << __FILE__ << __LINE__
// 	      << "Formula after accounting for all the residual chains:"
// 	      << formula.elementalComposition();
    
    // We now have to account for the left/right cappings. However,
    // when there are multiple region selections(that is multiple
    // Coordinate elements in the calcOptions.coordinateList()) it is
    // necessary to know if the user wants each of these Coordinates
    // to be considered real oligomers(each one with its left/right
    // caps) or as residual chains. Thus there are two cases:

    // 1. Each Coordinates item should be considered an oligomer
    //(SelectionType is SELECTION_TYPE_OLIGOMERS), thus for each item
    // the left and right caps should be accounted for. This is
    // typically the case when the user selects multiple regions to
    // compute the mass of cross-linked oligomers.

    // 2. Each Coordinates item should be considered a residual chain
    //(SelectionType is SELECTION_TYPE_RESIDUAL_CHAINS), thus only
    // one item should see its left and right caps accounted for. This
    // is typically the case when the user selects multiple regions
    // like it would select repeated sequence elements in a polymer
    // sequence: all the regions selected are treated as a single
    // oligomer.

    // Holds the number of times the chemical entities are to be
    // accounted for.
    int times = 0;
    
    if (mp_calcOptions->selectionType() == SELECTION_TYPE_RESIDUAL_CHAINS)
      {
	times = 1;
// 	qDebug() << __FILE__ << __LINE__
// 		  << "SELECTION_TYPE_RESIDUAL_CHAINS ; times:" << times;
      }
    else
      {
	times = mp_calcOptions->coordinateList().size();

// 	qDebug() << __FILE__ << __LINE__
// 		  << "SELECTION_TYPE_OLIGOMERS ; times:" << times;
      }
    
    // Account for the left and right cap masses, if so required.

    if (mp_calcOptions->capping() & MXT_CAP_LEFT)
      {
	formula.setFormula(polChemDef->leftCap());

	// Incrementally account for the new formula in the same
	// atomcount list in the formula.
	if(formula.accountAtoms(atomRefList, times) == false)
	  {
	    setCursor(Qt::ArrowCursor);
	  
	    QMessageBox::warning 
	     (0, 
	       tr("massXpert - Compositions"),
	       tr("Failed accounting left cap."),
	       QMessageBox::Ok);
	  
	    return;
	  }
	
// 	qDebug() << __FILE__ << __LINE__
// 		  << "Formula after accounting left cap:"
// 		  << formula.elementalComposition();
      }
    
    if (mp_calcOptions->capping() & MXT_CAP_RIGHT)
      {
	formula.setFormula(polChemDef->rightCap());
      
	// Incrementally account for the new formula in the same
	// atomcount list in the formula.
	if(formula.accountAtoms(atomRefList, times) == false)
	  {
	    setCursor(Qt::ArrowCursor);
	  
	    QMessageBox::warning 
	     (0, 
	       tr("massXpert - Compositions"),
	       tr("Failed accounting right cap."),
	       QMessageBox::Ok);
	  
	    return;
	  }
	
// 	qDebug() << __FILE__ << __LINE__
// 		  << "Formula after accounting right cap:"
// 		  << formula.elementalComposition();
      }
    
    // Account for the left and right modification masses, if so
    // required and the region(s) require(s) it: we have to make it
    // clear if the selection encompasses indices 0(left end) and/or
    // polymerSize-1(right end).
    
    if (mp_calcOptions->polymerEntities() & 
	MXT_POLYMER_CHEMENT_LEFT_END_MODIF)
      {
	if(m_coordinateList.encompassIndex(0))
	  {
	    Modif modif =  mp_polymer->leftEndModif();
      
	    formula.setFormula(modif.formula());

	    qDebug() << __FILE__ << __LINE__
		      << "Accounting for left end modif:"
		      << modif.name();
	    
	    // Incrementally account for the new formula in the same
	    // atomcount list in the formula.
	    if (formula.accountAtoms(atomRefList, 1) == false)
	      {
		setCursor(Qt::ArrowCursor);
	  
		QMessageBox::warning 
		 (0, 
		   tr("massXpert - Compositions"),
		   tr("Failed accounting left end modification."),
		   QMessageBox::Ok);
	  
		return;
	      }

// 	    qDebug() << __FILE__ << __LINE__
// 		      << "Formula after accounting left end modif:"
// 		      << formula.elementalComposition();
	  }
      }
    
    if (mp_calcOptions->polymerEntities() & 
	MXT_POLYMER_CHEMENT_RIGHT_END_MODIF)
      {
	if(m_coordinateList.encompassIndex(mp_polymer->size() - 1))
	  {
	    Modif modif =  mp_polymer->rightEndModif();
      
	    formula.setFormula(modif.formula());

// 	    qDebug() << __FILE__ << __LINE__
// 		      << "Accounting for right end modif:"
// 		      << modif.name();
	    
	    // Incrementally account for the new formula in the same
	    // atomcount list in the formula.
	    if (formula.accountAtoms(atomRefList, 1) == false)
	      {
		setCursor(Qt::ArrowCursor);
	  
		QMessageBox::warning 
		 (0, 
		   tr("massXpert - Compositions"),
		   tr("Failed accounting right end modification."),
		   QMessageBox::Ok);
	  
		return;
	      }

// 	    qDebug() << __FILE__ << __LINE__
// 		      << "Formula after accounting right end modif:"
// 		      << formula.elementalComposition();
	  }
      }

    // At this point we should not forget if the user asks to take into
    // account the cross-links... However, BE CAREFUL that cross-links
    // can only be taken into account if all the partners of a given
    // cross-link are actually encompassed into the selection.

    if (mp_calcOptions->monomerEntities() & MXT_MONOMER_CHEMENT_CROSS_LINK)
      {
	const CrossLinkList &crossLinkList = mp_polymer->crossLinkList();
      
	for(int iter = 0; iter < crossLinkList.size(); ++iter)
	  {
	    CrossLink *crossLink = crossLinkList.at(iter);
	  
	    if (crossLink->encompassedBy(m_coordinateList)
		== MXP_CROSS_LINK_ENCOMPASSED_FULL)
	      {
		// The crossLink is fully encompassed by our monomer
		// stretch, so we should take it into account.
	      
// 		qDebug() << __FILE__ << __LINE__
// 			  << "Accounting for fully encompassed cross-link:"
// 			  << crossLink->name();
		
		if(!crossLink->formula().isEmpty())
		  {
		    formula.setFormula(crossLink->formula());
		  
		    // qDebug() << __FILE__ << __LINE__ 
		    // << "Cross-link formula:" << crossLink->formula();
		  
		    // Incrementally account for the new formula in the same
		    // atomcount list in the formula.
		    if (formula.accountAtoms(atomRefList, 1) == false)
		      {
			setCursor(Qt::ArrowCursor);
		      
			QMessageBox::warning 
			 (0, 
			   tr("massXpert - Compositions"),
			   tr("%1@%\n"
			       "Failed accounting for crossLink %3 "
			       "with formula %4")
			   .arg(__FILE__)
			   .arg(__LINE__)
			   .arg(crossLink->name())
			   .arg(crossLink->formula()),
			   QMessageBox::Ok);
		      }
		  }
	      
		// And now each modification that belongs to the
		// crosslinker.
	      
		for(int jter = 0; 
		     jter < crossLink->modifList().size(); 
		     ++jter)
		  {
		    QString iterFormulaString = 
		      crossLink->modifList().at(jter)->formula();
		  
		    // 		  qDebug() << __FILE__ << __LINE__ 
		    // 			    << "Cross-link's modif formula:" 
		    // 			    << iterFormulaString;
		  
		    formula.setFormula(iterFormulaString);
		  
		    // Incrementally account for the new formula in the same
		    // atomcount list in the formula.
		    if (formula.accountAtoms(atomRefList, 1) == false)
		      {
			setCursor(Qt::ArrowCursor);
		      
			QMessageBox::warning 
			 (0, 
			   tr("massXpert - Compositions"),
			   tr("%1@%2\n"
			       "Failed accounting for cross-link %3"
			       "with modif formula %4")
			   .arg(crossLink->name())
			   .arg(crossLink->formula()),
			   QMessageBox::Ok);
		      
			return;
		      }
		  }
	      }
	    // End of 
	    // if (ret == MXP_CROSS_LINK_ENCOMPASSED_FULL)
	  }
	// End of
	// for (int iter = 0; iter < crossLinkList->size(); ++iter)
      }
    // End of
    // if (mp_calcOptions->monomerEntities() & MXT_MONOMER_CHEMENT_CROSS_LINK)
  
  
    // The ionization rule. Do not forget to take into account the
    // level!
    formula.setFormula(mp_ionizeRule->formula());
  
    // Incrementally account for the new formula in the same
    // atomcount list in the formula.
    if (formula.accountAtoms(atomRefList, mp_ionizeRule->level()) == false)
      {
	setCursor(Qt::ArrowCursor);
      
	QMessageBox::warning 
	 (0, 
	   tr("massXpert - Compositions"),
	   tr("Failed accounting ionization rule."),
	   QMessageBox::Ok);
      
	return;
      }
  
    m_ui.elementalCompositionLineEdit->
      setText(formula.elementalComposition());

    prepareResultsTxtString(MXP_TARGET_ELEMENTAL);

    setCursor(Qt::ArrowCursor);

  }


  void
  CompositionsDlg::setupTreeView()
  {
    // Model stuff all thought for sorting.
    mpa_compositionTreeViewModel = 
      new CompositionTreeViewModel(&m_monomerList, this);

    mpa_compositionProxyModel = new CompositionTreeViewSortProxyModel(this);
    mpa_compositionProxyModel->setSourceModel(mpa_compositionTreeViewModel);

    m_ui.compositionTreeView->setModel(mpa_compositionProxyModel);
    m_ui.compositionTreeView->setParentDlg(this);
    mpa_compositionTreeViewModel->setTreeView(m_ui.compositionTreeView);
  }


  void 
  CompositionsDlg::freeMonomerList()
  {
    while(!m_monomerList.isEmpty())
      delete m_monomerList.takeFirst();
  }


  // The results-exporting functions. ////////////////////////////////
  // The results-exporting functions. ////////////////////////////////
  // The results-exporting functions. ////////////////////////////////
  void
  CompositionsDlg::exportResults(int index)
  {
    // Remember that we had set up the combobox with the following strings:
    // << tr("To &Clipboard") 
    // << tr("To &File")
    // << tr("&Select File");

    if (index == 0)
      {
	exportResultsClipboard();
      }
    else if (index == 1)
      {
	exportResultsFile();
      }
    else if (index == 2)
      {
	selectResultsFile();
      }
    else 
      Q_ASSERT(0);
  
  }


  void
  CompositionsDlg::prepareResultsTxtString(int target)
  {
    mpa_resultsString->clear();
  
    *mpa_resultsString += QObject::tr("\n-------------\n"
				       "Compositions: \n"
				       "-------------\n");

    if (target == MXP_TARGET_ELEMENTAL)
      {
	*mpa_resultsString += QObject::tr("\nIonization rule:\n");

	*mpa_resultsString += QObject::tr("Formula: %1 - ")
	  .arg(mp_ionizeRule->formula());
  
	*mpa_resultsString += QObject::tr("Charge: %1 - ")
	  .arg(mp_ionizeRule->charge());
  
	*mpa_resultsString += QObject::tr("Level: %1\n")
	  .arg(mp_ionizeRule->level());
  
  
	*mpa_resultsString += QObject::tr("\nCalculation options:\n");
  
      
	bool withEntities =(mp_calcOptions->monomerEntities() &
			 MXT_MONOMER_CHEMENT_MODIF);

	// We want a delimited sequence with indication of the
	// different sequence regions for which the composition was
	// determined, thus the true below.
	
	QString *sequence = 
	  mp_polymer->monomerText(m_coordinateList, withEntities, true);
	
	*mpa_resultsString += *sequence;
	
	delete sequence;
      
	if(withEntities)
	  *mpa_resultsString += QObject::tr("Account monomer modifs: yes\n");
	else
	  *mpa_resultsString += QObject::tr("Account monomer modifs: no\n");
	
	// Left end and right end modifs
	withEntities =(mp_calcOptions->polymerEntities() &
			MXT_POLYMER_CHEMENT_LEFT_END_MODIF ||
			mp_calcOptions->polymerEntities() &
			MXT_POLYMER_CHEMENT_RIGHT_END_MODIF);
	
	if(!withEntities)
	  {
	    *mpa_resultsString += QObject::tr("Account ends' modifs: no\n");
	  }
	else
	  {
	    *mpa_resultsString += QObject::tr("Account ends' modifs: yes - ");
	  
	    // Left end modif
	    withEntities =(mp_calcOptions->polymerEntities() &
			    MXT_POLYMER_CHEMENT_LEFT_END_MODIF);
	    if (withEntities)
	      {
		*mpa_resultsString += QObject::tr("Left end modif: %1 - ")
		  .arg(mp_polymer->leftEndModif().name());
	      }
	  
	    // Right end modif
	    withEntities =(mp_calcOptions->polymerEntities() &
			    MXT_POLYMER_CHEMENT_RIGHT_END_MODIF);
	    if (withEntities)
	      {
		*mpa_resultsString += QObject::tr("Right end modif: %1")
		  .arg(mp_polymer->leftEndModif().name());
	      }
	  }
	
	*mpa_resultsString += QObject::tr("\n\nElemental composition: %1")
	  .arg(m_ui.elementalCompositionLineEdit->text());
      }
    else if (target == MXP_TARGET_MONOMERIC)
      {
	CompositionTreeViewModel *model = 
	  static_cast<CompositionTreeViewModel *> 
	 (m_ui.compositionTreeView->model());
	Q_ASSERT(model);
	
	int rowCount = model->rowCount();
	//   qDebug() << __FILE__ << __LINE__ << "rowCount" << rowCount;
	if(!rowCount)
	  return;
      
	QString composString;
      
	for(int iter = 0; iter < rowCount; ++iter)
	  {
	    QModelIndex currentIndex = model->index(iter,
						     COMPOSITION_NAME_COLUMN,
						     QModelIndex());
	    Q_ASSERT(currentIndex.isValid());
	  
	    composString += QObject::tr("%1 - ").
	      arg(model->data(currentIndex, Qt::DisplayRole).toString());
	    

	    currentIndex = model->index(iter,
					 COMPOSITION_CODE_COLUMN,
					 QModelIndex());
	    Q_ASSERT(currentIndex.isValid());
	  
	    composString += QObject::tr("%1 - ").
	      arg(model->data(currentIndex, Qt::DisplayRole).toString());
	  
	  
	    currentIndex = model->index(iter,
					 COMPOSITION_MODIF_COLUMN,
					 QModelIndex());
	    Q_ASSERT(currentIndex.isValid());
	  
	    composString += QObject::tr("Modified ?: %1 - ").
	      arg(model->data(currentIndex, Qt::DisplayRole).toString());
	  
	  
	    currentIndex = model->index(iter,
					 COMPOSITION_COUNT_COLUMN,
					 QModelIndex());
	    Q_ASSERT(currentIndex.isValid());
	  
	    composString += QObject::tr("Count: %1.\n").
	      arg(model->data(currentIndex, Qt::DisplayRole).toString());
	  }
	*mpa_resultsString += composString;
      }
    else
      Q_ASSERT(0);
  }


  bool 
  CompositionsDlg::exportResultsClipboard()
  {
    QClipboard *clipboard = QApplication::clipboard();

    clipboard->setText(*mpa_resultsString, QClipboard::Clipboard);
  
    return true;
  }


  bool 
  CompositionsDlg::exportResultsFile()
  {
    if (m_resultsFilePath.isEmpty())
      {
	if(!selectResultsFile())
	  return false;
      }
  
    QFile file(m_resultsFilePath);
  
    if (!file.open(QIODevice::WriteOnly | QIODevice::Append))
      {
	QMessageBox::information(0, 
				  tr("massXpert - Export Data"),
				  tr("Failed to open file in append mode."),
				  QMessageBox::Ok);
	return false;
      }
  
    QTextStream stream(&file);
    stream.setCodec("UTF-8");

    stream << *mpa_resultsString;
  
    file.close();

    return true;
  }


  bool 
  CompositionsDlg::selectResultsFile()
  {
    m_resultsFilePath = 
      QFileDialog::getSaveFileName(this, tr("Select file to export data to"),
				    QDir::homePath(),
				    tr("Data files(*.dat *.DAT)"));
  
    if (m_resultsFilePath.isEmpty())
      return false;

    return true;
  }
  //////////////////////////////////// The results-exporting functions.
  //////////////////////////////////// The results-exporting functions.
  //////////////////////////////////// The results-exporting functions.

} // namespace massXpert
