/* 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>
#include <QFileDialog>


/////////////////////// Local includes
#include "globals.hpp"
#include "application.hpp"
#include "pkaPhPiDlg.hpp"
#include "pkaPhPi.hpp"


namespace massXpert
{

  PkaPhPiDlg::PkaPhPiDlg(QWidget *parent,
			  PkaPhPi *pkaPhPi, 
			  Polymer &polymer,
			  CalcOptions &calcOptions)
    : QDialog(parent), 
      mpa_pkaPhPi(pkaPhPi),
      m_polymer(polymer), 
      m_calcOptions(calcOptions)
  {
    Q_ASSERT(mpa_pkaPhPi); // ownership taken by *this.

    m_ui.setupUi(this);
  
    mp_editorWnd = static_cast<SequenceEditorWnd *>(parent);

    // The selection might exist as a list of region selections.
    
    CoordinateList coordList;
    
    if (mp_editorWnd->mpa_editorGraphicsView->
	selectionIndices(&coordList))
      {
	m_ui.selectionCoordinatesLineEdit->
	  setText(coordList.positionsAsText());
	
	m_ui.selectedSequenceRadioButton->setChecked(true);
      }
    else
      {
	m_ui.selectionCoordinatesLineEdit->setText("");
	
	m_ui.wholeSequenceRadioButton->setChecked(true);
      }

    // 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("pka_ph_pi_dlg");

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


    connect(m_ui.isoelectricPointPushButton,
	     SIGNAL(clicked()),
	     this,
	     SLOT(isoelectricPoint()));

    connect(m_ui.netChargePushButton, 
	     SIGNAL(clicked()),
	     this, 
	     SLOT(netCharge()));
  }


  PkaPhPiDlg::~PkaPhPiDlg()
  {
    delete mpa_pkaPhPi;
    delete mpa_resultsString;
  }


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

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


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


  bool
  PkaPhPiDlg::fetchValidateInputData()
  {
    CoordinateList coordList;
    
    // If the selected sequence should be dealt-with, then get its
    // borders.
    if (m_ui.selectedSequenceRadioButton->isChecked())
      {
	bool res = mp_editorWnd->mpa_editorGraphicsView->
	  selectionIndices(&coordList);
    
	if(!res)
	  {
	    // There is no selection, set the "selection" to be the
	    // whole sequence.
	    
	    Coordinates coordinates(0, m_polymer.size() - 1);
	    m_calcOptions.setCoordinateList(coordinates);
	    
	    m_ui.wholeSequenceRadioButton->setChecked(true);
	  }
	else
	  {
	    m_calcOptions.setCoordinateList(coordList);
	  }
      }
    else
      {
	Coordinates coordinates(0, m_polymer.size() - 1);
	m_calcOptions.setCoordinateList(coordinates);
	
	m_ui.wholeSequenceRadioButton->setChecked(true);
      }
    
    m_ui.selectionCoordinatesLineEdit->setText 
     (m_calcOptions.coordinateList().positionsAsText());
    
    // Duplicate the current calcOptions from the sequence editor and
    // change the start/end indices within it. Then copy these new
    // calcOptions into the mpa_pkaPhPi.
  
    mpa_pkaPhPi->setCalcOptions(m_calcOptions);
    
    double ph = m_ui.phDoubleSpinBox->value();
    Q_ASSERT(ph > 0 && ph < 14);
  
    mpa_pkaPhPi->setPh(ph);
    
    return true;
  }


  void 
  PkaPhPiDlg::netCharge()
  {
    Application *application = static_cast<Application *>(qApp);
    QLocale locale = application->locale();

    if (!fetchValidateInputData())
      {
	QMessageBox::warning(0, 
			      tr("massXpert - pKa pH pI"),
			      tr("Failed validating input data."),
			      QMessageBox::Ok);
	return;
      }
    
    setCursor(Qt::WaitCursor);
  
    m_chemGroupsTested = -1;
  
    m_chemGroupsTested = mpa_pkaPhPi->calculateCharges();
  
    setCursor(Qt::ArrowCursor);


    if (m_chemGroupsTested == -1)
      {
	QMessageBox::warning(0, 
			      tr("massXpert - pKa pH pI"),
			      tr("Failed computing charges."),
			      QMessageBox::Ok);
	return;
      }


    // And now display the results.

    QString value;
  
    value.setNum(m_chemGroupsTested);
    m_ui.testedChemGroupsLabel->setText(value);

    value = locale.toString(mpa_pkaPhPi->positiveCharges(), 
			     'f', MXP_PH_PKA_DEC_PLACES);
    m_ui.positiveChargesLabel->setText(value);

    value = locale.toString(mpa_pkaPhPi->negativeCharges(), 
			     'f', MXP_PH_PKA_DEC_PLACES);
    m_ui.negativeChargesLabel->setText(value);

    value = locale.toString(mpa_pkaPhPi->positiveCharges() +
			     mpa_pkaPhPi->negativeCharges(), 
			     'f', MXP_PH_PKA_DEC_PLACES);
    m_ui.netChargeLabel->setText(value);
  
    m_ui.isoelectricPointLabel->setText("");

    prepareResultsTxtString(MXP_TARGET_NET_CHARGE);
  }


  void 
  PkaPhPiDlg::isoelectricPoint()
  {
    Application *application = static_cast<Application *>(qApp);
    QLocale locale = application->locale();

    if (!fetchValidateInputData())
      {
	QMessageBox::warning(0, 
			      tr("massXpert - pKa pH pI"),
			      tr("Failed validating input data."),
			      QMessageBox::Ok);
	return;
      }
    
    setCursor(Qt::WaitCursor);

    m_chemGroupsTested = -1;
  
    m_chemGroupsTested = mpa_pkaPhPi->calculatePi();

    setCursor(Qt::ArrowCursor);
  
    if (m_chemGroupsTested == -1)
      {
	QMessageBox::warning(0, 
			      tr("massXpert - pKa pH pI"),
			      tr("Failed computing charges."),
			      QMessageBox::Ok);
	return;
      }
  
    // And now display the results.

    QString value;
  
    value.setNum(m_chemGroupsTested);
    m_ui.testedChemGroupsLabel->setText(value);
  
    m_ui.positiveChargesLabel->setText("");
  
    m_ui.negativeChargesLabel->setText("");
  
    m_ui.netChargeLabel->setText("");

    value = locale.toString(mpa_pkaPhPi->pi(), 'f', MXP_PH_PKA_DEC_PLACES);
    m_ui.isoelectricPointLabel->setText(value);
    
    prepareResultsTxtString(MXP_TARGET_PI);
  }


  // The results-exporting functions. ////////////////////////////////
  // The results-exporting functions. ////////////////////////////////
  // The results-exporting functions. ////////////////////////////////
  void
  PkaPhPiDlg::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
  PkaPhPiDlg::prepareResultsTxtString(int target)
  {
    Application *application = static_cast<Application *>(qApp);
    QLocale locale = application->locale();

    mpa_resultsString->clear();
  
    *mpa_resultsString += QObject::tr("# \n"
                                      "# ----------------------------\n"
                                      "# pKa - pH - pI Calculations: \n"
                                      "# ----------------------------\n");
    
    bool entities =(m_calcOptions.monomerEntities() &
		     MXT_MONOMER_CHEMENT_MODIF);
  
    QString *sequence = m_polymer.monomerText(m_startIndex,
					       m_endIndex,
					       entities);
    
    *mpa_resultsString += 
      QObject::tr("Start position: %1 - End position: %2 - Sequence: %3\n\n")
      .arg(m_startIndex + 1)
      .arg(m_endIndex + 1)
      .arg(*sequence);
  
    delete sequence;
  
    if (entities)
      *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
    entities =(m_calcOptions.polymerEntities() &
		MXT_POLYMER_CHEMENT_LEFT_END_MODIF ||
		m_calcOptions.polymerEntities() &
		MXT_POLYMER_CHEMENT_RIGHT_END_MODIF);
      
    if (!entities)
      {
	*mpa_resultsString += QObject::tr("Account ends' modifs: no\n");
      }
    else
      {
	*mpa_resultsString += QObject::tr("Account ends' modifs: yes - ");
	  
	// Left end modif
	entities =(m_calcOptions.polymerEntities() &
		    MXT_POLYMER_CHEMENT_LEFT_END_MODIF);
	if(entities)
	  {
	    *mpa_resultsString += QObject::tr("Left end modif: %1 - ")
	      .arg(m_polymer.leftEndModif().name());
	  }
	  
	// Right end modif
	entities =(m_calcOptions.polymerEntities() &
		    MXT_POLYMER_CHEMENT_RIGHT_END_MODIF);
	if(entities)
	  {
	    *mpa_resultsString += QObject::tr("Right end modif: %1")
	      .arg(m_polymer.leftEndModif().name());
	  }
      }
  
  
    if (target == MXP_TARGET_PI)
      {
	QString value = 
	  locale.toString(mpa_pkaPhPi->pi(), 'f', MXP_PH_PKA_DEC_PLACES);
      
	*mpa_resultsString += QObject::tr("\npI Calculation:\n"
					   "---------------\n"
					   "pI value: %1 - Chemical groups "
					   "tested: %2\n\n")
	  .arg(value)
	  .arg(m_chemGroupsTested);
      }
    else if (target == MXP_TARGET_NET_CHARGE)
      {
	QString value
	  = locale.toString(mpa_pkaPhPi->ph(), 'f', MXP_PH_PKA_DEC_PLACES);

	*mpa_resultsString += QObject::tr("\nNet Charge Calculation:\n"
					   "-----------------------\n"
					   "At pH value: %1\n"
					   "Chemical groups tested: %2\n")
	  .arg(value)
	  .arg(m_chemGroupsTested);
      
      
	value = locale.toString(mpa_pkaPhPi->positiveCharges(), 
				 'f', MXP_PH_PKA_DEC_PLACES);
	*mpa_resultsString += QObject::tr("Positive: %1 - ")
	  .arg(value);
      
	value = locale.toString(mpa_pkaPhPi->negativeCharges(), 
				 'f', MXP_PH_PKA_DEC_PLACES);
	*mpa_resultsString += QObject::tr("Negative:  %1 - ")
	  .arg(value);
	
	value = locale.toString(mpa_pkaPhPi->positiveCharges() +
				 mpa_pkaPhPi->negativeCharges(), 
				 'f', MXP_PH_PKA_DEC_PLACES);
	*mpa_resultsString += QObject::tr("Net:  %1\n\n")
	  .arg(value);
      }
    else
      Q_ASSERT(0);
  }


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

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


  bool 
  PkaPhPiDlg::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 
  PkaPhPiDlg::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
