/* BEGIN software license
 *
 * msXpertSuite - mass spectrometry software suite
 * -----------------------------------------------
 * Copyright(C) 2009,...,2018 Filippo Rusconi
 *
 * http://www.msxpertsuite.org
 *
 * This file is part of the msXpertSuite project.
 *
 * The msXpertSuite project is the successor of the massXpert project. This
 * project now includes various independent modules:
 *
 * - massXpert, model polymer chemistries and simulate mass spectrometric data;
 * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner;
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 program. If not, see <http://www.gnu.org/licenses/>.
 *
 * END software license
 */


/////////////////////// Qt includes
#include <QObject>
#include <QDebug>
#include <QDialog>
#include <QWidget>
#include <QDir>
#include <QFileDialog>
#include <QMessageBox>
#include <QDate>
#include <QSettings>
#include <QStatusBar>


/////////////////////// Local includes
#include <minexpert/gui/SqlMassDataSlicerWnd.hpp>
#include <minexpert/nongui/SqlMassDataSlicer.hpp>


namespace msXpSmineXpert
{

/*/js/ Class: SqlMassDataSlicerWnd
 *
 * <comment>This class cannot be instantiated with the new operator. The only
 * object of this class that is published to the scripting environment is
 * named ``sqlMassDataSlicerWnd''.
 *
 * This class can only be used safely if it is first activated by using the
 * exportData() function of the <PlotWidget> classes (see <PlotWidget>).
 * Indeed, the <PlotWidget>.exportData() activates this sqlMassDataSlicerWnd
 * object and initializes it with the current data range displayed in the
 * widget.
 *
 * The following describes the various methods that can be used to automate
 * fully the slicing of very large mass spectrometry data files in a set of
 * smaller files. There are three ways to create new files of a reduced size
 * with respect to the initial mass data file:
 *
 * - export data from the original file in a single file but with a smaller
 *   set of data. For example, export only part of a TIC chromatogram or
 *   export only the mass data corresponding to a given drift time range.
 * - export data from the original file in multiple smaller files (either the
 *   whole initial data range or a smaller range). Two possibilities are
 *   offered:
 *
 *    - define the number of slices (their size is dynamically computed);
 *    - define the size of the slices (their number is dynamically computed);
 *
 * This window is available for any <PlotWidget> displaying TIC/XIC
 * chromatogram data or drift spectrum data. It is therefore essential to
 * start a data export process from that specific plot widget of interest by
 * calling <PlotWidget>.exportData(). This call will initialize all the
 * widget-specific data into this data export configuration window.
 *
 * Ex:
 * mainWnd.openMassSpectrometryFile("/path/to/file/20161027-cgb-wt-mobility.db",
 * false);
 * // The TIC chromatogram window displays the TIC chromatogram in
 * ticChromPlotWidget0
 *
 * // Activate and initialize the SqlMassDataSlicerWnd (its object name is
 * sqlMassDataSlicerWnd)
 * ticChromPlotWidget0.exportData();
 *
 *
 * // Set the number of slices to 2
 * sqlMassDataSlicerWnd.setSliceCount(2);
 *
 * // Set the start of the range of interest (retention time)
 * sqlMassDataSlicerWnd.rangeStart(0.2);
 *
 * // Set the end of the range of interest (retention time)
 * sqlMassDataSlicerWnd.rangeEnd(12.7);
 *
 * // Set the format string to use to craft the file names
 * sqlMassDataSlicerWnd.setFormatString("%f-export-slice-%n-of-%c-range_%s-%e.db");
 *
 * // Validate the configuration settings (crafts the file names according to
 * the configuration of the data export and according to the format string)
 *
 * sqlMassDataSlicerWnd.validate(false);
 *
 * // Take a peek at the various file names that have been crafted.
 * sqlMassDataSlicerWnd.reviewFileNames();
 *
 * // If the file names have no error, then start the data export process.
 * sqlMassDataSlicerWnd.confirm();
 *
 * Note that the calls to validate() and reviewFilenames() and confirm() can
 * be shortened to a single call to run() if it is expected that no error will
 * occur upon validation of the data export parameters (typically when the
 * script has been thoroughly tested). In that case, the script runs perfectly
 * unattended.</comment>
 */


//! Construct an initialized SqlMassDataSlicerWnd instance.
/*!

  \param parent parent widget.

  \param slicer pointer to the SqlMassDataSlicer instance to be used for the
  slicing operation.

  \param applicationName name of the application to be displayed in the window
  title.

*/
SqlMassDataSlicerWnd::SqlMassDataSlicerWnd(QWidget *parent,
                                           const QString &applicationName)
  : QMainWindow{parent}, m_applicationName{applicationName}
{
  m_ui.setupUi(this);

  // This window might be used by more than only one application, set its
  // name properly.
  setWindowTitle(QString("%1 - Data slicer options").arg(m_applicationName));

  // The review groupbox is hidden when the dialog window is created, it is
  // only shown if necessary.

  m_ui.reviewGroupBox->setVisible(false);

  // The file names text edit is infact read only, it is only used for
  // checking

  m_ui.fileNamesPlainTextEdit->setReadOnly(true);

  m_ui.rangeStartDoubleSpinBox->setMaximum(std::numeric_limits<double>::max());
  m_ui.rangeStartDoubleSpinBox->setMinimum(-std::numeric_limits<double>::max());

  m_ui.rangeEndDoubleSpinBox->setMaximum(std::numeric_limits<double>::max());
  m_ui.rangeEndDoubleSpinBox->setMinimum(-std::numeric_limits<double>::max());

  // Make the connections

  // The slicer emits a message telling what file it is exporting dat to.
  connect(&m_slicer,
          &SqlMassDataSlicer::updateSliceFileName,
          this,
          &SqlMassDataSlicerWnd::updateSliceFileName);

  connect(m_ui.destDirectoryPushButton,
          &QPushButton::clicked,
          this,
          &SqlMassDataSlicerWnd::destDirectoryPushButton);

  connect(m_ui.sliceSizeRadioButton,
          &QRadioButton::toggled,
          this,
          &SqlMassDataSlicerWnd::sliceRadioButtonToggled);

  connect(m_ui.sliceCountRadioButton,
          &QRadioButton::toggled,
          this,
          &SqlMassDataSlicerWnd::sliceRadioButtonToggled);

  connect(m_ui.validatePushButton,
          &QPushButton::clicked,
          this,
          &SqlMassDataSlicerWnd::validate);

  connect(m_ui.displayedRegionCheckBox,
          &QCheckBox::stateChanged,
          this,
          &SqlMassDataSlicerWnd::displayedRegionCheckBoxStateChanged);

  connect(m_ui.confirmPushButton,
          &QPushButton::clicked,
          this,
          &SqlMassDataSlicerWnd::confirm);


  // Restore the geometry of the window and fill in the history combo box.
  readSettings();
}


//! Destruct \c this SqlMassDataSlicerWnd instance.
SqlMassDataSlicerWnd::~SqlMassDataSlicerWnd()
{
  writeSettings();
}


//! Read the settings to restore the window geometry.
void
SqlMassDataSlicerWnd::readSettings()
{
  QSettings settings;

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

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

  bool wasVisible = settings.value("SqlMassDataSlicerWnd_visible").toBool();
  setVisible(wasVisible);
}


//! Write the settings to restore the window geometry later.
void
SqlMassDataSlicerWnd::writeSettings()
{
  QSettings settings;

  settings.setValue("splitter", m_ui.splitter->saveState());
  settings.setValue("splitter_2", m_ui.splitter_2->saveState());
  settings.setValue("splitter_3", m_ui.splitter_3->saveState());

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

  settings.setValue("SqlMassDataSlicerWnd_visible", isVisible());
}


//! Initialize the dialog window.
void
SqlMassDataSlicerWnd::initialize(QString fileName,
                                 double rangeStart,
                                 double rangeEnd,
                                 const History &history)
{
  // Sanity check

  QFileInfo fileInfo(fileName);

  if(!fileInfo.exists())
    qFatal(
      "Fatal error at %s@%d -- %s(). "
      "The file was not found: %s"
      "Program aborted.",
      __FILE__,
      __LINE__,
      __FUNCTION__,
      fileName.toLatin1().data());

  if(!fileInfo.isAbsolute())
    qFatal(
      "Fatal error at %s@%d -- %s(). "
      "The plot widget's member m_fileName must be an absolute file path name."
      "Program aborted.",
      __FILE__,
      __LINE__,
      __FUNCTION__);


  // First initialize the SqlMassDataSlicer member
  m_slicer.initialize(fileName, rangeStart, rangeEnd, history);

  // The following call both set data to member values and update the GUI
  // widgets when applicable.
  setSrcFileName(fileName);
  setHistory(history);
  setRange(rangeStart, rangeEnd);
}


//! Make sure \c this window is shown and raised.
void
SqlMassDataSlicerWnd::activate()
{
  activateWindow();
  raise();
  show();
}

/*/js/
 * sqlMassDataSlicerWnd.setSrcFileName(fileName)
 *
 * Set the source SQLite3 db mass data file name. Only use this function if
 * you know the range of the data you are willing to export. Usually this
 * function is not used, because when starting the data export process using
 * the <PlotWidget>.exportData() function, that source file name is set
 * automatically. Thus we recommend you use this function only if you know
 * what you are doing.
 *
 * fileName : <String> that holds the name of the source mass spectrometry
 * data file in the SQLite3 database format.
 */

//! Set the name of the source file.
void
SqlMassDataSlicerWnd::setSrcFileName(const QString &fileName)
{
  // If the source filename is absolute, then put the directory in the
  // member datum.
  QFileInfo fileInfo(fileName);

  if(!fileInfo.exists())
    {
      QString msg("The data source file name is inexistent. Error.");
      qDebug() << __FILE__ << __LINE__ << msg;
      QMessageBox msgBox;
      msgBox.setText(msg);
      msgBox.setStandardButtons(QMessageBox::Ok);
      msgBox.exec();

      return;
    }

  if(!fileInfo.isAbsolute())
    {
      QString msg("The data source file name must be absolute. Error.");
      qDebug() << __FILE__ << __LINE__ << msg;
      QMessageBox msgBox;
      msgBox.setText(msg);
      msgBox.setStandardButtons(QMessageBox::Ok);
      msgBox.exec();

      return;
    }

  // Get the directory component.
  m_slicer.setDestDirectory(fileInfo.absolutePath());

  m_slicer.setSrcFileName(fileInfo.absoluteFilePath());

  m_ui.formatStringLineEdit->setText("%f-export-slice-%n-of-%c-range_%s-%e.db");
}


/*/js/
 * sqlMassDataSlicerWnd.setDestDirectory(dir)
 *
 * Set the directory (create it if it does not exist) where all the slice
 * files are to be written to. If this function is not called before calling
 * the validate() or run() functions, the destination directory will be the
 * directory where the source file is residing.
 *
 * dir: <String> holding the name of the directory in which to write all the
 * slice files.
 */

//! Set the destination directory. Create it if it does not exist.
bool
SqlMassDataSlicerWnd::setDestDirectory(QString destDir)
{
  QDir dir(destDir);

  if(!dir.exists())
    {
      if(!dir.mkpath(destDir))
        {
          QMessageBox msgBox;
          QString msg =
            QString("Directory %1 does not exist.\nFailed to create it")
              .arg(destDir);
          msgBox.setText(msg);
          msgBox.setStandardButtons(QMessageBox::Ok);
          msgBox.exec();

          return false;
        }
    }

  m_slicer.setDestDirectory(destDir);

  return true;
}


/*/js/
 * sqlMassDataSlicerWnd.setFormatString(format)
 *
 * Set the string that configures the naming of the slice files. The
 * following format specification is available:
 *
 * %f: original filename without <.extension>
 * %s: slice range start value
 * %e: slice range end value
 * %n: slice number
 * %c: total count of slices
 *
 * For example, if the original (source) filename were "my-mass-data.db"
 * and the format string were "%f-export-slice-%n-of-%c-range_%s-%e.db",
 * the first file would be named
 * "my-mass-data-export-slice-1-of-7-range_17-28.db"
 * the second file
 * "my-mass-data-export-slice-2-of-7-range_29-40.db"
 * and so on for another 5 files since the total count of slices is 7.
 */

//! Set the format string used to craft the destination file names
bool
SqlMassDataSlicerWnd::setFormatString(QString formatString)
{
  m_ui.formatStringLineEdit->setText(formatString);

  return true;
}


//! Set the user widget's History to \c this window
void
SqlMassDataSlicerWnd::setHistory(const History &history)
{
  m_history = history;
}


/*/js/
 * sqlMassDataSlicerWnd.setRangeStart(start)
 *
 * Set the start value of the data range to be exported. For example, if the
 * data export process was started in a TIC chromatogram plot widget, and the
 * whole acquisition run spanned [0-35] minutes, then the user might want to
 * only export a fraction of that span: [17-28]. In that case, the range start
 * value would be 17.
 *
 * start: <Number> setting the start value of the range to be exported.
 */

//! Set the range start value in the corresponding line edit widget.
void
SqlMassDataSlicerWnd::setRangeStart(double start)
{
  m_ui.rangeStartDoubleSpinBox->setValue(start);
}


/*/js/
 * sqlMassDataSlicerWnd.rangeStart()
 *
 * Return the start value of the data range to be exported. For example, if the
 * data export process was started in a TIC chromatogram plot widget, and the
 * whole acquisition run spanned [0-35] minutes, then the user might want to
 * only export a fraction of that span: [17-28]. In that case, the range start
 * value would be 17.
 */

//! Return the range start value in the corresponding widget
double
SqlMassDataSlicerWnd::rangeStart()
{
  return m_ui.rangeStartDoubleSpinBox->value();
}


/*/js/
 * sqlMassDataSlicerWnd.setRangeEnd(end)
 *
 * Set the end value of the data range to be exported. For example, if the
 * data export process was started in a TIC chromatogram plot widget, and the
 * whole acquisition run spanned [0-35] minutes, then the user might want to
 * only export a fraction of that span: [17-28]. In that case, the range end
 * value would be 28.
 *
 * end: <Number> setting the end value of the range to be exported.
 */

//! Set the range end value in the corresponding widget
void
SqlMassDataSlicerWnd::setRangeEnd(double end)
{
  m_ui.rangeEndDoubleSpinBox->setValue(end);
}


/*/js/
 * sqlMassDataSlicerWnd.rangeEnd()
 *
 * Return the end value of the data range to be exported. For example, if the
 * data export process was started in a TIC chromatogram plot widget, and the
 * whole acquisition run spanned [0-35] minutes, then the user might want to
 * only export a fraction of that span: [17-28]. In that case, the range end
 * value would be 28.
 */

//! Return the range end value in the corresponding widget.
double
SqlMassDataSlicerWnd::rangeEnd()
{
  return m_ui.rangeEndDoubleSpinBox->value();
}


/*/js/
 * sqlMassDataSlicerWnd.setRange(start, end)
 *
 * Set the data range to be exported. For example, if the data export process
 * was started in a TIC chromatogram plot widget, and the whole acquisition
 * run spanned [0-35] minutes, then the user might want to only export a
 * fraction of that span: [17-28]. In that case, the range start and end
 * values would be 17 and 28 respectively.
 *
 * start: <Number> setting the start value of the range to be exported.
 * end: <Number> setting the end value of the range to be exported.
 */

//! Set the range start and end values in their corresponding line edit widgets.
void
SqlMassDataSlicerWnd::setRange(double start, double end)
{
  setRangeStart(start);
  setRangeEnd(end);
}

/*/js/
 * sqlMassDataSlicerWnd.setSliceCount(count)
 *
 * Set the number of slices to be created out of the initial data set.
 *
 * count: <Number> indicating the number of slices.
 */

//! Set the number of slices to be created.
bool
SqlMassDataSlicerWnd::setSliceCount(int count)
{
  // Implicitely, the user is asking that the checkbox be unchecked since she
  // is willing to set the slice count so clearly the idea is not to export
  // the data in the visibile plot range in a single file.
  m_ui.displayedRegionCheckBox->setChecked(false);

  if(count < 1)
    return false;

  m_ui.sliceCountSpinBox->setValue(count);

  return true;
}


/*/js/
 * sqlMassDataSlicerWnd.setSliceSize(size)
 *
 * Set the size of the slices to be created out of the initial data set.
 *
 * size: <Number> indicating the size of the slices.
 */

//! Set the size of the various slices.
bool
SqlMassDataSlicerWnd::setSliceSize(double size)
{
  if(size <= 0)
    return false;

  m_ui.sliceSizeDoubleSpinBox->setValue(size);

  return true;
}


/*/js/
 * sqlMassDataSlicerWnd.setExportToOneFile()
 *
 * Tells that the initial mass data set must be exported into a single file.
 * Any [rangeStart-rangeEnd] export range that might have been set is taken
 * into account. Not limiting the range of the exported data makes the data
 * export operation equivalent to copying the file into another file virtually
 * identical to the initial one.
 */

//! Set the export to be destinated to a single file.
bool
SqlMassDataSlicerWnd::setExportToOneFile()
{
  m_ui.displayedRegionCheckBox->setChecked(true);

  return true;
}

/*/js/
 * sqlMassDataSlicerWnd.fileNames()
 *
 * Print the names of the files that will be written during the data export
 * process. For the list of file names to be created and be available, first set
 * the various data export options (format string, number or size of slices...)
 * and call sqlMassDataSlicerWnd.validate().
 */

//! Return a string holding all the names of the destination files.
/*!

  The names of the destination files have been crafted on the basis of the
  data set by the user in this dialog window. This function returns all the
  file names as a single string.
  */
QString
SqlMassDataSlicerWnd::fileNames()
{
  return m_slicer.allFileNamesAsString();
}


/*/js/
 * sqlMassDataSlicerWnd.reviewFileNames()
 *
 * Print the names of the files that will be written during the data export
 * process. For the list of file names to be created and available, first set
 * the various data export options (format string, number or size of slices...)
 * and call sqlMassDataSlicerWnd.validate(). In this function, the graphical
 * user interface is modified to allow the user to review the file names and
 * makes available a push button to start the export process.
 */

//! Ensure that there are names of the destination files.
/*!

  The names of the destination files have been crafted on the basis of the
  data set by the user in this dialog window. To allow the user to review
  these file names, they are printed in a text edit widget that is make
  visible at that precise time (it is invisible before this call).

*/
QString
SqlMassDataSlicerWnd::reviewFileNames()
{
  QString fileNameList = fileNames();

  if(fileNameList.isEmpty())
    {
      m_ui.reviewGroupBox->setVisible(true);
      m_ui.fileNamesPlainTextEdit->setPlainText(
        "Major error, there are no file names in the list.");
    }
  else
    {
      m_ui.reviewGroupBox->setVisible(true);
      m_ui.fileNamesPlainTextEdit->setPlainText(fileNameList);
    }

  return fileNameList;
}


/*/js/
 * sqlMassDataSlicerWnd.validate(noconfirm)
 *
 * Verify the configuration settings for the data export operation. Upon this
 * validation, the file names of the files that will be written during the data
 * export are crafted according to the file format string (see
 * setFormatString()). This function needs to be called prior to calling
 * reviewFileNames() or fileNames(). After reviewing the file names,
 * a call to the function confirm() starts the data export process.
 *
 * noconfirm: <Boolean> value telling if the data export process can proceed
 * automatically. If true, no call to confirm() needs to be done for the data
 * export to start immediately if there is no error during the validation step.
 */

//! Trigger the verification of all the data set by the user.
/*!

  The verification of the data and the crafting of the destination file names
  is followed by a call to reviewFileNames() if \p noconfirm is false,
  otherwise the \c confirm() function is automatically called.

*/
void
SqlMassDataSlicerWnd::validate(bool noconfirm)
{
  // We'll have to perform a thorough checking of all the parameters in the
  // dialog window. Then we'll have to craft all the required stuff to
  // actually perform the requested data export.

  // First, reset all the slicer members.
  m_slicer.reset();

  // First verify that the file format string is not empty.
  if(m_ui.formatStringLineEdit->text().isEmpty())
    {
      QString msg("Please set the file name format string.");
      qDebug() << __FILE__ << __LINE__ << msg;
      QMessageBox msgBox;
      msgBox.setText(msg);
      msgBox.setStandardButtons(QMessageBox::Ok);
      msgBox.exec();

      return;
    }

  m_slicer.setFormatString(m_ui.formatStringLineEdit->text());

  // The check that all the numerical values are sane.
  double start = 0;
  double end   = 0;

  // When the dialog was setup, the slicer had the proper range values set
  // from the plot widget from which the export of the data was triggered.
  // Let's get these values and set them to handy local variables.

  start = m_ui.rangeStartDoubleSpinBox->value();
  end   = m_ui.rangeEndDoubleSpinBox->value();

  // Make sure end is greater than start...
  if(start < end)
    {
      m_slicer.setRange(start, end);
    }
  else
    {
      m_slicer.setRange(end, start);
    }

  if(m_ui.displayedRegionCheckBox->isChecked())
    {

      // Export the data as displayed in the corresponding widget to a single
      // file. Note that the slicer was constructed with the range values, so
      // we have all the necessary to perform the export.

      m_slicer.setSliceType(SliceType::SLICE_ZOOMED_REGION);
    }
  else
    {

      // Perform some kind of slicing of the data to various files.

      if(m_ui.sliceSizeRadioButton->isChecked())
        {
          m_slicer.setSliceType(SliceType::SLICE_BY_SIZE);

          double sliceSize = m_ui.sliceSizeDoubleSpinBox->value();
          m_slicer.setSliceSize(sliceSize);
        }
      else
        {
          // Sanity check
          if(!m_ui.sliceCountRadioButton->isChecked())
            qFatal(
              "Fatal error at %s@%d. Program aborted.", __FILE__, __LINE__);

          m_slicer.setSliceType(SliceType::SLICE_BY_COUNT);

          int sliceCount = m_ui.sliceCountSpinBox->value();
          m_slicer.setSliceCount(sliceCount);
        }
    }

  // At this point, we have all the required data to validate the
  // destFileName string and thus potentially construct the various
  // fileNames.

  QString errors;
  if(!m_slicer.configureSpecifs(&errors))
    {
      QString msg =
        "Failed to validate the input data, please check the input\n";
      msg += errors;
      QMessageBox msgBox;
      msgBox.setText(msg);
      msgBox.setStandardButtons(QMessageBox::Ok);
      msgBox.exec();

      return;
    }

  // Show all the file names to the user so she can verify, fix and confirm.

  if(noconfirm)
    confirm();
  else
    reviewFileNames();
}


/*/js/
 * sqlMassDataSlicerWnd.confirm()
 *
 * Confirm that the data export process can start. This function must be
 * called after the validation has been performed (see validate()) and the
 * user has checked that the file names have been correcly crafted upon
 * validation according to the format string.
 *
 */
//! Confirm that the destination file names are valid.
QString
SqlMassDataSlicerWnd::confirm()
{
  // qDebug() << __FILE__ << __LINE__
  //<< "Destination file names have been confirmed.";

  // qDebug() << __FILE__ << __LINE__
  //<< "History right before slicing: " << m_history.asText();

  // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()"
  //<< "m_slicer:" << m_slicer.asText();

  //! Sqlite3 database data file handler
  MassSpecSqlite3Handler sqlHandler;

  // When the sql data handler will perform the export, it will emit
  // progression signals. Make the connecions.
  connect(&sqlHandler,
          &MassSpecSqlite3Handler::updateFeedbackSignal,
          this,
          &SqlMassDataSlicerWnd::updateFeedback);

  QString errors = m_slicer.slice(&sqlHandler);

  if(!errors.isEmpty())
    {
      QString msg =
        "An error occurred while slicing the data. Please check "
        "your configuration.";
      QMessageBox msgBox;
      msgBox.setText(msg);
      msgBox.setStandardButtons(QMessageBox::Ok);
      msgBox.exec();
    }
  else
    {
      statusBar()->showMessage("The data slicing is finished without error.\n",
                               3000);
    }

  writeSettings();

  if(errors.isEmpty())
    return QString("The data slicing finished without error.\n");

  return errors;
}


/*/js/
 * sqlMassDataSlicerWnd.run()
 *
 * After having set the range, the number (or size) of the slices, let the
 * program compute the size (or number, respectively) of the slices and run
 * the data export. This function calls validate() with noconfirm set to True
 * (see validate()). This way, if no error is encountered during the
 * validation, then the data export process is started right way, which is
 * expected for a script to run unattended.
 */
//! Validate and automatically confirm (for the scripting environment)
void
SqlMassDataSlicerWnd::run()
{
  validate(true);
}


//! React to the clicking of UI elements.
void
SqlMassDataSlicerWnd::displayedRegionCheckBoxStateChanged(int checkState)
{
  if(checkState == Qt::Checked)
    {
      m_ui.autoSliceGroupBox->setEnabled(false);
    }
  else
    {
      m_ui.autoSliceGroupBox->setEnabled(true);
    }
}


//! React to the clicking of UI elements.
void
SqlMassDataSlicerWnd::sliceRadioButtonToggled(bool checked)
{
  QRadioButton *sender = static_cast<QRadioButton *>(QObject::sender());
  if(sender == m_ui.sliceCountRadioButton)
    {
      m_ui.sliceCountSpinBox->setEnabled(checked);
      m_ui.sliceSizeDoubleSpinBox->setEnabled(!checked);
    }
  else if(sender == m_ui.sliceSizeRadioButton)
    {
      m_ui.sliceSizeDoubleSpinBox->setEnabled(checked);
      m_ui.sliceCountSpinBox->setEnabled(!checked);
    }
}


//! Update the feedback to the user.
void
SqlMassDataSlicerWnd::updateFeedback(
  QString msg, int currentValue, bool setVisible, int startValue, int endValue)
{
  m_ui.feedbackLineEdit->setText(msg);
  m_ui.feedbackProgressBar->setValue(currentValue);

  if(startValue != -1)
    m_ui.feedbackProgressBar->setMinimum(startValue);

  if(endValue != -1)
    m_ui.feedbackProgressBar->setMaximum(endValue);

  // Process the app events, to refresh the GUI.
  QApplication::processEvents();
}


//! Change the name of the destination file currently being dealt with.
void
SqlMassDataSlicerWnd::updateSliceFileName(QString fileName)
{
  m_ui.feedbackFileLineEdit->setText(fileName);
}


//! Allow the user to define the directory where the destination files should be
//! written.
void
SqlMassDataSlicerWnd::destDirectoryPushButton()
{
  // Let the user define the destination directory for all the files
  // produced by the data slicer. However, try to open a meaningful
  // directory for choosing another.

  QString openDir;

  if(m_slicer.destDirectory().isEmpty())
    openDir = QDir::homePath();
  else
    openDir = m_slicer.destDirectory();

  QString newDirectory = QFileDialog::getExistingDirectory(
    this, tr("Open Directory"), openDir, QFileDialog::ShowDirsOnly);

  // If the user escapes the dialog box, then we do not change anything.
  if(!newDirectory.isEmpty())
    m_slicer.setDestDirectory(newDirectory);
}

} // namespace msXpSmineXpert
