/* 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<QtGui>
#include<QtGlobal>
#include<QMessageBox>
#include<QInputDialog>

/////////////////////// Local includes
#include "mzLabWnd.hpp"
#include "mainWindow.hpp"
#include "application.hpp"
#include "mzLabInputOligomerTableViewDlg.hpp"
#include "mzLabOutputOligomerTableViewDlg.hpp"
#include "mzLabFormulaBasedActionsDlg.hpp"
#include "mzLabMassBasedActionsDlg.hpp"
#include "mzLabMatchBasedActionsDlg.hpp"


namespace massXpert
{

  MzLabWnd::MzLabWnd(QString &filePath)
  {
    m_forciblyClose = false;
    m_noDelistWnd = false;

    if (filePath.isEmpty())
      {
	QMessageBox::warning(0, 
			      tr("massXpert - Calculator"),
			      tr("Polymer chemistry definition "
				  "filepath empty."),
			      QMessageBox::Ok);
      
	return;
      }
  
    m_polChemDef.setFilePath(filePath);

    if (!initialize())
      {
	QMessageBox::warning(0, 
			      tr("massXpert - Calculator"),
			      tr("Failed to initialize the mz lab window."),
			      QMessageBox::Ok);
      }

    setWindowTitle(tr("%1 %2[*]")
		    .arg(tr("massXpert: mz Lab - "))
		    .arg(m_polChemDef.name()));
    
    QSettings settings 
     (static_cast<Application *>(qApp)->configSettingsFilePath(), 
       QSettings::IniFormat);
    
    settings.beginGroup("mz_lab_wnd");

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

    settings.endGroup();

    show();
  }
    

  MzLabWnd::~MzLabWnd()
  {
    while(m_dlgList.size())
      delete m_dlgList.takeFirst();
  }
  

  void 
  MzLabWnd::closeEvent(QCloseEvent *event)
  {
    Application *application = static_cast<Application *>(qApp);

    // We are asked to close the window even if it has unsaved data.
    if (m_forciblyClose)
      {
	m_forciblyClose = false;
      
	// We have to delist the window.
	if(!m_noDelistWnd)
	  {
	    m_noDelistWnd = false;
	    int index = application->mzLabWndList()->indexOf(this);
	  
	    application->mzLabWndList()->takeAt(index);
	  }
      
	writeSettings();
	
	event->accept();
      
	return;
      }

    if (maybeSave())
      {
	// We are asked not to remove this window from the list of all
	// the mz lab windows. This occurs when all the windows are
	// closed in a raw in the application object.

	if(m_noDelistWnd)
	  {
	    m_noDelistWnd = false;
	  
	    writeSettings();

	    event->accept();
	  
	    return;
	  }
      
	// We must remove this window from the application's list of
	// such windows:

	int index = application->mzLabWndList()->indexOf(this);
      
	application->mzLabWndList()->takeAt(index);
  
	writeSettings();
      
	event->accept();
      }
    else 
      {
	event->ignore();
      }
  }
  
  
  bool
  MzLabWnd::initialize()
  {
    m_ui.setupUi(this);
    
    QPixmap pixmap(":/images/massxpert-icon-32.png");
    QIcon icon(pixmap);
    setWindowIcon(icon);
  
    setAttribute(Qt::WA_DeleteOnClose);
    statusBar()->setSizeGripEnabled(true);

    ////// Connection of the SIGNALS and SLOTS //////

    connect(m_ui.newInputListPushButton,
	     SIGNAL(clicked()),
	     this,
	     SLOT(newInputList()));

    connect(m_ui.catalogue1ListWidget,
	     SIGNAL(itemClicked(QListWidgetItem *)),
	     this,
	     SLOT(inputListWidgetItemClicked(QListWidgetItem *)));
    
    connect(m_ui.catalogue2ListWidget,
	     SIGNAL(itemClicked(QListWidgetItem *)),
	     this,
	     SLOT(inputListWidgetItemClicked(QListWidgetItem *)));

    connect(m_ui.deleteInputListPushButton,
	     SIGNAL(clicked()),
	     this,
	     SLOT(deleteInputListItem()));

    // We need to update m_ionizeRule each time one of the values is
    // changed.

    connect(m_ui.ionizationChargeSpinBox,
            SIGNAL(valueChanged(int)),
            this,
            SLOT(ionizationChargeChanged(int)));

    connect(m_ui.ionizationLevelSpinBox,
            SIGNAL(valueChanged(int)),
            this,
            SLOT(ionizationLevelChanged(int)));
    
    connect(m_ui.ionizationFormulaLineEdit,
            SIGNAL(textChanged(const QString &)),
            this,
            SLOT(ionizationFormulaChanged(const QString &)));
    
    // The dialog actions that might be triggered within the lab are
    // called _via_ as set of buttons:

    connect(m_ui.formulaBasedActionsPushButton,
            SIGNAL(clicked()),
            this,
            SLOT(formulaBasedActionsPushButton()));

    connect(m_ui.massBasedActionsPushButton,
            SIGNAL(clicked()),
            this,
            SLOT(massBasedActionsPushButton()));

    connect(m_ui.matchBasedActionsPushButton,
            SIGNAL(clicked()),
            this,
            SLOT(matchBasedActionsPushButton()));


    if (!m_polChemDef.renderXmlPolChemDefFile())
      return false;

    m_ui.ionizationChargeSpinBox->setRange(1, 1000000000);
    m_ui.ionizationLevelSpinBox->setRange(0, 1000000000);

    m_ionizeRule = m_polChemDef.ionizeRule();
    
    m_ui.ionizationFormulaLineEdit->setText(m_ionizeRule.formula());
    m_ui.ionizationChargeSpinBox->setValue(m_ionizeRule.charge());
    m_ui.ionizationLevelSpinBox->setValue(m_ionizeRule.level());
    
    return true;
  }
 

  PolChemDef *
  MzLabWnd::polChemDef()
  {
    return &m_polChemDef;
  }
  
  
  const IonizeRule &
  MzLabWnd::ionizeRule() const
  {
    return m_ionizeRule;
  }
  
  
  void 
  MzLabWnd::writeSettings()
  {
    QSettings settings 
     (static_cast<Application *>(qApp)->configSettingsFilePath(), 
       QSettings::IniFormat);
    
    settings.beginGroup("mz_lab_wnd");
    
    settings.setValue("geometry", saveGeometry());
    
    settings.endGroup();
  }
  

  void
  MzLabWnd::updateWindowTitle()
  {
    setWindowTitle(tr("%1 %2[*]")
		    .arg(tr("massXpert: mz Lab - "))
		    .arg(m_polChemDef.name()));
  }
  

  void 
  MzLabWnd::ionizationChargeChanged(int value)
  {
    m_ionizeRule.setCharge(value);
  }
  
  
  void 
  MzLabWnd::ionizationLevelChanged(int value)
  {
    m_ionizeRule.setLevel(value);
  }
  
  void 
  MzLabWnd::ionizationFormulaChanged(const QString &text)
  {
    Formula formula(text);
    
    if (!formula.validate(m_polChemDef.atomList()))
      {
	QMessageBox::warning(this,
                             tr("massXpert: mz Lab"),
                             tr("%1@%2\n"
                                "The formula '%3' is not valid.")
                             .arg(__FILE__)
                             .arg(__LINE__)
                             .arg(formula.text()));
        
        m_ui.ionizationFormulaLineEdit->setText("");
        
	return;
      }
    
    m_ionizeRule.setFormula(text);
  }
  
  
  void 
  MzLabWnd::formulaBasedActionsPushButton()
  {
    MzLabFormulaBasedActionsDlg  *dlg = new MzLabFormulaBasedActionsDlg(this);
    dlg->show();
  }
  
  
  void 
  MzLabWnd::massBasedActionsPushButton()
  {
    MzLabMassBasedActionsDlg  *dlg = new MzLabMassBasedActionsDlg(this);
    dlg->show();
  }
  
  
  void 
  MzLabWnd::matchBasedActionsPushButton()
  {
    MzLabMatchBasedActionsDlg  *dlg = new MzLabMatchBasedActionsDlg(this);
    dlg->show();
  }
  
  
  bool
  MzLabWnd::inputListDlg(MzLabInputOligomerTableViewDlg **dlg)
  {
    QListWidgetItem *item = 0;
    
    // Which list of lists is selected ?
    
    if (m_ui.catalogue1RadioButton->isChecked())
      item = m_ui.catalogue1ListWidget->currentItem();
    else
      item = m_ui.catalogue2ListWidget->currentItem();
    
    if (!item)
      return false;
    
    // Get the name of the dialog window represented by the item and
    // find the dialog window itself.
    
    QString text = item->text();
    
    MzLabInputOligomerTableViewDlg *dialog = findDlg(text);
    Q_ASSERT(dialog);
    
    *dlg = dialog;
    
    return true;
  }
  
  
 bool
 MzLabWnd::inputListsDlg(MzLabInputOligomerTableViewDlg **dlg1,
			  MzLabInputOligomerTableViewDlg **dlg2)
  {
    QListWidgetItem *item1 = 0;
    QListWidgetItem *item2 = 0;

    // Which lists of lists is selected ?
    
    item1 = m_ui.catalogue1ListWidget->currentItem();
    item2 = m_ui.catalogue2ListWidget->currentItem();
    
    if (!item1 || !item2)
      return false;
        
    // Get the name of the dialog window represented by the two items
    // and find the corresponding dialog windows.
    
    QString text = item1->text();
    
    MzLabInputOligomerTableViewDlg *dialog = findDlg(text);
    Q_ASSERT(dialog);
    
    *dlg1 = dialog;

    text = item2->text();
    
    dialog = 0;
    
    dialog = findDlg(text);
    Q_ASSERT(dialog);
    
    *dlg2 = dialog;
    
    return true;
  }
  

  MzLabInputOligomerTableViewDlg *
  MzLabWnd::inputListDlg()
  {
    MzLabInputOligomerTableViewDlg *dlg = 0;
    
    if (!inputListDlg(&dlg))
      {
        return 0;
      }
    
    return dlg;
  }
  

  bool
  MzLabWnd::inPlaceCalculation()
  {
    bool inPlace = m_ui.inPlaceCalculationCheckBox->isChecked();
    return inPlace;
  }
  
  
  MzLabInputOligomerTableViewDlg *
  MzLabWnd::newInputList()
  {
    return newInputList(QString(), MXT_MASS_NONE, false);
  }
  
  MzLabInputOligomerTableViewDlg *
  MzLabWnd::newInputList(QString name, MassType massType, bool isFragment)
  {
    // Ask that the user give a name to the list.
    
    bool ok;
    
    QString localName = name;
    
    if(localName.isEmpty())
      {
        // Each dialog must have a name. So ask for one.
        localName = 
          QInputDialog::getText(this, 
                                tr("Give a name to the new input list"),
                                tr("List name:"), 
                                QLineEdit::Normal,
                                tr("New input list name"), &ok);
        
        if (localName.isEmpty() || !ok)
          return 0;
      }
    else
      {
        // The name is already set, only ask the user to confirm it.
        
        localName = 
          QInputDialog::getText(this, 
                                tr("Confirm the name of the new input list"),
                                tr("List name:"), 
                                QLineEdit::Normal,
                                localName, &ok);
        
        if (localName.isEmpty() || !ok)
          return 0;
      }
    


    // Make sure the name is not already taken.
    if (findDlg(localName))
      {
	QMessageBox::warning(this,
                             tr("massXpert: mz Lab"),
                             tr("%1@%2\n"
                                "The '%3' name is already taken.")
                             .arg(__FILE__)
                             .arg(__LINE__)
                             .arg(localName),
                             QMessageBox::Ok);
	return 0;
      }
    
    MzLabInputOligomerTableViewDlg *dlg = 
      new MzLabInputOligomerTableViewDlg(this, localName, massType, isFragment);
    
    dlg->show();
    
    // Now we can add the item to the lists of input...

    m_ui.catalogue1ListWidget->addItem(localName);
    m_ui.catalogue2ListWidget->addItem(localName);

    // Finally we can add the list to the list of dialog windows.
    
    m_dlgList.append(dlg);

    setWindowModified(true);
    
    return dlg;
  }
  

  MzLabInputOligomerTableViewDlg *
  MzLabWnd::findDlg(const QString &name)
  {
    for (int iter = 0; iter < m_dlgList.size(); ++iter)
      {
	MzLabInputOligomerTableViewDlg *dlg = m_dlgList.at(iter);
	
	if(dlg->name() == name)
	  return dlg;
      }
    
    return 0;
  }
  

  void
  MzLabWnd::inputListWidgetItemClicked(QListWidgetItem *item)
  {
    QString text = item->text();
    
    MzLabInputOligomerTableViewDlg *dlg = findDlg(text);
    Q_ASSERT(dlg);
    
    dlg->show();
  }


  void
  MzLabWnd::deleteInputListItem()
  {
    // Do this for input 1 list only.

    QListWidgetItem *item = m_ui.catalogue1ListWidget->currentItem();
    if (!item)
      return ;

    // Get the name of the dialog window represented by the item and
    // find the dialog window itself.

    QString text = item->text();
    
    // Destroy the dialog window and update the 2 lists contents.
    destroyDlg(text);

    // If the last dialog window is destroyed, then the lab is not
    // modified anymore.
    if (!m_dlgList.size())
      setWindowModified(false);
  }
  


  void
  MzLabWnd::destroyDlg(const QString &name)
  {
    MzLabInputOligomerTableViewDlg *dlg = findDlg(name);
    Q_ASSERT(dlg);

    // Remove the dialog window from the list of dialog windows and
    // delete it.
#ifdef DEBUG
    int result = m_dlgList.removeAll(dlg);
    Q_ASSERT(result);
#else
    m_dlgList.removeAll(dlg);
#endif
    delete(dlg);

    // Update the listWidgets(1 and 2) corresponding items.
    
    QList<QListWidgetItem *> list = 
      m_ui.catalogue1ListWidget->findItems(name,
					Qt::MatchExactly);
    Q_ASSERT(list.size() == 1);
    
    QListWidgetItem *item = list.first();
    
    m_ui.catalogue1ListWidget->takeItem(m_ui.catalogue1ListWidget->row(item));
    
    if (item)
      delete(item);

    list = m_ui.catalogue2ListWidget->findItems(name,
					     Qt::MatchExactly);
    Q_ASSERT(list.size() == 1);
    
    item = list.first();
    
    m_ui.catalogue2ListWidget->takeItem(m_ui.catalogue2ListWidget->row(item));
    
    if (item)
      delete(item);
  }
  
  
  bool 
  MzLabWnd::maybeSave()
  {
    // Returns true if we can continue(either saved ok or discard). If
    // save failed or cancel we return false to indicate to the caller
    // that something is wrong.

    if (isWindowModified())
      {
	QMessageBox::StandardButton ret;
	ret = QMessageBox::warning 
	 (this, tr("massXpert - mz Lab"),
	   tr("The mz Lab has been modified.\n"
	       "OK to exit?"),
	   QMessageBox::Yes | QMessageBox::No);
      
	if(ret == QMessageBox::Yes)
	  return true;
	else
	  return false;
      }
    
    return true;
  }


} // namespace massXpert
