/* BEGIN software license
 *
 * MsXpertSuite - mass spectrometry software suite
 * -----------------------------------------------
 * Copyright(C) 2009,...,2026 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
 */

/////////////////////// libXpertMass includes
#include <MsXpS/libXpertMassCore/Oligomer.hpp>
#include <MsXpS/libXpertMassCore/Prop.hpp>


/////////////////////// Local includes
#include "MassSearchOligomerTableViewModel.hpp"


namespace MsXpS
{

namespace MassXpert
{


MassSearchOligomerTableViewModel::MassSearchOligomerTableViewModel(
  libXpertMassCore::OligomerCollection *oligomerList, QObject *parent)
  : QAbstractTableModel(parent)
{
  if(!oligomerList)
    qFatal("Fatal error at %s@%d. Aborting.", __FILE__, __LINE__);

  mp_oligomers = oligomerList;

  if(!parent)
    qFatal("Fatal error at %s@%d. Aborting.", __FILE__, __LINE__);

  mp_parentDlg = static_cast<MassSearchDlg *>(parent);

  // Port to Qt5
  // reset();
}


MassSearchOligomerTableViewModel::~MassSearchOligomerTableViewModel()
{
}


const libXpertMassCore::OligomerCollection *
MassSearchOligomerTableViewModel::oligomerList()
{
  return mp_oligomers;
}


MassSearchDlg *
MassSearchOligomerTableViewModel::parentDlg()
{
  return mp_parentDlg;
}


void
MassSearchOligomerTableViewModel::setTableView(
  MassSearchOligomerTableView *tableView)
{
  if(!tableView)
    qFatal("Fatal error at %s@%d. Aborting.", __FILE__, __LINE__);

  mp_tableView = tableView;
}


MassSearchOligomerTableView *
MassSearchOligomerTableViewModel::tableView()
{
  return mp_tableView;
}


int
MassSearchOligomerTableViewModel::rowCount(const QModelIndex &parent) const
{
  Q_UNUSED(parent);

  return mp_oligomers->size();
}


int
MassSearchOligomerTableViewModel::columnCount(const QModelIndex &parent) const
{
  Q_UNUSED(parent);

  return MASS_SEARCH_OLIGO_TOTAL_COLUMNS;
}


QVariant
MassSearchOligomerTableViewModel::headerData(int section,
                                             Qt::Orientation orientation,
                                             int role) const
{
  if(role != Qt::DisplayRole)
    return QVariant();

  if(orientation == Qt::Vertical)
    {
      // Return the row number.
      QString valueString;
      valueString.setNum(section);
    }
  else if(orientation == Qt::Horizontal)
    {
      // Return the header of the column.
      switch(section)
        {
          case MASS_SEARCH_OLIGO_SEARCHED_COLUMN:
            return tr("Searched");
          case MASS_SEARCH_OLIGO_NAME_COLUMN:
            return tr("Name");
          case MASS_SEARCH_OLIGO_COORDINATES_COLUMN:
            return tr("Coords");
          case MASS_SEARCH_OLIGO_ERROR_COLUMN:
            return tr("Error");
          case MASS_SEARCH_OLIGO_MONO_COLUMN:
            return tr("Mono");
          case MASS_SEARCH_OLIGO_AVG_COLUMN:
            return tr("Avg");
          case MASS_SEARCH_OLIGO_MODIF_COLUMN:
            return tr("Modif?");
          default:
            qFatal("Fatal error at %s@%d. Aborting.", __FILE__, __LINE__);
        }
    }

  // Should never get here.
  return QVariant();
}


QVariant
MassSearchOligomerTableViewModel::data(const QModelIndex &index, int role) const
{
  if(!index.isValid())
    return QVariant();

  if(role == Qt::TextAlignmentRole)
    {
      return int(Qt::AlignRight | Qt::AlignVCenter);
    }
  else if(role == Qt::DisplayRole)
    {
      int row    = index.row();
      int column = index.column();

      // Let's get the data for the right column and the right
      // row. Let's find the row first, so that we get to the proper
      // oligomer.

      libXpertMassCore::OligomerSPtr oligomer_sp =
        mp_oligomers->getOligomersCstRef().at(row);

      // Now see what's the column that is asked for. Prepare a
      // string that we'll feed with the right data before returning
      // it as a QVariant.

      QString valueString;

      if(column == MASS_SEARCH_OLIGO_SEARCHED_COLUMN)
        {
          // The searched m/z value is stored in a Prop. Get it
          // right away.

          libXpertMassCore::Prop *prop = oligomer_sp->prop("SEARCHED_MZ");

          Q_ASSERT(prop);

          double value = 0;

          value = *static_cast<const double *>(prop->data());
          valueString.setNum(value, 'f', libXpertMassCore::OLIGOMER_DEC_PLACES);
        }
      else if(column == MASS_SEARCH_OLIGO_NAME_COLUMN)
        {
          valueString = oligomer_sp->getName();
        }
      else if(column == MASS_SEARCH_OLIGO_COORDINATES_COLUMN)
        {
          libXpertMassCore::IndexRange *index_range_p =
            oligomer_sp->getCalcOptionsCstRef()
              .getIndexRangeCollectionCstRef()
              .mostInclusiveLeftRightIndexRange();

          valueString = QString("[%1-%2]")
                          .arg(index_range_p->m_start + 1)
                          .arg(index_range_p->m_stop + 1);

          delete(index_range_p);
        }
      else if(column == MASS_SEARCH_OLIGO_ERROR_COLUMN)
        {
          // The error m/z value is stored in a Prop. Get it right
          // away.

          libXpertMassCore::DoubleProp *prop =
            static_cast<libXpertMassCore::DoubleProp *>(
              oligomer_sp->prop("ERROR_MZ"));
          Q_ASSERT(prop);

          double value = 0;

          value = *static_cast<const double *>(prop->data());
          valueString.setNum(value, 'f', libXpertMassCore::OLIGOMER_DEC_PLACES);
        }
      else if(column == MASS_SEARCH_OLIGO_MONO_COLUMN)
        {
          valueString = QString::number(
            oligomer_sp->getMass(libXpertMassCore::Enums::MassType::MONO),
            'f',
            libXpertMassCore::OLIGOMER_DEC_PLACES);
        }
      else if(column == MASS_SEARCH_OLIGO_AVG_COLUMN)
        {
          valueString = QString::number(
            oligomer_sp->getMass(libXpertMassCore::Enums::MassType::AVG),
            'f',
            libXpertMassCore::OLIGOMER_DEC_PLACES);
        }
      else if(column == MASS_SEARCH_OLIGO_MODIF_COLUMN)
        {
          if(oligomer_sp->isModified())
            valueString = "True";
          else
            valueString = "False";
        }
      else
        {
          qFatal("Fatal error at %s@%d. Aborting.", __FILE__, __LINE__);
        }

      return valueString;
    }
  // End of
  // else if(role == Qt::DisplayRole)

  return QVariant();
}


int
MassSearchOligomerTableViewModel::addOligomer(
  libXpertMassCore::Oligomer *oligomer_p)
{
  if(!oligomer_p)
    qFatal() << "Programming error.";

  // We receive an oligomer which we are asked to add to the list of
  // oligomers that we actually do not own, since they are owned by
  // the MassSearchDlg instance that is the "owner" of this
  // model. But since we want to add the oligomer to the list of
  // oligomers belonging to the MassSearchDlg instance in such a way
  // that the tableView takes it into account, we have to do that
  // addition work here.

  // Note that we are acutally *transferring* the oligomer to this
  // model's mp_oligomers.

  int existing_oligomers_count = mp_oligomers->size();

  // qDebug() << __FILE__ << __LINE__
  //          << "model oligomers:" << oligomerCount;

  int oligomers_to_add_count = 1;

  // qDebug() << __FILE__ << __LINE__
  //          << "oligomers to add:" << oligomersToAdd;

  // We have to let know the model that we are going to modify the
  // list of oligomers and thus the number of rows in the table
  // view. We will append the oligomer to the preexisting ones,
  // which means we are going to have our appended oligomer at
  // index=oligomerCount.

  beginInsertRows(QModelIndex(),
                  existing_oligomers_count,
                  (existing_oligomers_count + oligomers_to_add_count - 1));
  // FIXME check that this works flawlessly
  mp_oligomers->getOligomersRef().emplace_back(oligomer_p);
  endInsertRows();

  return 1 /*the single oligomer added*/;
}


int
MassSearchOligomerTableViewModel::addOligomers(
  const libXpertMassCore::OligomerCollection &oligomer_collection)
{
  // We receive a list of oligomers which we are asked to add to the
  // list of oligomers that we actually do not own, since they are
  // owned by the MassSearchDlg instance that is the "owner" of this
  // model. But since we want to add the oligomers to the list of
  // oligomers belonging to the MassSearchDlg instance in such a way
  // that the tableView takes them into account, we have to do that
  // addition work here.

  // Note that we are acutally *transferring* all the oligomers from
  // the oligomerList to this model's mp_oligomers.

  int existing_oligomers_count = mp_oligomers->size();

  // qDebug() << __FILE__ << __LINE__
  //          << "model oligomers:" << oligomerCount;

  int oligomers_to_add = oligomer_collection.size();

  // qDebug() << __FILE__ << __LINE__
  //          << "oligomers to add:" << oligomersToAdd;

  int added_oligomers_count = 0;

  // We have to let know the model that we are going to modify the
  // list of oligomers and thus the number of rows in the table
  // view. We will append the oligomers to the preexisting ones,
  // which means we are going to have our first appended oligomer at
  // index = oligomerCount.

  beginInsertRows(QModelIndex(),
                  existing_oligomers_count,
                  (existing_oligomers_count + oligomers_to_add - 1));

  for(libXpertMassCore::OligomerSPtr oligomer_sp :
      oligomer_collection.getOligomersCstRef())
    {
      mp_oligomers->getOligomersRef().push_back(oligomer_sp);
      ++added_oligomers_count;
    }

  endInsertRows();

  return added_oligomers_count;
}


int
MassSearchOligomerTableViewModel::removeOligomers(std::size_t start_index,
                                                  std::size_t stop_index)
{
  // We are asked to remove the oligomers [firstIndex--lastIndex].

  // firstIndex has to be >= 0 and < mp_oligomers->size().

  // We are asked to remove the oligomers from the list of oligomers
  // that we actually do not own, since they are owned by the
  // MassSearchDlg instance that is the "owner" of this model. But
  // since we want to remove the oligomers from the list of
  // oligomers belonging to the MassSearchDlg instance in such a way
  // that the tableView takes them into account, we have to do that
  // removal work here.

  std::size_t existing_oligomers_count = mp_oligomers->size();

  qDebug() << "There are" << existing_oligomers_count << "oligomers.";

  if(!existing_oligomers_count)
    return 0;

  if(start_index >= existing_oligomers_count)
    qFatal() << "Programming error. Out of bounds error.";

  if(stop_index >= existing_oligomers_count)
    qFatal() << "Programming error. Out of bounds error.";

  qDebug() << "start_index:" << start_index << "stop_index:" << stop_index;

  beginRemoveRows(QModelIndex(), start_index, stop_index);

  std::vector<libXpertMassCore::OligomerSPtr>::iterator the_start_iterator =
    mp_oligomers->getOligomersRef().begin() + start_index;
  std::vector<libXpertMassCore::OligomerSPtr>::iterator the_stop_iterator =
    mp_oligomers->getOligomersRef().begin() + stop_index + 1;

  mp_oligomers->getOligomersRef().erase(the_start_iterator, the_stop_iterator);

  endRemoveRows();

  return existing_oligomers_count - mp_oligomers->getOligomersRef().size();
}


} // namespace MassXpert

} // namespace MsXpS
