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


/////////////////////// Qt includes


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


namespace MsXpS
{

namespace MassXpert
{


CleaveOligomerTableViewModel::CleaveOligomerTableViewModel(
  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<CleavageDlg *>(parent);

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


CleaveOligomerTableViewModel::~CleaveOligomerTableViewModel()
{
}


libXpertMassCore::OligomerCollection *
CleaveOligomerTableViewModel::getOligomers()
{
  return mp_oligomers;
}


const CleavageDlg *
CleaveOligomerTableViewModel::parentDlg() const
{
  return mp_parentDlg;
}


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

  mp_tableView = tableView;
}


CleaveOligomerTableView *
CleaveOligomerTableViewModel::tableView()
{
  return mp_tableView;
}


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

  return mp_oligomers->size();
}


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

  return CLEAVE_OLIGO_TOTAL_COLUMNS;
}


QVariant
CleaveOligomerTableViewModel::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 CLEAVE_OLIGO_PARTIAL_COLUMN:
            return tr("Part. cleav.");
          case CLEAVE_OLIGO_NAME_COLUMN:
            return tr("Name");
          case CLEAVE_OLIGO_COORDINATES_COLUMN:
            return tr("Coords");
          case CLEAVE_OLIGO_MONO_COLUMN:
            return tr("Mono");
          case CLEAVE_OLIGO_AVG_COLUMN:
            return tr("Avg");
          case CLEAVE_OLIGO_CHARGE_COLUMN:
            return tr("Charge");
          case CLEAVE_OLIGO_FORMULA_COLUMN:
            return tr("Formula");
          case CLEAVE_OLIGO_MODIF_COLUMN:
            return tr("Modif?");
          default:
            qFatal("Fatal error at %s@%d. Aborting.", __FILE__, __LINE__);
        }
    }
  // Should never get here.
  return QVariant();
}


QVariant
CleaveOligomerTableViewModel::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 == CLEAVE_OLIGO_PARTIAL_COLUMN)
        {
          int partialCleavage = oligomer_sp->getPartialCleavage();

          valueString.setNum(partialCleavage);
        }
      else if(column == CLEAVE_OLIGO_NAME_COLUMN)
        {
          valueString = oligomer_sp->getName();
        }
      else if(column == CLEAVE_OLIGO_COORDINATES_COLUMN)
        {
          if(!oligomer_sp->getCalcOptionsRef()
                .getIndexRangeCollectionCstRef()
                .getRangesCstRef()
                .size())
            qDebug() << "The IndexRangeCollection is empty!!";

          foreach(const libXpertMassCore::IndexRange *item,
                  oligomer_sp->getCalcOptionsRef()
                    .getIndexRangeCollectionCstRef()
                    .getRangesCstRef())
            valueString +=
              QString("[%1-%2]").arg(item->m_start + 1).arg(item->m_stop + 1);
        }
      else if(column == CLEAVE_OLIGO_MONO_COLUMN)
        {
          valueString = QString("%1\n").arg(
            oligomer_sp->getMass(libXpertMassCore::Enums::MassType::MONO),
            0,
            'f',
            libXpertMassCore::OLIGOMER_DEC_PLACES);
        }
      else if(column == CLEAVE_OLIGO_AVG_COLUMN)
        {
          valueString = QString("%1\n").arg(
            oligomer_sp->getMass(libXpertMassCore::Enums::MassType::AVG),
            0,
            'f',
            libXpertMassCore::OLIGOMER_DEC_PLACES);
        }
      else if(column == CLEAVE_OLIGO_CHARGE_COLUMN)
        {
          valueString.setNum(oligomer_sp->getIonizerCstRef().charge());
        }
      else if(column == CLEAVE_OLIGO_FORMULA_COLUMN)
        {
          // elementalComposition() will not yield the right formula
          // when the cleavage has been performed using a cleavage agent
          // that has cleavage rules. Indeed, elementalComposition() only
          // look at "hard" oligomer data : sequence, modifications, cross-links,
          // but not formulas that are applied as a result of a cleavage rule
          // application.
          // valueString = oligomer_sp->elementalComposition();
          valueString = oligomer_sp->getFormulaCstRef().getActionFormula(false);
        }
      else if(column == CLEAVE_OLIGO_MODIF_COLUMN)
        {
          // If the user changed the sequence by removing monomers, looking
          // into the polymer might encounter a monomer index that is greater
          // than the actual size of the polymer. Thus look into
          // oligomer->m_isModified only.
          if(oligomer_sp->isModified(false /*do not look deep in polymer*/))
            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
CleaveOligomerTableViewModel::addOligomers(
  const libXpertMassCore::OligomerCollection &oligomers)
{
  // 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 CleavageDlg instance that is the "owner" of this
  // model. But since we want to add the oligomers to the list of
  // oligomers belonging to the CleavageDlg 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.

  std::size_t initial_size = mp_oligomers->getOligomersCstRef().size();

  // 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 = oligomer_count.

  beginInsertRows(QModelIndex(),
                  initial_size,
                  (initial_size + oligomers.getOligomersCstRef().size() - 1));

  for(const libXpertMassCore::OligomerSPtr &oligomer_sp :
      oligomers.getOligomersCstRef())
    mp_oligomers->getOligomersRef().push_back(oligomer_sp);

#if 0
//  old version
  for(int iter = 0; iter < oligomersToAdd; ++iter)
    {
      libXpertMassCore::OligomerSPtr oligomer_sp =oligomers.at(iter);
      mp_oligomers->append(std::make_shared<CleaveOligomer>(*oligomer_sp));
      ++added_oligomer_count;
    }
#endif

  endInsertRows();

  return mp_oligomers->size() - initial_size;
}


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

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

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

  if(!initial_size)
    return 0;

  if(start_index >= initial_size)
    qFatal() << "Programming error.";
  if(stop_index >= initial_size)
    qFatal() << "Programming error.";

  beginRemoveRows(QModelIndex(), start_index, stop_index);

#if 0
// Old version
  int iter = lastIdx;
  while(iter >= firstIdx)
    {
      // The shared pointer will help with the life of the object.
      mp_oligomers->takeAt(iter);
      ++removedOligomerCount;
      --iter;
}
#endif

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

  mp_oligomers->getOligomersRef().erase(the_begin_iterator, the_end_iterator);

  endRemoveRows();

  return initial_size - mp_oligomers->size();
}


} // namespace MassXpert

} // namespace MsXpS
