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


/////////////////////// Local includes
#include "FragmentOligomerTableViewModel.hpp"
#include "FragmentOligomerTableViewSortProxyModel.hpp"


namespace MsXpS
{

namespace massxpert
{


  FragmentOligomerTableViewSortProxyModel::
    FragmentOligomerTableViewSortProxyModel(QObject *parent)
    : QSortFilterProxyModel(parent)
  {
  }


  FragmentOligomerTableViewSortProxyModel::
    ~FragmentOligomerTableViewSortProxyModel()
  {
  }


  void
  FragmentOligomerTableViewSortProxyModel::setPatternFilter(QString value)
  {
    m_patternFilter = value;
  }


  void
  FragmentOligomerTableViewSortProxyModel::setMonoFilter(double value)
  {
    m_monoFilter = value;
  }


  void
  FragmentOligomerTableViewSortProxyModel::setAvgFilter(double value)
  {
    m_avgFilter = value;
  }


  void
  FragmentOligomerTableViewSortProxyModel::setTolerance(double value)
  {
    m_tolerance = value;
  }


  void
  FragmentOligomerTableViewSortProxyModel::setChargeFilter(int value)
  {
    m_chargeFilter = value;
  }


  bool
  FragmentOligomerTableViewSortProxyModel::lessThan(
    const QModelIndex &left, const QModelIndex &right) const
  {
    QVariant leftData  = sourceModel()->data(left);
    QVariant rightData = sourceModel()->data(right);

    if(leftData.typeId() == QMetaType::Int)
      {
        // The Partial column

        int leftValue  = leftData.toInt();
        int rightValue = rightData.toInt();

        return (leftValue < rightValue);
      }
    if(leftData.typeId() == QMetaType::Bool)
      {
        // The libXpertMass::Modif column

        int leftValue  = leftData.toInt();
        int rightValue = rightData.toInt();

        return (leftValue < rightValue);
      }
    else if(leftData.typeId() == QMetaType::QString &&
            (left.column() == FRAGMENT_OLIGO_MONO_COLUMN ||
             left.column() == FRAGMENT_OLIGO_AVG_COLUMN))
      {
        bool ok = false;

        double leftValue = leftData.toDouble(&ok);
        Q_ASSERT(ok);
        double rightValue = rightData.toDouble(&ok);
        Q_ASSERT(ok);

        return (leftValue < rightValue);
      }

    if(leftData.typeId() == QMetaType::QString)
      {
        QString leftString  = leftData.toString();
        QString rightString = rightData.toString();

        if(!leftString.isEmpty() && leftString.at(0) == '[')
          return sortCoordinates(leftString, rightString);
        else if(!leftString.isEmpty() && leftString.contains('#'))
          return sortName(leftString, rightString);
        else
          return (QString::localeAwareCompare(leftString, rightString) < 0);
      }

    return true;
  }


  bool
  FragmentOligomerTableViewSortProxyModel::sortCoordinates(QString left,
                                                           QString right) const
  {
    QString local = left;

    local.remove(0, 1);
    local.remove(local.size() - 1, 1);

    int dash = local.indexOf('-');

    QString leftStartStr = local.left(dash);

    bool ok          = false;
    int leftStartInt = leftStartStr.toInt(&ok);

    local.remove(0, dash + 1);

    QString leftEndStr = local;

    ok             = false;
    int leftEndInt = leftEndStr.toInt(&ok);

    //    qDebug() << "left coordinates" << leftStartInt << "--" << leftEndInt;

    local = right;

    local.remove(0, 1);
    local.remove(local.size() - 1, 1);

    dash = local.indexOf('-');

    QString rightStartStr = local.left(dash);

    ok                = false;
    int rightStartInt = rightStartStr.toInt(&ok);

    local.remove(0, dash + 1);

    QString rightEndStr = local;

    ok              = false;
    int rightEndInt = rightEndStr.toInt(&ok);

    //    qDebug() << "right coordinates" << rightStartInt << "--" <<
    //    rightEndInt;


    if(leftStartInt < rightStartInt)
      return true;

    if(leftEndInt < rightEndInt)
      return true;

    return false;
  }


  bool
  FragmentOligomerTableViewSortProxyModel::sortName(QString left,
                                                    QString right) const
  {
    // The format is this way:
    //
    // <fragspecname>#<numb>#(<fragrulename>)#z=<charge> if there is a
    // fragrule that has been taken into account or
    //
    //<fragspecname>#<numb>#z=<charge> if no fragrule has been taken
    // into account.


    // left QString.
    //
    QString local = left;

    QStringList leftList = local.split("#", Qt::SkipEmptyParts);

    // First item is necessarily <fragspecname>, second item is
    // nessarily <numb> and last item is necessarily z=<charge>.

    QString leftFragSpecName = leftList.first();

    bool ok           = false;
    int leftNumberInt = leftList.at(1).toInt(&ok);

    QString leftChargeString = leftList.last().section('=', 1, 1);

    ok                = false;
    int leftChargeInt = leftChargeString.toInt(&ok);

    // And now check if there was a fragmentation rule.
    QString leftFragRuleName;

    if(leftList.size() == 4)
      {
        leftFragRuleName = leftList.at(2);
      }

    // right QString.
    //
    local = right;

    QStringList rightList = local.split("#", Qt::SkipEmptyParts);

    // First item is necessarily <fragspecname>, second item is
    // nessarily <numb> and last item is necessarily z=<charge>.

    QString rightFragSpecName = rightList.first();

    ok                 = false;
    int rightNumberInt = rightList.at(1).toInt(&ok);

    QString rightChargeString = rightList.last().section('=', 1, 1);

    ok                 = false;
    int rightChargeInt = rightChargeString.toInt(&ok);

    // And now check if there was a fragmentation rule.
    QString rightFragRuleName;

    if(rightList.size() == 4)
      {
        rightFragRuleName = rightList.at(2);
      }

    // Test first the fragmentation name string.
    if(QString::localeAwareCompare(leftFragSpecName, rightFragSpecName) < 0)
      {
        return true;
      }
    else if(QString::localeAwareCompare(leftFragSpecName, rightFragSpecName) ==
            0)
      {
        if(leftNumberInt < rightNumberInt)
          {
            return true;
          }
        else if(leftNumberInt == rightNumberInt)
          {
            if(QString::localeAwareCompare(leftFragRuleName,
                                           rightFragRuleName) < 0)
              {
                return true;
              }
            else if(QString::localeAwareCompare(leftFragRuleName,
                                                rightFragRuleName) == 0)
              {
                if(leftChargeInt < rightChargeInt)
                  {
                    return true;
                  }
              }
          }
      }

    return false;
  }


  bool
  FragmentOligomerTableViewSortProxyModel::filterAcceptsRow(
    int sourceRow, const QModelIndex &sourceParent) const
  {

    if(filterKeyColumn() == FRAGMENT_OLIGO_PATTERN_COLUMN)
      {
        QModelIndex index = sourceModel()->index(
          sourceRow, FRAGMENT_OLIGO_PATTERN_COLUMN, sourceParent);

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

        if(m_patternFilter == pattern)
          return true;
        else
          return false;
      }
    else if(filterKeyColumn() == FRAGMENT_OLIGO_MONO_COLUMN)
      {
        QModelIndex index = sourceModel()->index(
          sourceRow, FRAGMENT_OLIGO_MONO_COLUMN, sourceParent);
        bool ok = false;

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

        double mass = data.toDouble(&ok);
        Q_ASSERT(ok);

        double left  = m_monoFilter - m_tolerance;
        double right = m_monoFilter + m_tolerance;

        if(left <= mass && mass <= right)
          return true;
        else
          return false;
      }
    else if(filterKeyColumn() == FRAGMENT_OLIGO_AVG_COLUMN)
      {
        QModelIndex index = sourceModel()->index(
          sourceRow, FRAGMENT_OLIGO_AVG_COLUMN, sourceParent);
        bool ok = false;

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

        double mass = data.toDouble(&ok);
        Q_ASSERT(ok);

        double left  = m_avgFilter - m_tolerance;
        double right = m_avgFilter + m_tolerance;

        if(left <= mass && mass <= right)
          return true;
        else
          return false;
      }
    else if(filterKeyColumn() == FRAGMENT_OLIGO_CHARGE_COLUMN)
      {
        // The column of the charge.
        QModelIndex index = sourceModel()->index(sourceRow, 5, sourceParent);
        int charge        = sourceModel()->data(index).toInt();

        if(m_chargeFilter == charge)
          return true;
        else
          return false;
      }

    return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);
  }


  void
  FragmentOligomerTableViewSortProxyModel::applyNewFilter()
  {
    invalidateFilter();
  }

} // namespace massxpert

} // namespace MsXpS
