/* BEGIN software license
 *
 * msXpertSuite - mass spectrometry software suite
 * -----------------------------------------------
 * Copyright (C) 2009--2020 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
 */


/////////////////////// StdLib includes
#include <vector>

/////////////////////// OPENMP include
#include <omp.h>

/////////////////////// Qt includes
#include <QDebug>
#include <QThread>


/////////////////////// pappsomspp includes
#include <pappsomspp/processing/combiners/tracepluscombiner.h>


/////////////////////// Local includes
#include "QualifiedMassSpectrumVectorMassDataIntegrator.hpp"
#include "QualifiedMassSpectrumVectorMassDataIntegratorToMz.hpp"


int qualifiedMassSpectrumVectorMassDataIntegratorToMzMetaTypeId =
  qRegisterMetaType<
    msxps::minexpert::QualifiedMassSpectrumVectorMassDataIntegratorToMz>(
    "msxps::minexpert::QualifiedMassSpectrumVectorMassDataIntegratorToMz");

int qualifiedMassSpectrumVectorMassDataIntegratorToMzSPtrMetaTypeId =
  qRegisterMetaType<
    msxps::minexpert::QualifiedMassSpectrumVectorMassDataIntegratorToMzSPtr>(
    "msxps::minexpert::QualifiedMassSpectrumVectorMassDataIntegratorToMzSPtr");


namespace msxps
{
namespace minexpert
{

// Needed for qRegisterMetaType
QualifiedMassSpectrumVectorMassDataIntegratorToMz::
  QualifiedMassSpectrumVectorMassDataIntegratorToMz()
  : QualifiedMassSpectrumVectorMassDataIntegrator()
{
}


QualifiedMassSpectrumVectorMassDataIntegratorToMz::
  QualifiedMassSpectrumVectorMassDataIntegratorToMz(
    MsRunDataSetCstSPtr ms_run_data_set_csp,
    const ProcessingFlow &processing_flow,
    QualifiedMassSpectraVectorSPtr &qualified_mass_spectra_to_integrate_sp)
  : QualifiedMassSpectrumVectorMassDataIntegrator(
      ms_run_data_set_csp,
      processing_flow,
      qualified_mass_spectra_to_integrate_sp)
{
  m_processingFlow.setMsRunDataSetCstSPtr(mcsp_msRunDataSet);
}


// Needed for qRegisterMetaType
QualifiedMassSpectrumVectorMassDataIntegratorToMz::
  QualifiedMassSpectrumVectorMassDataIntegratorToMz(
    const QualifiedMassSpectrumVectorMassDataIntegratorToMz &other)
  : QualifiedMassSpectrumVectorMassDataIntegrator(other)
{
}


QualifiedMassSpectrumVectorMassDataIntegratorToMz::
  ~QualifiedMassSpectrumVectorMassDataIntegratorToMz()
{
}


void
QualifiedMassSpectrumVectorMassDataIntegratorToMz::integrateToMz()
{
  // qDebug();

  std::chrono::system_clock::time_point chrono_start_time =
    std::chrono::system_clock::now();

  if(!mcsp_qualifiedMassSpectraToIntegrateVector->size())
    {
      qDebug() << "The qualified mass spectrum vector is empty, nothing to do.";

      emit cancelOperationSignal();
    }

  // Nothing special to do about processing flow info because that must have
  // been set before calling this function.

  // We need to clear the map trace!
  m_mapTrace.clear();

  if(m_processingFlow.getDefaultMzIntegrationParams().getBinningType() ==
     pappso::BinningType::NONE)
    {
      integrateToMzNoBinning();
    }
  else if(m_processingFlow.getDefaultMzIntegrationParams().getBinningType() ==
          pappso::BinningType::ARBITRARY)
    {
      integrateToMzArbitraryBinning();
    }

  std::chrono::system_clock::time_point chrono_end_time =
    std::chrono::system_clock::now();

  QString chrono_string = pappso::Utils::chronoIntervalDebugString(
    "Integration to mass spectrum took:", chrono_start_time, chrono_end_time);

  emit logTextToConsoleSignal(chrono_string);

  // qDebug().noquote() << chrono_string;

  return;
}


bool
QualifiedMassSpectrumVectorMassDataIntegratorToMz::integrateToMzNoBinning()
{
  // qDebug();

  emit setTaskDescriptionTextSignal("Integrating to a mass spectrum");

  // We need a processing flow to work, and, in particular, a processing step
  // from which to find the specifics of the calculation.

  if(!m_processingFlow.size())
    qFatal("The processing flow cannot be empty. Program aborted.");

  // qDebug().noquote() << "The processing flow has" << m_processingFlow.size()
  //<< "steps and is:" << m_processingFlow.toString();

  // Get the most recent step that holds all the specifics of this integration.
  const ProcessingStep *most_recent_processing_step_p =
    m_processingFlow.mostRecentStep();

  // There must be at least one ProcessingSpec (and thus a type) in the
  // processing step.

  if(!most_recent_processing_step_p->processingTypes().size())
    qFatal("The processing step cannot be empty. Program aborted.");

  // We are integrating to m/z so we need a spec description that matches
  // "ANY_TO_MZ".

  if(!most_recent_processing_step_p->matches("ANY_TO_MZ"))
    qFatal("There should be one ProcessingType = ANY_TO_MZ. Program aborted.");

  // At this point, allocate a visitor that is specific for the calculation of
  // the mass spectrum. Since we are not binning, we can use a visitor that
  // integrates to a pappso::Trace, not a pappso::MassSpectrum.

  // But we want to parallelize the computation. Se we will allocate a vector of
  // visitors (as many as possible for the available processor threads), each
  // visitor having a subset of the initial data to integrate.

  // Determine the best number of threads to use and how many spectra to provide
  // each of the threads.

  // In the pair below, first is the ideal number of threads and second is the
  // number of mass spectra per thread.
  std::pair<std::size_t, std::size_t> best_parallel_params =
    bestParallelIntegrationParams(
      mcsp_qualifiedMassSpectraToIntegrateVector->size());

  // Handy alias.
  using MassSpecVector = std::vector<pappso::QualifiedMassSpectrumCstSPtr>;
  using VectorIterator = MassSpecVector::const_iterator;

  // Prepare a set of iterators that will distribute all the mass spectra
  // uniformly.
  std::vector<std::pair<VectorIterator, VectorIterator>> iterators =
    calculateIteratorPairs(best_parallel_params.first,
                           best_parallel_params.second);

  // Set aside a vector pappso::MapTrace shared pointers that will be getting
  // the result of the integrations. There will be one such instance per thread
  // executing.

  std::vector<pappso::MapTraceSPtr> vector_of_map_trace;
  for(std::size_t iter = 0; iter < iterators.size(); ++iter)
    vector_of_map_trace.push_back(std::make_shared<pappso::MapTrace>());

  // Now that we have all the parallel material, we can start the parallel work.
  emit setProgressBarMaxValueSignal(
    mcsp_qualifiedMassSpectraToIntegrateVector->size());

  omp_set_num_threads(iterators.size());
#pragma omp parallel for ordered
  for(std::size_t iter = 0; iter < iterators.size(); ++iter)
    {
      // qDebug() << "thread index:" << iter;

      // Get the iterator range that we need to deal in this thread.
      VectorIterator current_iterator = iterators.at(iter).first;
      VectorIterator end_iterator     = iterators.at(iter).second;

      // Get the map for this thread.

      pappso::MapTraceSPtr current_map_trace_sp = vector_of_map_trace.at(iter);

      // Allocate the combiner because we'll need it for the checking of the
      // steps (will set eventual processing to the Trace if there are
      // start-end ranges for the mz values, for example with
      // setFilterResampleKeepXRange.

      pappso::TracePlusCombiner trace_plus_combiner;

      trace_plus_combiner.setDecimalPlaces(
        m_processingFlow.getDefaultMzIntegrationParams().getDecimalPlaces());

      // We need to inform the combiner if we have to limit the mz range
      // for the combinations.

      // These values are set in the in the base class constuctor.

      if(m_mzRange.first != std::numeric_limits<double>::max() &&
         m_mzRange.second != std::numeric_limits<double>::min())
        {
          //qDebug() << "The m_mzRange.first:" << m_mzRange.first
                   //<< "and m_mzRange.second:" << m_mzRange.second;

          trace_plus_combiner.setFilterResampleKeepXRange(
            pappso::FilterResampleKeepXRange(m_mzRange.first,
                                             m_mzRange.second));
        }
      //else
        //qDebug() << "There is no limitation on the m/z range.";

      while(current_iterator != end_iterator)
        {
          // We want to be able to intercept any cancellation of the
          // operation.

          if(m_isOperationCancelled)
            break;

          // Make the computation.

          pappso::QualifiedMassSpectrumCstSPtr qualified_mass_spectrum_csp =
            *current_iterator;

          // Ensure that the qualified mass spectrum contains a mass spectrum
          // that effectively contains the binary (mz,i) data.

          qualified_mass_spectrum_csp =
            checkQualifiedMassSpectrum(qualified_mass_spectrum_csp);

          // Great, at this point we actually have the mass spectrum!

          // We now need to ensure that the processing flow allows for this mass
          // spectrum to be combined.

          // Immediately check if the qualified mass spectrum is of a MS level
          // that matches the greatest MS level found in the member processing
          // flow instance.

          bool check_failed = false;

          if(!checkMsLevel(qualified_mass_spectrum_csp))
            {
              // qDebug() << "The MsLevel check returned false";
              check_failed = true;
            }
          if(!checkMsFragmentation(qualified_mass_spectrum_csp))
            {
              // qDebug() << "The MsFragmentation check returned false";
              check_failed = true;
            }
          if(!checkRtRange(qualified_mass_spectrum_csp))
            {
              // qDebug() << "The RtRange check returned false";
              check_failed = true;
            }
          if(!checkDtRange(qualified_mass_spectrum_csp))
            {
              // qDebug() << "The DtRange check returned false";
              check_failed = true;
            }

          if(check_failed)
            {
              // qDebug() << "The checks returned false";

              ++current_iterator;
              continue;
            }

#if 0 

          // This code is no more necessary because we handle all checks above
          // and both are equipotent unless the code above is much quicker
          // because it avoids cycling too much into the various steps/specs by
          // looking at innermost Dt|Mz|Rt ranges. 
          
          // At this point we need to go deep in the processing flow's steps to
          // check if the node matches the whole set of the steps' specs.

          // qDebug() << "The processing flow has" << m_processingFlow.size() <<
          // "steps.";

          // Boolean value needed because we need to break two loops.
          bool should_increment_iterator_and_continue = false;

          for(auto &&step : m_processingFlow)
            {
              if(!checkProcessingStep(*step,
                                      qualified_mass_spectrum_csp,
                                      mass_spectrum_plus_combiner))
                {
                  should_increment_iterator_and_continue = true;
                  break;
                }
            }

          if(should_increment_iterator_and_continue)
            {
              ++current_iterator;
              continue;
            }
#endif

          trace_plus_combiner.combine(
            *current_map_trace_sp,
            *qualified_mass_spectrum_csp->getMassSpectrumCstSPtr());

          // qDebug() << "Processed one more spectrum.";

          emit incrementProgressBarCurrentValueAndSetStatusTextSignal(
            1, "Processing spectra");

          // Now we can go to the next mass spectrum.
          ++current_iterator;
        }
        // End of
        // while(current_iterator != end_iterator)

#if 0
			qDebug() << "At the end of the current iterator pair, current map:";
			for(auto &&pair : *current_tic_chrom_map_sp)
				qDebug().noquote() << "(" << pair.first << "," << pair.second << ")";
#endif
    }
  // End of
  // #pragma omp parallel for ordered
  // for(std::size_t iter = 0; iter < iterators.size(); ++iter)


  // In the loop above, for each thread, a pappso::MapTrace was
  // filled in with the m/z,intensity pairs.

  // If the task was cancelled, the monitor widget was locked. We need to
  // unlock it.
  emit unlockTaskMonitorCompositeWidgetSignal();
  emit setupProgressBarSignal(0, vector_of_map_trace.size() - 1);

  // We might be here because the user cancelled the operation in the for loop
  // above (visiting all the visitors). In this case m_isOperationCancelled is
  // true. We want to set it back to false, so that the following loop is gone
  // through. The user can ask that the operation be cancelled once more. But
  // we want that at least the performed work be used to show the trace.

  pappso::TracePlusCombiner trace_plus_combiner;

  trace_plus_combiner.setDecimalPlaces(
    m_processingFlow.getDefaultMzIntegrationParams().getDecimalPlaces());

  m_isOperationCancelled = false;
  int iter               = 0;

  // qDebug() << "The vector of map traces has:" << vector_of_map_trace.size()
  //<< "items.";

  for(auto &&current_map_trace_sp : vector_of_map_trace)
    {
      if(m_isOperationCancelled)
        break;

      ++iter;

      emit setStatusTextSignal(
        QString("Consolidating mass spectra from thread %1").arg(iter));

      // qDebug() << "In the consolidating loop, iter:" << iter
      //<< "with a MapTrace of this size : "
      //<< current_map_trace_sp->size();

      trace_plus_combiner.combine(m_mapTrace, *current_map_trace_sp);

      emit setProgressBarCurrentValueSignal(iter);

      // qDebug() << "Emitted current value:" << iter;
    }

  // Write the data to file so that we can check. It works !!!
  // pappso::Utils::appendToFile(m_mapTrace.toString(), "/tmp/prova.txt");

  // At this point, we have really combined all the traces into a single map
  // trace!

  return true;
}


bool
QualifiedMassSpectrumVectorMassDataIntegratorToMz::
  integrateToMzArbitraryBinning()
{
  // qDebug();

  //  This function needs to be called by integrateToMz() that has setup all the
  //  required parameters.

  emit setTaskDescriptionTextSignal("Integrating to a mass spectrum");

  // We need a processing flow to work, and, in particular, a processing step
  // from which to find the specifics of the calculation.

  if(!m_processingFlow.size())
    qFatal("The processing flow cannot be empty. Program aborted.");

  pappso::MzIntegrationParams local_mz_integration_params =
    m_processingFlow.getDefaultMzIntegrationParams();

  if(!local_mz_integration_params.isValid())
    {
      qDebug() << "The mz integration paramas are not valid.";

      // If the mz integration params are not valid, then we cannot know the
      // start and end of the bins ! We need to resort to the statistical data
      // that were computed right after having loaded the file.

      local_mz_integration_params.setSmallestMz(
        mcsp_msRunDataSet->getMsRunDataSetStats().m_minMz);

      local_mz_integration_params.setGreatestMz(
        mcsp_msRunDataSet->getMsRunDataSetStats().m_maxMz);
    }

  // qDebug().noquote() << "Starting integration with mz integration params:"
  //<< local_mz_integration_params.toString();

  // Now we can create the bins!
  std::vector<double> bins = local_mz_integration_params.createBins();

  // qDebug() << "Created bins at:"
  //<< QDateTime::currentDateTime().toString("yyyyMMdd-HH-mm-ss");

  // At this point, allocate a visitor that is specific for the calculation of
  // the mass spectrum. Since we are not binning, we can use a visitor that
  // integrates to a pappso::Trace, not a pappso::MassSpectrum.

  // But we want to parallelize the computation. Se we will allocate a vector of
  // visitors (as many as possible for the available processor threads), each
  // visitor having a subset of the initial data to integrate.

  emit setProgressBarMaxValueSignal(
    mcsp_qualifiedMassSpectraToIntegrateVector->size());

  // Determine the best number of threads to use and how many spectra to provide
  // each of the threads.

  // In the pair below, first is the ideal number of threads and second is the
  // number of mass spectra per thread.
  std::pair<std::size_t, std::size_t> best_parallel_params =
    bestParallelIntegrationParams(
      mcsp_qualifiedMassSpectraToIntegrateVector->size());

  using MassSpecVector = std::vector<pappso::QualifiedMassSpectrumCstSPtr>;
  using VectorIterator = MassSpecVector::const_iterator;

  // Prepare a set of iterators that will distribute all the mass spectra
  // uniformly.
  std::vector<std::pair<VectorIterator, VectorIterator>> iterators =
    calculateIteratorPairs(best_parallel_params.first,
                           best_parallel_params.second);

  // Set aside a vector pappso::MapTrace shared pointers that will be getting
  // the result of the integrations. There will be one such instance per thread
  // executing.

  std::vector<pappso::MapTraceSPtr> vector_of_map_trace;
  for(std::size_t iter = 0; iter < iterators.size(); ++iter)
    vector_of_map_trace.push_back(std::make_shared<pappso::MapTrace>());

  omp_set_num_threads(iterators.size());

  // qDebug() << "Will use" << iterators.size() << "threads";

#pragma omp parallel for ordered
  for(std::size_t iter = 0; iter < iterators.size(); ++iter)
    {
      // qDebug() << "thread index:" << iter;

      // Get the iterator range that we need to deal in this thread.
      VectorIterator current_iterator = iterators.at(iter).first;
      // For debugging purposes
      // VectorIterator end_iterator     = std::prev(iterators.at(iter).second);
      VectorIterator end_iterator = iterators.at(iter).second;

      // Get the map for this thread.

      pappso::MapTraceSPtr current_map_trace_sp = vector_of_map_trace.at(iter);

      // std::size_t qualified_mass_spectrum_count =
      // std::distance(current_iterator, end_iterator);

      // qDebug() << "For thread index:" << iter
      //<< "the number of mass spectra to process:"
      //<< qualified_mass_spectrum_count;

      // Allocate the combiner because we'll need it for the checking of the
      // steps (will set eventual processing to the Trace if there are
      // start-end ranges for the mz values, for example with
      // setFilterResampleKeepXRange.

      pappso::MassSpectrumPlusCombiner mass_spectrum_plus_combiner(
        local_mz_integration_params.getDecimalPlaces());

      // We need to inform the combiner if we have to limit the mz range
      // for the combinations.

      // These values are set in the in the base class constuctor.

      if(m_mzRange.first != std::numeric_limits<double>::max() &&
         m_mzRange.second != std::numeric_limits<double>::min())
        {

          // qDebug() << "The m_mzRange.first:" << m_mzRange.first
          //<< "and m_mzRange.second:" << m_mzRange.second;

          mass_spectrum_plus_combiner.setFilterResampleKeepXRange(
            pappso::FilterResampleKeepXRange(m_mzRange.first,
                                             m_mzRange.second));
        }
      // else
      // qDebug() << "There is no limitation on the m/z range.";

      // Configure the binning mass spectrum combiner.

      mass_spectrum_plus_combiner.setBins(bins);

      // Now process each qualified mass spectrum in the iterator range.
      // Remember the begin and end iterators point to a range of mass spectra
      // in the mass spectra vector that holds *all* the mass spectra to be
      // processed.
      while(current_iterator != end_iterator)
        {
          // We want to be able to intercept any cancellation of the
          // operation.

          if(m_isOperationCancelled)
            break;

          // Make the computation.

          pappso::QualifiedMassSpectrumCstSPtr qualified_mass_spectrum_csp =
            *current_iterator;

          if(qualified_mass_spectrum_csp == nullptr)
            {
              qFatal("The qualified_mass_spectrum_csp cannot be nullptr");
            }
          // else
          // qDebug() << "Current mass spectrum is valid with rt:"
          //<< qualified_mass_spectrum_csp->getRtInMinutes();

          // Ensure that the qualified mass spectrum contains a mass spectrum
          // that effectively contains the binary (mz,i) data. In particular, if
          // that mass spectrum is not there, that might be because the data are
          // not loaded in memory but need to be read from the file.

          qualified_mass_spectrum_csp =
            checkQualifiedMassSpectrum(qualified_mass_spectrum_csp);

          // Great, at this point we actually have the mass spectrum!

          // We now need to ensure that the processing flow allows for this mass
          // spectrum to be combined.

          // Immediately check if the qualified mass spectrum is of a MS level
          // that matches the greatest MS level found in the member processing
          // flow instance.

          bool check_failed = false;

          if(!checkMsLevel(qualified_mass_spectrum_csp))
            {
              // qDebug() << "The MsLevel check returned false";
              check_failed = true;
            }
          if(!checkMsFragmentation(qualified_mass_spectrum_csp))
            {
              // qDebug() << "The MsFragmentation check returned false";
              check_failed = true;
            }
          if(!checkRtRange(qualified_mass_spectrum_csp))
            {
              // qDebug() << "The RtRange check returned false";
              check_failed = true;
            }
          if(!checkDtRange(qualified_mass_spectrum_csp))
            {
              // qDebug() << "The DtRange check returned false";
              check_failed = true;
            }

          if(check_failed)
            {
              // qDebug() << "The checks returned false";

              ++current_iterator;
              continue;
            }

#if 0 

          // This code is no more necessary because we handle all checks above
          // and both are equipotent unless the code above is much quicker
          // because it avoids cycling too much into the various steps/specs by
          // looking at innermost Dt|Mz|Rt ranges. 
          
          // At this point we need to go deep in the processing flow's steps to
          // check if the node matches the whole set of the steps' specs.

          // qDebug() << "The processing flow has" << m_processingFlow.size() <<
          // "steps.";

          // Boolean value needed because we need to break two loops.
          bool should_increment_iterator_and_continue = false;

          for(auto &&step : m_processingFlow)
            {
              if(!checkProcessingStep(*step,
                                      qualified_mass_spectrum_csp,
                                      mass_spectrum_plus_combiner))
                {
                  should_increment_iterator_and_continue = true;
                  break;
                }
            }

          if(should_increment_iterator_and_continue)
            {
              ++current_iterator;
              continue;
            }
#endif

          mass_spectrum_plus_combiner.combine(
            *current_map_trace_sp,
            *qualified_mass_spectrum_csp->getMassSpectrumCstSPtr());

          // qDebug() << "Processed one more spectrum:"
          //<< qualified_mass_spectrum_csp->getMassSpectrumSPtr().get()
          //<< "with size:" << qualified_mass_spectrum_csp->size();

          emit incrementProgressBarCurrentValueAndSetStatusTextSignal(
            1, "Processing spectra");

          // Now we can go to the next mass spectrum.
          ++current_iterator;
        }
        // End of
        // while(current_iterator != end_iterator)

#if 0
			qDebug() << "At the end of the current iterator pair, current map:";
			for(auto &&pair : *current_tic_chrom_map_sp)
				qDebug().noquote() << "(" << pair.first << "," << pair.second << ")";
#endif
    }
  // End of
  // #pragma omp parallel for ordered
  // for(std::size_t iter = 0; iter < iterators.size(); ++iter)


  // In the loop above, for each thread, a pappso::MapTrace was
  // filled in with the m/z,intensity pairs.

  // If the task was cancelled, the monitor widget was locked. We need to
  // unlock it.
  emit unlockTaskMonitorCompositeWidgetSignal();
  emit setupProgressBarSignal(0, vector_of_map_trace.size() - 1);

  // We might be here because the user cancelled the operation in the for loop
  // above (visiting all the visitors). In this case m_isOperationCancelled is
  // true. We want to set it back to false, so that the following loop is gone
  // through. The user can ask that the operation be cancelled once more. But
  // we want that at least the performed work be used to show the trace.

  pappso::MassSpectrumPlusCombiner mass_spectrum_plus_combiner(
    local_mz_integration_params.getDecimalPlaces());

  mass_spectrum_plus_combiner.setBins(bins);

  m_isOperationCancelled = false;
  int iter               = 0;

  // qDebug() << "The vector of map traces has:" << vector_of_map_trace.size()
  //<< "items.";

  for(auto &&current_map_trace_sp : vector_of_map_trace)
    {
      if(m_isOperationCancelled)
        break;

      ++iter;

      QString message =
        QString("Consolidating mass spectra from thread %1").arg(iter);

      emit setStatusTextSignal(message);

      //qDebug() << message;

      mass_spectrum_plus_combiner.combine(m_mapTrace, *current_map_trace_sp);

      emit setProgressBarCurrentValueSignal(iter);

      //qDebug() << "Emitted current value:" << iter;
    }

  // Write the data to file so that we can check. It works !!!
  // pappso::Utils::appendToFile(m_mapTrace.toString(), "/tmp/prova.txt");

  // At this point, we have really combined all the traces into a single map
  // trace!
  return true;
}


} // namespace minexpert

} // namespace msxps
