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


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


/////////////////////// pappsomspp includes


/////////////////////// Local includes
#include "MsRunDataSetTableViewModel.hpp"
#include "MsRunDataSetTableViewProxyModel.hpp"
#include "MsRunDataSetTableViewItem.hpp"
#include "../nongui/ProcessingStep.hpp"

namespace MsXpS
{
namespace MineXpert
{

MsRunDataSetTableViewProxyModel::MsRunDataSetTableViewProxyModel(
  QObject *parent)
  : QSortFilterProxyModel(parent)
{
  // Initialize all the filtering member variables to values that show that no
  // filtering is being asked for.
  m_indicesPair = std::pair<std::size_t, std::size_t>(
    std::numeric_limits<std::size_t>::max(),
    std::numeric_limits<std::size_t>::min());

  m_msLevel = 0;

  m_rtValuesPair = std::pair<double, double>(
    std::numeric_limits<double>::max(), std::numeric_limits<double>::min());

  m_dtValuesPair = std::pair<double, double>(
    std::numeric_limits<double>::max(), std::numeric_limits<double>::min());

  m_precursorIndexPair = std::pair<std::size_t, std::size_t>(
    std::numeric_limits<std::size_t>::max(),
    std::numeric_limits<std::size_t>::min());

  m_precursorMzValuesPair = std::pair<double, double>(
    std::numeric_limits<double>::max(), std::numeric_limits<double>::min());

  m_precursorChargePair = std::pair<std::size_t, std::size_t>(
    std::numeric_limits<std::size_t>::max(),
    std::numeric_limits<std::size_t>::min());
}

MsRunDataSetTableViewProxyModel::~MsRunDataSetTableViewProxyModel()
{
}

std::pair<size_t, size_t>
MsRunDataSetTableViewProxyModel::parseSizeTString(const QString &text,
                                                  size_t accepted_lower_value)
{
  // The accepted syntax is like this:
  //
  // xxx
  // xxx - yyy
  // < yyy
  // > xxx
  // < yyy > xxx

  // We can test various QRegularExpression_s and check which one works.

  // qDebug() << "Parsing size_t string:" << text;

  QRegularExpression regexp;
  QRegularExpressionMatch match;
  std::pair<std::size_t, std::size_t> range_pair;
  bool ok = false;


  // Single value pattern (xxx)
  regexp.setPattern("^\\s*(\\d+)\\s*$");

  match = regexp.match(text);

  if(match.hasMatch())
    {
      // qDebug();

      QString value_string = match.captured(1);

      ok                = false;
      std::size_t value = value_string.toULongLong(&ok);

      // Because min() for std::size_t is 0, we cannot use it to test the
      // validity of the pair first and second members... We thus resort to the
      // max() for both first and second.

      if(!ok || value < accepted_lower_value)
        return std::pair<std::size_t, std::size_t>(
          std::numeric_limits<std::size_t>::max(),
          std::numeric_limits<std::size_t>::max());

      // qDebug() << "pair:" << std::pair<std::size_t, std::size_t>(value,
      // value);

      return std::pair<std::size_t, std::size_t>(value, value);
    }


  // Range of values pattern (xxx -- yyy)
  regexp.setPattern("^\\s*(\\d+)\\s*--\\s*(\\d+)\\s*$");

  match = regexp.match(text);

  if(match.hasMatch())
    {
      // qDebug();

      QString lower_value_string = match.captured(1);
      QString upper_value_string = match.captured(2);

      ok               = false;
      range_pair.first = lower_value_string.toULongLong(&ok);

      // Because min() for std::size_t is 0, we cannot use it to test the
      // validity of the pair first and second members... We thus resort to the
      // max() for both first and second.

      if(!ok || range_pair.first < accepted_lower_value)
        return std::pair<std::size_t, std::size_t>(
          std::numeric_limits<std::size_t>::max(),
          std::numeric_limits<std::size_t>::max());

      range_pair.second = upper_value_string.toULongLong(&ok);

      if(!ok || range_pair.second < accepted_lower_value)
        return std::pair<std::size_t, std::size_t>(
          std::numeric_limits<std::size_t>::max(),
          std::numeric_limits<std::size_t>::max());

      // At this point we have the pair.

      // qDebug() << "pair:" << range_pair;

      return range_pair;
    }


  // Tolerance-based singlevalue pattern (xxx%yyy)
  regexp.setPattern("^\\s*(\\d+)\\s*%\\s*(\\d+)\\s*$");

  match = regexp.match(text);

  if(match.hasMatch())
    {
      QString value_string     = match.captured(1);
      QString tolerance_string = match.captured(2);
      ok                       = false;

      std::size_t value = value_string.toULongLong(&ok);

      if(!ok)
        return std::pair<std::size_t, std::size_t>(
          std::numeric_limits<std::size_t>::max(),
          std::numeric_limits<std::size_t>::max());

      double tolerance = tolerance_string.toULongLong(&ok);

      if(!ok)
        return std::pair<std::size_t, std::size_t>(
          std::numeric_limits<std::size_t>::max(),
          std::numeric_limits<std::size_t>::max());

      range_pair.first = value - tolerance;

      if(range_pair.first < accepted_lower_value)
        return std::pair<std::size_t, std::size_t>(
          std::numeric_limits<std::size_t>::max(),
          std::numeric_limits<std::size_t>::max());

      range_pair.second = value + tolerance;

      // At this point we have the pair.

      // qDebug() << "pair:" << range_pair;

      return range_pair;
    }


  // < yyy pattern.
  regexp.setPattern("^\\s*<\\s*(\\d+)\\s*$");

  match = regexp.match(text);

  if(match.hasMatch())
    {
      // qDebug();

      // By essence the lower value is the lowest accepted value.
      range_pair.first = accepted_lower_value;

      // Get the string value.
      QString upper_value_string = match.captured(1);

      ok                      = false;
      std::size_t upper_value = upper_value_string.toULongLong(&ok);

      // Because min() for std::size_t is 0, we cannot use it to test the
      // validity of the pair first and second members... We thus resort to the
      // max() for both first and second.

      if(!ok || !upper_value)
        return std::pair<std::size_t, std::size_t>(
          std::numeric_limits<std::size_t>::max(),
          std::numeric_limits<std::size_t>::max());

      // But the comparison is < not <=, thus we ned to reduce the value by one
      // unit. We already tested that it --upper_value could not yield a
      // negative (and thus erroneous unsigned) value.

      --upper_value;

      // Now test if we still have an acceptable upper value, that cannot be
      // less than the accepted lower value.
      if(upper_value < accepted_lower_value)
        return std::pair<std::size_t, std::size_t>(
          std::numeric_limits<std::size_t>::max(),
          std::numeric_limits<std::size_t>::max());

      range_pair.second = upper_value;

      // At this point we have the pair.

      // qDebug() << "pair:" << range_pair;

      return range_pair;
    }


  // > xxx pattern.
  regexp.setPattern("^\\s*>\\s*(\\d+)\\s*$");

  match = regexp.match(text);

  if(match.hasMatch())
    {
      // qDebug();

      // By essence, the upper value is max:
      range_pair.second = std::numeric_limits<std::size_t>::max();

      // Get the string value.
      QString lower_value_string = match.captured(1);

      ok                      = false;
      std::size_t lower_value = lower_value_string.toULongLong(&ok);

      // Because min() for std::size_t is 0, we cannot use it to test the
      // validity of the pair first and second members... We thus resort to the
      // max() for both first and second.

      if(!ok)
        return std::pair<std::size_t, std::size_t>(
          std::numeric_limits<std::size_t>::max(),
          std::numeric_limits<std::size_t>::max());

      // But the comparison is > not >=, thus we ned to

      ++lower_value;

      // Now check that this value is an acceptable lower value.
      if(lower_value < accepted_lower_value)
        return std::pair<std::size_t, std::size_t>(
          std::numeric_limits<std::size_t>::max(),
          std::numeric_limits<std::size_t>::max());

      range_pair.first = lower_value;

      // At this point we have the pair.

      // qDebug() << "pair:" << range_pair;

      return range_pair;
    }

  // < yyy > xxx pattern or > xxx < yyy
  // < yyy > xxx, that is xxx < VAR < yyy
  regexp.setPattern("^\\s*<\\s*(\\d+)\\s*>\\s*(\\d+)\\s*$");

  match = regexp.match(text);

  if(match.hasMatch())
    {
      // qDebug();

      // Get the string values
      QString upper_value_string = match.captured(1);
      QString lower_value_string = match.captured(2);

      ok                      = false;
      std::size_t lower_value = lower_value_string.toULongLong(&ok);

      // Because min() for std::size_t is 0, we cannot use it to test the
      // validity of the pair first and second members... We thus resort to the
      // max() for both first and second.

      if(!ok)
        return std::pair<std::size_t, std::size_t>(
          std::numeric_limits<std::size_t>::max(),
          std::numeric_limits<std::size_t>::max());

      // But the comparison is > not >=, thus we ned to

      ++lower_value;

      // Now  check that this value is an acceptable lwoer value.
      if(lower_value < accepted_lower_value)
        return std::pair<std::size_t, std::size_t>(
          std::numeric_limits<std::size_t>::max(),
          std::numeric_limits<std::size_t>::max());

      range_pair.first = lower_value;

      // qDebug() << "range_pair.first:" << range_pair.first;

      std::size_t upper_value = upper_value_string.toULongLong(&ok);

      if(!ok || !upper_value)
        return std::pair<std::size_t, std::size_t>(
          std::numeric_limits<std::size_t>::max(),
          std::numeric_limits<std::size_t>::max());

      // But the comparison is < not <=, thus we ned to decrement second, which
      // is why we tested above that --upper_value would not be negative (and
      // thus an erroneous unsigned value).

      --upper_value;

      // Now test if we still have an acceptable upper value, that cannot be
      // less than the accepted lower value.
      if(upper_value < accepted_lower_value)
        return std::pair<std::size_t, std::size_t>(
          std::numeric_limits<std::size_t>::max(),
          std::numeric_limits<std::size_t>::max());

      range_pair.second = upper_value;

      // At this point we have the pair.

      // qDebug() << "pair:" << range_pair;

      return range_pair;
    }


  // < yyy > xxx pattern or > xxx < yyy
  // > xxx < yyy, that is xxx < VAR < yyy
  regexp.setPattern("^\\s*>\\s*(\\d+)\\s*<\\s*(\\d+)\\s*$");

  match = regexp.match(text);

  if(match.hasMatch())
    {
      // qDebug();

      // Get the string values
      QString lower_value_string = match.captured(1);
      QString upper_value_string = match.captured(2);

      ok = false;

      std::size_t lower_value = lower_value_string.toULongLong(&ok);

      // Because min() for std::size_t is 0, we cannot use it to test the
      // validity of the pair first and second members... We thus resort to the
      // max() for both first and second.

      if(!ok)
        return std::pair<std::size_t, std::size_t>(
          std::numeric_limits<std::size_t>::max(),
          std::numeric_limits<std::size_t>::max());

      // But the comparison is > not >=, thus we ned to

      ++lower_value;

      if(lower_value < accepted_lower_value)
        return std::pair<std::size_t, std::size_t>(
          std::numeric_limits<std::size_t>::max(),
          std::numeric_limits<std::size_t>::max());

      range_pair.first = lower_value;

      std::size_t upper_value = upper_value_string.toULongLong(&ok);

      if(!ok || !upper_value)
        return std::pair<std::size_t, std::size_t>(
          std::numeric_limits<std::size_t>::max(),
          std::numeric_limits<std::size_t>::max());

      // But the comparison is < not <=, thus we ned to

      --upper_value;

      if(upper_value < accepted_lower_value)
        return std::pair<std::size_t, std::size_t>(
          std::numeric_limits<std::size_t>::max(),
          std::numeric_limits<std::size_t>::max());

      range_pair.second = upper_value;

      // At this point we have the pair.

      // qDebug() << "pair:" << range_pair;

      return range_pair;
    }

  return std::pair<std::size_t, std::size_t>(
    std::numeric_limits<std::size_t>::max(),
    std::numeric_limits<std::size_t>::max());
}

std::pair<double, double>
MsRunDataSetTableViewProxyModel::parseDoubleString(const QString &text,
                                                   double accepted_lower_value)
{
  // qDebug() << "text:" << text;

  // The accepted syntax is like this:
  //
  // xxx
  // xxx%yyy (with yyy tolerance)
  // xxx - yyy
  // < yyy
  // > xxx
  // < yyy > xxx

  // We can test various QRegularExpression_s and check which one works.

  QString sci_number = "([-]?\\s*\\d*\\.?\\d*[e-]?\\d*)";

  QRegularExpression regexp;
  QRegularExpressionMatch match;
  std::pair<double, double> range_pair;
  bool ok = false;


  // Single value pattern (xxx, being positive or negative double real with
  // decimals and exponential (positive or negative)
  regexp.setPattern("^\\s*" + sci_number + "\\s*$");

  match = regexp.match(text);

  if(match.hasMatch())
    {
      QString value_string = match.captured(1);

      ok           = false;
      double value = value_string.toDouble(&ok);

      if(!ok || value < accepted_lower_value)
        return std::pair<double, double>(std::numeric_limits<double>::max(),
                                         std::numeric_limits<double>::min());

      // qDebug() << "pair:" << std::pair<double, double>(value, value);

      return std::pair<double, double>(value, value);
    }


  // Tolerance-based single value pattern (xxx%yyy)
  regexp.setPattern("^\\s*" + sci_number + "\\s*%\\s*" + sci_number + "\\s*$");

  match = regexp.match(text);

  if(match.hasMatch())
    {
      QString value_string     = match.captured(1);
      QString tolerance_string = match.captured(2);
      ok                       = false;

      double value = value_string.toDouble(&ok);

      if(!ok)
        return std::pair<double, double>(std::numeric_limits<double>::max(),
                                         std::numeric_limits<double>::max());

      double tolerance = tolerance_string.toDouble(&ok);

      if(!ok)
        return std::pair<double, double>(std::numeric_limits<double>::max(),
                                         std::numeric_limits<double>::max());

      range_pair.first = value - tolerance;

      if(range_pair.first < accepted_lower_value)
        return std::pair<double, double>(std::numeric_limits<double>::max(),
                                         std::numeric_limits<double>::max());

      range_pair.second = value + tolerance;

      // At this point we have the pair.

      // qDebug() << "pair:" << range_pair;

      return range_pair;
    }


  // Range of values pattern (xxx -- yyy)
  regexp.setPattern("^\\s*" + sci_number + "\\s*--\\s*" + sci_number + "\\s*$");

  // qDebug() << "testing range:" << regexp.pattern();

  match = regexp.match(text);

  if(match.hasMatch())
    {
      QString lower_value_string = match.captured(1);
      QString upper_value_string = match.captured(2);

      ok               = false;
      range_pair.first = lower_value_string.toDouble(&ok);

      if(!ok || range_pair.first < accepted_lower_value)
        return std::pair<double, double>(std::numeric_limits<double>::max(),
                                         std::numeric_limits<double>::max());

      range_pair.second = upper_value_string.toDouble(&ok);

      if(!ok || range_pair.second < accepted_lower_value)
        {
          qDebug() << "Failed to convert or was below lower.";

          return std::pair<double, double>(std::numeric_limits<double>::max(),
                                           std::numeric_limits<double>::min());
        }

      // At this point we have the pair.

      // qDebug() << "pair:" << range_pair;

      return range_pair;
    }


  // < yyy pattern.
  regexp.setPattern("^\\s*<\\s*" + sci_number + "\\s*$");

  match = regexp.match(text);

  if(match.hasMatch())
    {
      // qDebug();

      // By essence the lower value is the lowest accepted value.
      range_pair.first = accepted_lower_value;

      // Get the string value.
      QString upper_value_string = match.captured(1);

      ok                 = false;
      double upper_value = upper_value_string.toDouble(&ok);

      if(!ok)
        return std::pair<double, double>(std::numeric_limits<double>::max(),
                                         std::numeric_limits<double>::min());

      // But the comparison is < not <=, thus we ned to reduce the value by one
      // epsilon.

      upper_value -= std::numeric_limits<double>::epsilon();

      // Now test if we still have an acceptable upper value, that cannot be
      // less than the accepted lower value.
      if(upper_value < accepted_lower_value)
        return std::pair<double, double>(std::numeric_limits<double>::max(),
                                         std::numeric_limits<double>::min());

      range_pair.second = upper_value;

      // At this point we have the pair.

      // qDebug() << "pair:" << range_pair;

      return range_pair;
    }


  // > xxx pattern.
  regexp.setPattern("^\\s*>\\s*" + sci_number + "\\s*$");

  match = regexp.match(text);

  if(match.hasMatch())
    {
      // qDebug() << "text that matched:" << text;

      // By essence the upper value is max.
      range_pair.second = std::numeric_limits<double>::max();

      // Get the string value.
      QString lower_value_string = match.captured(1);

      // qDebug() << "captured:" << lower_value_string;

      ok                 = false;
      double lower_value = lower_value_string.toDouble(&ok);

      if(!ok)
        return std::pair<double, double>(std::numeric_limits<double>::max(),
                                         std::numeric_limits<double>::min());

      // But the comparison is > not >=, thus we ned to

      lower_value += std::numeric_limits<double>::epsilon();

      // Now check that this value is an acceptable lower value.
      if(lower_value < accepted_lower_value)
        return std::pair<double, double>(std::numeric_limits<double>::max(),
                                         std::numeric_limits<double>::min());

      range_pair.first = lower_value;

      // At this point we have the pair.

      // qDebug() << "pair:" << range_pair;

      return range_pair;
    }


  // < yyy > xxx pattern or > xxx < yyy
  // > xxx < yyy, that is xxx < VAR < yyy
  regexp.setPattern("^\\s*>\\s*" + sci_number + "\\s*<\\s*" + sci_number +
                    "\\s*$");

  match = regexp.match(text);

  if(match.hasMatch())
    {
      // qDebug();

      // Get the string values
      QString lower_value_string = match.captured(1);
      QString upper_value_string = match.captured(2);

      ok = false;

      double lower_value = lower_value_string.toDouble(&ok);

      if(!ok)
        return std::pair<double, double>(std::numeric_limits<double>::max(),
                                         std::numeric_limits<double>::min());

      // But the comparison is > not >=, thus we ned to

      lower_value += std::numeric_limits<double>::epsilon();

      if(lower_value < accepted_lower_value)
        return std::pair<double, double>(std::numeric_limits<double>::max(),
                                         std::numeric_limits<double>::min());

      range_pair.first = lower_value;

      double upper_value = upper_value_string.toDouble(&ok);

      if(!ok)
        return std::pair<double, double>(std::numeric_limits<double>::max(),
                                         std::numeric_limits<double>::min());

      // But the comparison is < not <=, thus we ned to

      upper_value -= std::numeric_limits<double>::epsilon();

      if(upper_value < accepted_lower_value)
        return std::pair<double, double>(std::numeric_limits<double>::max(),
                                         std::numeric_limits<double>::min());

      range_pair.second = upper_value;

      // At this point we have the pair.

      // qDebug() << "pair:" << range_pair;

      return range_pair;
    }

  return std::pair<double, double>(std::numeric_limits<double>::max(),
                                   std::numeric_limits<double>::min());
}

bool
MsRunDataSetTableViewProxyModel::convertFilteringStrings(
  std::size_t ms_level,
  const QString &indices,
  const QString &rt_values,
  const QString &dt_values,
  const QString &precursor_index_values,
  const QString &precursor_mz_values,
  const QString &precursor_charge_values)
{
  m_msLevel = ms_level;

  // We need to parse each string and make sense of what it contains. If it is
  // empty, then we set values that we'll test later to know if the pair is to
  // be used for the filtering or not.

  using SizeTPair  = std::pair<std::size_t, std::size_t>;
  using DoublePair = std::pair<double, double>;

  // The indices (they start at value 0, which is acceptable).
  if(!indices.isEmpty())
    {
      // qDebug();

      m_indicesPair = parseSizeTString(indices, 0);
      if(m_indicesPair.first == std::numeric_limits<std::size_t>::max() ||
         m_indicesPair.second == std::numeric_limits<std::size_t>::min())
        {
          // qDebug() << "The indices values:" << m_indicesPair.first << "-"
          //          << m_indicesPair.second;

          return false;
        }
    }
  else
    m_indicesPair = SizeTPair(std::numeric_limits<std::size_t>::max(),
                              std::numeric_limits<std::size_t>::min());

  // Now the retention time values.
  if(!rt_values.isEmpty())
    {
      m_rtValuesPair = parseDoubleString(rt_values, 0);
      if(m_rtValuesPair.first == std::numeric_limits<double>::max() ||
         m_rtValuesPair.second == std::numeric_limits<double>::min())
        {
          // qDebug() << "The retention time values:" << m_rtValuesPair.first
          //          << "-" << m_rtValuesPair.second;

          return false;
        }
    }
  else
    m_rtValuesPair = DoublePair(std::numeric_limits<double>::max(),
                                std::numeric_limits<double>::min());

  // Now the drift time values.
  if(!dt_values.isEmpty())
    {
      m_dtValuesPair = parseDoubleString(dt_values, 0);
      if(m_dtValuesPair.first == std::numeric_limits<double>::max() ||
         m_dtValuesPair.second == std::numeric_limits<double>::min())
        {
          // qDebug() << "The drift time values:" << m_dtValuesPair.first << "-"
          //          << m_dtValuesPair.second;

          return false;
        }
    }
  else
    m_dtValuesPair = DoublePair(std::numeric_limits<double>::max(),
                                std::numeric_limits<double>::min());

  // Now the precursor index values
  if(!precursor_index_values.isEmpty())
    {
      m_precursorIndexPair = parseSizeTString(precursor_index_values, 0);
      if(m_precursorIndexPair.first ==
           std::numeric_limits<std::size_t>::max() ||
         m_precursorIndexPair.second == std::numeric_limits<std::size_t>::min())
        {
          // qDebug() << "The precursor index values :"
          //          << m_precursorIndexPair.first << "-"
          //          << m_precursorIndexPair.second;

          return false;
        }
    }
  else
    m_precursorIndexPair = SizeTPair(std::numeric_limits<std::size_t>::max(),
                                     std::numeric_limits<std::size_t>::min());

  // Now the precursor m/z values
  if(!precursor_mz_values.isEmpty())
    {
      m_precursorMzValuesPair = parseDoubleString(precursor_mz_values, 0);
      if(m_precursorMzValuesPair.first == std::numeric_limits<double>::max() ||
         m_precursorMzValuesPair.second == std::numeric_limits<double>::min())
        {
          // qDebug() << "The precursor m/z values:"
          //          << m_precursorMzValuesPair.first << "-"
          //          << m_precursorMzValuesPair.second;

          return false;
        }
    }
  else
    m_precursorMzValuesPair = DoublePair(std::numeric_limits<double>::max(),
                                         std::numeric_limits<double>::min());

  // Now the precursor z values
  if(!precursor_charge_values.isEmpty())
    {
      m_precursorChargePair = parseSizeTString(precursor_charge_values, 0);

      // qDebug() << "The pair: [" << m_precursorChargePair.first << ","
      //<< m_precursorChargePair.second << "]";

      // Because min() for std::size_t is 0, we cannot use it to test the
      // validity of the pair first and second members... We thus resort to the
      // max() for both first and second.
      if(m_precursorChargePair.first ==
           std::numeric_limits<std::size_t>::max() ||
         m_precursorChargePair.second ==
           std::numeric_limits<std::size_t>::max())
        {
          // qDebug() << "The precursor charge values:"
          //          << m_precursorChargePair.first << "-"
          //          << m_precursorChargePair.second;

          return false;
        }
    }
  else
    m_precursorChargePair = SizeTPair(std::numeric_limits<std::size_t>::max(),
                                      std::numeric_limits<std::size_t>::min());

  // qDebug() << "Returning true;";

  return true;
}

void
MsRunDataSetTableViewProxyModel::setPrecision(pappso::PrecisionPtr precision_p)
{
  // This is going to be the tolerance with which we try the precursor mz
  // values matches.
  mp_precision = precision_p;
}

bool
MsRunDataSetTableViewProxyModel::isValidFilterPair(
  const std::pair<std::size_t, std::size_t> &pair) const
{
  return (pair.first != std::numeric_limits<std::size_t>::max());
}

bool
MsRunDataSetTableViewProxyModel::isValidFilterPair(
  const std::pair<double, double> &pair) const
{
  return (pair.first != std::numeric_limits<double>::max());
}

bool
MsRunDataSetTableViewProxyModel::filterAcceptsRow(
  int source_row, const QModelIndex &source_parent) const
{
  // We check all the various filters and each time one filter is valid, then
  // we can use it to filter the data.

  bool should_accept_row = true;
  bool ok                = false;

  QModelIndex index =
    sourceModel()->index(source_row,
                         static_cast<int>(MassSpecDataViewColumns::COLUMN_RT),
                         source_parent);

  QVariant data = sourceModel()->data(index);

  // Only used below for debug messages.
  // double rt_current_model_index = data.toDouble(&ok);

  if(isValidFilterPair(m_indicesPair))
    {

      // The column of the spectrum indices

      QModelIndex index = sourceModel()->index(
        source_row,
        static_cast<int>(MassSpecDataViewColumns::COLUMN_SPECTRUM_INDEX),
        source_parent);

      QVariant data = sourceModel()->data(index);

      std::size_t cell_value = data.toULongLong(&ok);

      if(!ok)
        qFatal() << "Failed to convert variant to std::size_t.";

      if(cell_value < m_indicesPair.first || cell_value > m_indicesPair.second)
        return false;
    }

  // We consider that a MS level of 0 matches all the MS levels, so, if that
  // value is 0, we do not care and should_accept_row is not set to false per
  // this criterion.

  if(m_msLevel)
    {

      // The column of the ms levels

      // The problem here is that the model does not return any string
      // (DisplayRole) for the ms level column, because we use an icon for
      // this bit of info. Then only place where we know the actual ms level
      // is in the tree widget item.

      QModelIndex index = sourceModel()->index(
        source_row,
        static_cast<int>(MassSpecDataViewColumns::COLUMN_MS_LEVEL),
        source_parent);

      // This is the call that returns the empty QString("").
      // QVariant data = sourceModel()->data(index)

      // So we need to get the actual widget item.
      MsRunDataSetTableViewItem *item =
        static_cast<MsRunDataSetTableViewItem *>(index.internalPointer());

      QVariant cell_data = item->data(index.column());

      std::size_t cell_value = cell_data.toULongLong(&ok);

      if(!ok)
        qFatal() << "Failed to convert variant to std::size_t.";

      if(cell_value != m_msLevel)
        return false;
    }

  if(isValidFilterPair(m_rtValuesPair))
    {

      // The column of the ms levels

      QModelIndex index = sourceModel()->index(
        source_row,
        static_cast<int>(MassSpecDataViewColumns::COLUMN_RT),
        source_parent);

      QVariant data = sourceModel()->data(index);

      double cell_value = data.toDouble(&ok);

      if(!ok)
        qFatal() << "Failed to convert variant to std::size_t.";

      if(cell_value < m_rtValuesPair.first ||
         cell_value > m_rtValuesPair.second)
        return false;
    }

  if(isValidFilterPair(m_dtValuesPair))
    {

      // The column of the ms levels

      QModelIndex index = sourceModel()->index(
        source_row,
        static_cast<int>(MassSpecDataViewColumns::COLUMN_DT),
        source_parent);

      QVariant data = sourceModel()->data(index);

      double cell_value = data.toDouble(&ok);

      if(!ok)
        qFatal() << "Failed to convert variant to std::size_t.";

      if(cell_value < m_dtValuesPair.first ||
         cell_value > m_dtValuesPair.second)
        return false;
    }

  if(isValidFilterPair(m_precursorIndexPair))
    {

      // This cell might contain *nothing* because it is only filled if the
      // row is about a fragmentation spectrum, that is, not a MS1 spectrum.

      QModelIndex index = sourceModel()->index(
        source_row,
        static_cast<int>(MassSpecDataViewColumns::COLUMN_PRECURSOR_INDEX),
        source_parent);

      // We know the data are stored in the table view cells as QString
      // variables.
      QString data = sourceModel()->data(index).toString();

      if(!data.isEmpty())
        {
          std::size_t cell_value = data.toULongLong(&ok);

          if(!ok)
            qFatal() << "Failed to convert variant to std::size_t.";

          if(cell_value < m_precursorIndexPair.first ||
             cell_value > m_precursorIndexPair.second)
            return false;
        }
      else
        return false;
    }

  if(isValidFilterPair(m_precursorMzValuesPair))
    {

      // The precursor mz values might be more than one in a given cell, because
      // more than one ion might be selected for fragmentation.

      // This cell might contain *nothing* because it is only filled if the
      // row is about a fragmentation spectrum, that is, not a MS1 spectrum.

      QModelIndex index = sourceModel()->index(
        source_row,
        static_cast<int>(MassSpecDataViewColumns::COLUMN_PRECURSOR_MZ),
        source_parent);

      // We know the data are stored in the tree view cells as QString
      // variables.
      //
      // The string in this kind of cell, might be either 1234.567 or a
      // ';'-separated list of such values. We thus need to get the first one
      // (the values are sorted in increasing order upon setting up of the model
      // data).

      QString data = sourceModel()->data(index).toString();

      if(!data.isEmpty())
        {
          // We need to deconstruct the ';' separated list of mz values.

          // If sep ';' does not match anywhere in the string, split() returns a
          // single-element list containing this string.

          QStringList mz_value_string_list(data.split(';', Qt::SkipEmptyParts));

          if(!mz_value_string_list.isEmpty())
            {
              std::vector<double> mz_value_vector;

              QStringList::const_iterator iter;

              for(iter = mz_value_string_list.constBegin();
                  iter != mz_value_string_list.constEnd();
                  ++iter)
                {
                  bool ok = false;

                  double mz_value = iter->toDouble(&ok);

                  if(ok)
                    {
                      mz_value_vector.push_back(mz_value);
                    }
                  else
                    continue;
                }

              // At this point we have a vector of precursor mz values matching
              // the values found in the cell-contained string.

              // We now need to check if any of these values match the
              // ]m_precursorMzValuesPair.first--m_precursorMzValuesPair.second[
              // range. Do not forget to take into account the
              // precision/tolerance.

              if(mz_value_vector.size())
                {
                  std::vector<double>::const_iterator iter = std::find_if(
                    mz_value_vector.begin(),
                    mz_value_vector.end(),
                    //[this, rt_current_model_index](double value) {
                    [this](double value) {
                      // qDebug() << "mz_value_vector iterated value:" << value;

                      // We need to account for the precision/tolerance.

                      if(mp_precision == nullptr)
                        qFatal() << "mp_precision is nullptr.";

                      double delta_first =
                        mp_precision->delta(m_precursorMzValuesPair.first);

                      double delta_second =
                        mp_precision->delta(m_precursorMzValuesPair.second);

                      // qDebug()
                      //<< "rt_current_model_index:" << rt_current_model_index
                      //<< "m_precursorMzValuesPair.first:"
                      //<< m_precursorMzValuesPair.first
                      //<< "m_precursorMzValuesPair.second:"
                      //<< m_precursorMzValuesPair.second << "value:" << value
                      //<< "delta_first:" << delta_first
                      //<< "delta_second:" << delta_second;

                      return (value >=
                                (m_precursorMzValuesPair.first - delta_first) &&
                              value <= (m_precursorMzValuesPair.second +
                                        delta_second));
                    });

                  if(iter == mz_value_vector.end())
                    return false;
                }
              else
                return false;
            }
        }
      else
        return false;
    }

  if(isValidFilterPair(m_precursorChargePair))
    {

      // The precursor z values might be more than one in a given cell, because
      // more than one ion might be selected for fragmentation.

      // This cell might contain *nothing* because it is only filled if the
      // row is about a fragmentation spectrum, that is, not a MS1 spectrum.

      QModelIndex index = sourceModel()->index(
        source_row,
        static_cast<int>(MassSpecDataViewColumns::COLUMN_PRECURSOR_Z),
        source_parent);

      // We know the data are stored in the tree view cells as QString
      // variables.
      //
      // The string in this kind of cell, might be either 2 or a
      // ';'-separated list of such values. We thus need to get the first one
      // (the values are sorted in increasing order upon setting up of the model
      // data).

      QString cell_data = sourceModel()->data(index).toString();

      if(!cell_data.isEmpty())
        {
          // We need to deconstruct the ';' separated list of mz values.

          // If sep ';' does not match anywhere in the string, split() returns a
          // single-element list containing this string.

          QStringList cell_data_z_value_string_list(
            cell_data.split(';', Qt::SkipEmptyParts));

          if(!cell_data_z_value_string_list.isEmpty())
            {
              std::vector<std::size_t> cell_data_z_value_vector;

              QStringList::const_iterator iter;

              for(iter = cell_data_z_value_string_list.constBegin();
                  iter != cell_data_z_value_string_list.constEnd();
                  ++iter)
                {
                  bool ok = false;

                  std::size_t z_value = iter->toULongLong(&ok);

                  if(!ok)
                    qFatal() << "Failed to convert string to std::size_t";

                  cell_data_z_value_vector.push_back(z_value);
                }

              // At this point we have a vector of precursor z values matching
              // the values found in the cell-contained string.

              // We now need to check if any of these values match the
              // ]m_precursorChargePair.first--m_precursorChargePair.second[
              // range.

              if(cell_data_z_value_vector.size())
                {
                  std::vector<std::size_t>::const_iterator iter = std::find_if(
                    cell_data_z_value_vector.begin(),
                    cell_data_z_value_vector.end(),
                    [this](std::size_t cell_data_value) {
                      return (cell_data_value >= m_precursorChargePair.first &&
                              cell_data_value <= m_precursorChargePair.second);
                    });

                  if(iter == cell_data_z_value_vector.end())
                    return false;
                }
              else
                return false;
            }
        }
      else
        return false;
    }

  return should_accept_row;
}

bool
MsRunDataSetTableViewProxyModel::lessThan(const QModelIndex &left,
                                          const QModelIndex &right) const
{

  // We'll have to compare values that have different types depending on the
  // column that is sorted.

  int column = left.column();

  QVariant leftData  = sourceModel()->data(left);
  QVariant rightData = sourceModel()->data(right);

  bool ok = false;

  if(column == static_cast<int>(MassSpecDataViewColumns::COLUMN_MS_LEVEL))
    {

      // The column of the ms levels

      // The problem here is that the model does not return any string
      // (DisplayRole) for the ms level column, because we use an icon for
      // this bit of info. Then only place where we know the actual ms level
      // is in the tree widget item.

      // So we need to get the actual widget item.
      MsRunDataSetTableViewItem *left_item =
        static_cast<MsRunDataSetTableViewItem *>(left.internalPointer());
      QVariant left_cell_data     = left_item->data(left.column());
      std::size_t left_cell_value = left_cell_data.toULongLong(&ok);

      MsRunDataSetTableViewItem *right_item =
        static_cast<MsRunDataSetTableViewItem *>(right.internalPointer());
      QVariant right_cell_data     = right_item->data(right.column());
      std::size_t right_cell_value = right_cell_data.toULongLong(&ok);

      return left_cell_value < right_cell_value;
    }
  else if(column ==
            static_cast<int>(MassSpecDataViewColumns::COLUMN_SPECTRUM_INDEX) ||
          column ==
            static_cast<int>(MassSpecDataViewColumns::COLUMN_PRECURSOR_INDEX))
    {
      return leftData.toUInt(&ok) < rightData.toUInt(&ok);
    }
  else if(column ==
          static_cast<int>(MassSpecDataViewColumns::COLUMN_PRECURSOR_Z))
    {
      // The string that is returned in this column, might either 2 or a
      // ';'-separated list of such values. We thus need to get the first one
      // (the values are sorted in increasing order upon setting up of the
      // model data).

      // If sep ';' does not match anywhere in the string, split() returns a
      // single-element list containing this string.

      QStringList z_value_list_left(
        leftData.toString().split(';', Qt::SkipEmptyParts));

      // qDebug() << "left_value_string_list:" << z_value_list_left;

      if(z_value_list_left.isEmpty())
        return true;

      QString left_value_string = z_value_list_left.first();

      // qDebug() << "left_value_string:" << left_value_string;

      QStringList z_value_list_right(
        rightData.toString().split(';', Qt::SkipEmptyParts));

      if(z_value_list_right.isEmpty())
        return false;

      // qDebug() << "right_value_string_list:" << z_value_list_right;

      QString right_value_string = z_value_list_right.first();

      // qDebug() << "right_value_string:" << right_value_string;

      std::size_t left_value = left_value_string.toUInt(&ok);
      if(!ok)
        qFatal() << "Failed to convert charge string to std::size_t";

      std::size_t right_value = right_value_string.toUInt(&ok);
      if(!ok)
        qFatal() << "Failed to convert charge string to std::size_t";

      return left_value < right_value;
    }
  else if(column == static_cast<int>(MassSpecDataViewColumns::COLUMN_RT) ||
          column == static_cast<int>(MassSpecDataViewColumns::COLUMN_DT))
    {
      bool ok = false;

      double left_value = leftData.toDouble(&ok);
      if(!ok)
        qFatal() << "Failed to convert RT|DT string to double.";

      double right_value = rightData.toDouble(&ok);
      if(!ok)
        qFatal() << "Failed to convert RT|DT string to double.";

      // qDebug() << "left_value:" << left_value << "right_value:" <<
      // right_value
      //<< "should return:" << (left_value < right_value);

      return (left_value < right_value);
    }
  else if(column ==
          static_cast<int>(MassSpecDataViewColumns::COLUMN_PRECURSOR_MZ))

    {
      // The string that is returned in this column, might either 1234.567 or
      // a
      // ';'-separated list of such values. We thus need to get the first one
      // (the values are sorted in increasing order upon setting up of the
      // model data).

      // If sep ';' does not match anywhere in the string, split() returns a
      // single-element list containing this string.

      QStringList mz_value_list_left(
        leftData.toString().split(';', Qt::SkipEmptyParts));

      // qDebug() << "left_value_string_list:" << mz_value_list_left;

      if(mz_value_list_left.isEmpty())
        return true;

      QString left_value_string = mz_value_list_left.first();

      // qDebug() << "left_value_string:" << left_value_string;

      QStringList mz_value_list_right(
        rightData.toString().split(';', Qt::SkipEmptyParts));

      if(mz_value_list_right.isEmpty())
        return false;

      // qDebug() << "right_value_string_list:" << mz_value_list_right;

      QString right_value_string = mz_value_list_right.first();

      // qDebug() << "right_value_string:" << right_value_string;

      double left_value = left_value_string.toDouble(&ok);
      if(!ok)
        qDebug() << "string" << left_value_string
                 << "did not convert to double";

      double right_value = right_value_string.toDouble(&ok);
      if(!ok)
        qDebug() << "string" << right_value_string
                 << "did not convert to double";

      return left_value < right_value;
    }
  else
    qFatal() << "Should never reach this point.";

  // To avoid warning.
  return false;
}

} // namespace MineXpert
} // namespace MsXpS
