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


/////////////////////// Local includes
#include "config.h"
#include "application.hpp"
#include "polChemDefCatParser.hpp"
#include "polChemDefWnd.hpp"
#include "calculatorWnd.hpp"
#include "configurationSettingsDlg.hpp"
#include "sequenceEditorWnd.hpp"

namespace massXpert
{

  Application::Application(int &argc, char **argv)
    : QApplication(argc, argv)
  {
    QPixmap splashPixmap(":/images/splashscreen.png");

    mpa_splash = new QSplashScreen(splashPixmap, Qt::WindowStaysOnTopHint);
    mpa_splash->show();

    QTimer::singleShot(2000, this, SLOT(destroySplash()));


    // We now have to perform a number of initialization steps.

    QCoreApplication::setOrganizationName("massXpert");
    QCoreApplication::setOrganizationDomain("massxpert.org");
    QCoreApplication::setApplicationName("massXpert");

    mp_lastFocusedWnd = 0;

    QPixmap iconPixmap(":/images/massxpert-icon-32.png");
    QIcon icon(iconPixmap);
    setWindowIcon(icon);

    // Initialize the user specifications.
    m_userSpec.setUserName();

    m_configSettingsFilePath = QString(QDir::homePath() +
                                       QDir::separator() +
                                       ".massxpert" +
                                       QDir::separator() +
                                       "configSettings.ini");

    // Initialize the configuration directories.
    int result = -1;

    if (!m_configSettings.initializeSystemConfig())
      {
        // We still have a chance to read the settings from the
        // configuration settings file for current user in
        // $HOME/.massxpert/configSettings.ini.

        result = systemConfigReadFileSettings();

        if(!result)
          result = systemConfigManuallySetSettings();

        if(!result)
          {
            QMessageBox::critical(0, tr("massXpert - Configuration Settings"),
                                  tr("%1@%2\n"
                                     "Failed to configure massXpert.\n"
                                     "The software will not work as "
                                     "intended.")
                                  .arg(__FILE__)
                                  .arg(__LINE__),
                                  QMessageBox::Ok);
          }
      }

    if (!m_configSettings.initializeUserConfig())
      {
        qDebug() << __FILE__ << __LINE__
                 << "User configuration settings result" << result;
      }

    initializeDecimalPlacesOptions();

    PolChemDefCatParser parser;
    parser.setConfigSysUser(POLCHEMDEF_CAT_PARSE_BOTH_CONFIG);
    parser.setPendingMode(POLCHEMDEF_CAT_PARSE_APPEND_CONFIG);

    if (parser.parseFiles(m_configSettings, &m_polChemDefCatList) == -1)
      {
        QMessageBox::critical(0, tr("massXpert - Configuration Settings"),
                              tr("%1@%2\n"
                                 "Failed to parse the polymer chemistry "
                                 "definition catalogues.\n"
                                 "The software will not work as intended.")
                              .arg(__FILE__)
                              .arg(__LINE__),
                              QMessageBox::Ok);
      }

  }


  Application::~Application()
  {
    // List of all the polymer chemistry definition specifications that
    // were found in the different catalogue files on the system.
    while(! m_polChemDefCatList.isEmpty())
      delete m_polChemDefCatList.takeFirst();
  }


  const QLocale &
  Application::locale()
  {
    return m_locale;
  }

  UserSpec *
  Application::userSpec()
  {
    return &m_userSpec;
  }


  ConfigSettings *
  Application::configSettings()
  {
    return &m_configSettings;
  }


  bool
  Application::systemConfigManuallySetSettings()
  {
    // Apparently, the system configuration directory was not found to
    // be of a canonical structure. This might be due to the
    // relocation of the package. Let the user tell where the package
    // is located.
    ConfigurationSettingsDlg dlg(&m_configSettings);

    int result = dlg.exec();
    if (result)
      return true;
    else
      return false;
  }


  bool
  Application::systemConfigReadFileSettings()
  {
    QSettings settings(m_configSettingsFilePath,
                       QSettings::IniFormat);

    settings.beginGroup("system_data_config_settings");

    QDir dir;


    dir.setPath(settings.value("system_data_dir").toString());

    if (!dir.exists())
      return false;
    else
      m_configSettings.setSystemDataDir(dir.absolutePath());


    dir.setPath(settings.value("system_pol_chem_def_cat_dir").toString());

    if (!dir.exists())
      return false;
    else
      m_configSettings.setSystemPolChemDefCatDir(dir.absolutePath());


    dir.setPath(settings.value("system_plugin_dir").toString());

    if (!dir.exists())
      return false;
    else
      m_configSettings.setSystemPluginDir(dir.absolutePath());


    dir.setPath(settings.value("system_localization_dir").toString());

    if (!dir.exists())
      return false;
    else
      m_configSettings.setSystemLocalizationDir(dir.absolutePath());

    settings.endGroup();


    return true;
  }


  void
  Application::initializeDecimalPlacesOptions()
  {
    // The decimal places should be read from the configuration
    // settings.

    QSettings settings(m_configSettingsFilePath,
                       QSettings::IniFormat);

    settings.beginGroup("decimal_places_options");

    MXP_ATOM_DEC_PLACES =
      settings.value("MXP_ATOM_DEC_PLACES", 10).toInt();
    //     qDebug() << __FILE__ << __LINE__
    // 	   << "MXP_ATOM_DEC_PLACES" << MXP_ATOM_DEC_PLACES;

    MXP_OLIGOMER_DEC_PLACES =
      settings.value("MXP_OLIGOMER_DEC_PLACES", 5).toInt();
    //     qDebug() << __FILE__ << __LINE__
    // 	   << "MXP_OLIGOMER_DEC_PLACES" << MXP_OLIGOMER_DEC_PLACES;

    MXP_POLYMER_DEC_PLACES =
      settings.value("MXP_POLYMER_DEC_PLACES", 3).toInt();
    //     qDebug() << __FILE__ << __LINE__
    // 	   << "MXP_POLYMER_DEC_PLACES" << MXP_POLYMER_DEC_PLACES;

    MXP_PH_PKA_DEC_PLACES =
      settings.value("MXP_PH_PKA_DEC_PLACES", 2).toInt();
    //     qDebug() << __FILE__ << __LINE__
    // 	   << "MXP_PH_PKA_DEC_PLACES" << MXP_PH_PKA_DEC_PLACES;

    settings.endGroup();
  }


  const QString &
  Application::configSettingsFilePath()
  {
    return m_configSettingsFilePath;
  }


  void
  Application::setLastFocusedWnd(SequenceEditorWnd *wnd)
  {
    mp_lastFocusedWnd = wnd;
    //   qDebug() << __FILE__<< __LINE__ << wnd;
  }


  QList<PolChemDefSpec *> *
  Application::polChemDefCatList()
  {
    return &m_polChemDefCatList;
  }


  QList<PolChemDef *> *
  Application::polChemDefList()
  {
    return &m_polChemDefList;
  }


  QList<PolChemDefWnd *> *
  Application::polChemDefWndList()
  {
    return &m_polChemDefWndList;
  }


  QList<CalculatorWnd *> *
  Application::calculatorWndList()
  {
    return &m_calculatorWndList;
  }


  QList<SequenceEditorWnd *> *
  Application::sequenceEditorWndList()
  {
    return &m_sequenceEditorWndList;
  }


  QList<MzLabWnd *> *
  Application::mzLabWndList()
  {
    return &m_mzLabWndList;
  }


  PolChemDefSpec *
  Application::polChemDefSpecName(const QString &name)
  {
    PolChemDefSpec *polChemDefSpec = 0;

    for (int iter = 0 ; iter < m_polChemDefCatList.size() ; ++iter)
      {
        polChemDefSpec = m_polChemDefCatList.at(iter);

        if(polChemDefSpec->name() == name)
          return polChemDefSpec;
      }

    return 0;
  }


  PolChemDefSpec *
  Application::polChemDefSpecFilePath(const QString &filePath)
  {
    PolChemDefSpec *polChemDefSpec = 0;

    for (int iter = 0 ; iter < m_polChemDefCatList.size() ; ++iter)
      {
        polChemDefSpec = m_polChemDefCatList.at(iter);

        if(polChemDefSpec->filePath() == filePath)
          return polChemDefSpec;
      }

    return 0;
  }


  PolChemDef *
  Application::polChemDefName(const QString &name)
  {
    PolChemDef *polChemDef = 0;

    for (int iter = 0 ; iter < m_polChemDefList.size() ; ++iter)
      {
        polChemDef = m_polChemDefList.at(iter);

        // 	qDebug() << __FILE__ << __LINE__
        // 		 << "iterated polChemDef:" << polChemDef->name();

        if(polChemDef->name() == name)
          return polChemDef;
      }

    return 0;
  }


  void
  Application::polChemDefCatStringList(QStringList &stringList)
  {
    PolChemDefSpec *polChemDefSpec = 0;

    for (int iter = 0 ; iter < m_polChemDefCatList.size() ; ++iter)
      {
        polChemDefSpec = m_polChemDefCatList.at(iter);

        stringList << polChemDefSpec->filePath();
      }
  }


  bool
  Application::closeAllPolChemDefWnd(bool forcibly)
  {
    PolChemDefWnd *wnd = 0;

    // Returns true is Shutdown can be performed.

    QMutableListIterator<PolChemDefWnd *>
      iterator(m_polChemDefWndList);

    while(iterator.hasNext())
      {
        wnd = iterator.next();

        if(forcibly || !wnd->isWindowModified() || wnd->maybeSave())
          {
            iterator.remove();

            // The close() function removes the window from the list of
            // windows by default, so ask that it not be removed because
            // we have done that above already.
            wnd->m_noDelistWnd = true;

            // Do not ask for a save even if the window is not saved,
            // because 'forcibly' is true.
            wnd->m_forciblyClose = true;

            wnd->close();

            continue;
          }

        continue;
      }

    // At this point, we might have windows still opened.

    if (m_polChemDefWndList.size() > 0)
      {
        QMessageBox::StandardButton ret;
        ret = QMessageBox::warning
          (0,
           tr("massXpert - Polymer chemistry definition"),
           tr("Modified polymer chemistry definition(s): %1\n"
              "Do you want to save your changes?")
           .arg(m_polChemDefWndList.size()),
           QMessageBox::Yes | QMessageBox::No);

        if(ret == QMessageBox::Yes)
          return false;

        while(!m_polChemDefWndList.isEmpty())
          {
            wnd = m_polChemDefWndList.takeFirst();

            // Forcible close(no maybeSave() call).
            wnd->m_forciblyClose = true;

            // No delisting of the window!
            wnd->m_noDelistWnd = true;
            wnd->close();
          }
      }

    return true;
  }


  bool
  Application::closeAllCalculatorWnd()
  {

    // Returns true is Shutdown can be performed.

    QMutableListIterator<CalculatorWnd *> iterator(m_calculatorWndList);

    while(iterator.hasNext())
      {
        CalculatorWnd *wnd = iterator.next();

        iterator.remove();

        // The close() function removes the window from the list of
        // windows by default, so ask that it not be removed because we
        // have done that above already.
        wnd->m_noDelistWnd = true;

        wnd->close();

        continue;
      }

    return true;
  }


  bool
  Application::isSequenceEditorWnd(SequenceEditorWnd *seqEdWnd)
  {
    // Does this pointer correspond to a sequence editor window
    // currently opened?
    SequenceEditorWnd *SequenceEditorWndPtr = 0;

    foreach(SequenceEditorWndPtr, m_sequenceEditorWndList)
      {
        if(SequenceEditorWndPtr == seqEdWnd)
          return true;
      }
    return false;
  }

  bool
  Application::closeAllSequenceEditorWnd(bool forcibly)
  {
    SequenceEditorWnd *wnd = 0;

    // Returns true is Shutdown can be performed.

    QMutableListIterator<SequenceEditorWnd *>
      iterator(m_sequenceEditorWndList);

    while(iterator.hasNext())
      {
        wnd = iterator.next();

        if(forcibly || !wnd->isWindowModified() || wnd->maybeSave())
          {
            iterator.remove();

            // The close() function removes the window from the list of
            // windows by default, so ask that it not be removed because
            // we have done that above already.
            wnd->m_noDelistWnd = true;

            // Do not ask for a save even if the window is not saved,
            // because 'forcibly' is true.
            wnd->m_forciblyClose = true;

            wnd->close();

            continue;
          }

        continue;
      }

    // At this point, we might have windows still opened.

    if (m_sequenceEditorWndList.size() > 0)
      {
        QMessageBox::StandardButton ret;
        ret = QMessageBox::warning(0, tr("massXpert - Polymer Sequence Editor"),
                                   tr("Modified sequence(s): %1\n"
                                      "Do you want to really quit?")
                                   .arg(m_sequenceEditorWndList.size()),
                                   QMessageBox::Yes | QMessageBox::No);

        if(ret == QMessageBox::No)
          return false;

        while(!m_sequenceEditorWndList.isEmpty())
          {
            wnd = m_sequenceEditorWndList.takeFirst();

            // Forcible close(no maybeSave() call).
            wnd->m_forciblyClose = true;

            // No delisting of the window!
            wnd->m_noDelistWnd = true;
            wnd->close();
          }
      }

    return true;
  }


  bool
  Application::closeAllMzLabWnd(bool forcibly)
  {
    MzLabWnd *wnd = 0;

    // Returns true is Shutdown can be performed.

    QMutableListIterator<MzLabWnd *>
      iterator(m_mzLabWndList);

    while(iterator.hasNext())
      {
        wnd = iterator.next();

        if(forcibly || !wnd->isWindowModified() || wnd->maybeSave())
          {
            iterator.remove();

            // The close() function removes the window from the list of
            // windows by default, so ask that it not be removed because
            // we have done that above already.
            wnd->m_noDelistWnd = true;

            // Do not ask for a save even if the window is not saved,
            // because 'forcibly' is true.
            wnd->m_forciblyClose = true;

            wnd->close();

            continue;
          }

        continue;
      }

    // At this point, we might have windows still opened.

    if (m_mzLabWndList.size() > 0)
      {
        QMessageBox::StandardButton ret;
        ret = QMessageBox::warning(0, tr("massXpert - mz Lab"),
                                   tr("Modified mz Lab(s): %1\n"
                                      "Close the labs?")
                                   .arg(m_mzLabWndList.size()),
                                   QMessageBox::Yes | QMessageBox::No);

        if(ret == QMessageBox::Yes)
          {
            while(!m_mzLabWndList.isEmpty())
              {
                wnd = m_mzLabWndList.takeFirst();

                // Forcible close(no maybeSave() call).
                wnd->m_forciblyClose = true;

                // No delisting of the window!
                wnd->m_noDelistWnd = true;
                wnd->close();
              }

            return true;
          }
        else //(ret == QMessageBox::No)
          return false;
      }

    return true;
  }


  void
  Application::destroySplash()
  {
    delete mpa_splash;

    mpa_splash = 0;
  }

  bool
  Application::prepareShutdown()
  {
    if (!closeAllPolChemDefWnd(false))
      return false;

    if (!closeAllCalculatorWnd())
      return false;

    if (!closeAllSequenceEditorWnd(false))
      return false;

    if (!closeAllMzLabWnd(false))
      return false;

    return true;
  }

} // namespace massXpert
