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


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


namespace MsXpS
{

namespace MassXpert
{


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

  mp_oligomerList = oligomerList;

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

  mp_parentDlg = static_cast<MzLabInputOligomerTableViewDlg *>(parent);

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


MzLabInputOligomerTableViewModel::~MzLabInputOligomerTableViewModel()
{
}


const libXpertMassCore::OligomerCollection *
MzLabInputOligomerTableViewModel::oligomerList() const
{
  return mp_oligomerList;
}


void
MzLabInputOligomerTableViewModel::setParentDlg(
  MzLabInputOligomerTableViewDlg *dlg)
{
  mp_parentDlg = dlg;
}


MzLabInputOligomerTableViewDlg *
MzLabInputOligomerTableViewModel::parentDlg()
{
  return mp_parentDlg;
}


void
MzLabInputOligomerTableViewModel::setMzLabWnd(MzLabWnd *wnd)
{
  mp_mzLabWnd = wnd;
}


MzLabWnd *
MzLabInputOligomerTableViewModel::mzLabWnd()
{
  return mp_mzLabWnd;
}


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

  mp_tableView = tableView;
}


MzLabInputOligomerTableView *
MzLabInputOligomerTableViewModel::tableView()
{
  return mp_tableView;
}


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

  return mp_oligomerList->size();
}


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

  return MZ_LAB_INPUT_OLIGO_TOTAL_COLUMNS;
}


QVariant
MzLabInputOligomerTableViewModel::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 MZ_LAB_INPUT_OLIGO_MASS_COLUMN:
            return tr("m/z");
          case MZ_LAB_INPUT_OLIGO_CHARGE_COLUMN:
            return tr("z");
          case MZ_LAB_INPUT_OLIGO_NAME_COLUMN:
            return tr("name");
          case MZ_LAB_INPUT_OLIGO_COORDS_COLUMN:
            return tr("coords");
          default:
            qFatal("Fatal error at %s@%d. Aborting.", __FILE__, __LINE__);
        }
    }

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


bool
MzLabInputOligomerTableViewModel::setData(
  const QModelIndex &index,
  [[maybe_unused]] const QVariant &value,
  [[maybe_unused]] int role)
{
  // Fixme. Is it correct to not call anything here ?

  dataChanged(index, index);

  return true;
}


QVariant
MzLabInputOligomerTableViewModel::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_oligomerList->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 == MZ_LAB_INPUT_OLIGO_MASS_COLUMN)
        {
          // valueString.setNum(oligomer->mono(libXpertMassCore::OLIGOMER_DEC_PLACES));

          // Fixme: should we check for the mass type ?

          libXpertMassCore::Enums::MassType massType = mp_parentDlg->massType();
          //    qDebug() << __FILE__ << __LINE__ << "massType:" << massType;

          valueString.setNum(oligomer_sp->mass(massType),
                             'f',
                             libXpertMassCore::OLIGOMER_DEC_PLACES);
        }
      else if(column == MZ_LAB_INPUT_OLIGO_CHARGE_COLUMN)
        {
          int charge = oligomer_sp->charge();

          valueString.setNum(charge);
        }
      else if(column == MZ_LAB_INPUT_OLIGO_NAME_COLUMN)
        {
          valueString = oligomer_sp->name();
        }
      else if(column == MZ_LAB_INPUT_OLIGO_COORDS_COLUMN)
        {
          //  Get the coordinates of the oligomer in the form of a
          //  string.
          valueString = oligomer_sp->positionsAsText();

          // qDebug() << __FILE__<< __LINE__
          //          << "valueString:" << valueString;
        }
      else
        {
          qFatal("Fatal error at %s@%d. Aborting.", __FILE__, __LINE__);
        }

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

  return QVariant();
}


int
MzLabInputOligomerTableViewModel::addOligomer(
  libXpertMassCore::OligomerSPtr oligomer_sp)
{
  if(oligomer_sp == nullptr)
    qFatal("Fatal error at %s@%d. Aborting.", __FILE__, __LINE__);

  // 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 parent dialog window instance that is the "owner" of this
  // model. But since we want to add the oligomer to the list of
  // oligomers belonging to that dialog 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_oligomerList.

  int oligomerCount = mp_oligomerList->size();

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

  int oligomersToAdd = 1;

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

  int addedOligomerCount = 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 oligomer to the preexisting ones,
  // which means we are going to have our appended oligomer at
  // index=oligomerCount.

  beginInsertRows(
    QModelIndex(), oligomerCount, (oligomerCount + oligomersToAdd - 1));

  for(int iter = 0; iter < oligomersToAdd; ++iter)
    {
      mp_oligomerList->append(oligomer_sp);

      ++addedOligomerCount;
    }

  endInsertRows();

  return addedOligomerCount;
}


int
MzLabInputOligomerTableViewModel::removeOligomers(int firstIndex, int lastIndex)
{
  // We are asked to remove the oligomers [firstIndex--lastIndex].

  // firstIndex has to be >= 0 and < mp_oligomerList->size(). If
  // lastIndex is -1, then remove all oligomers in range
  // [firstIndex--last oligomer].

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

  int oligomerCount = mp_oligomerList->size();

  if(!oligomerCount)
    return 0;

  if(firstIndex < 0 || firstIndex >= oligomerCount)
    {
      // qDebug() << __FILE__ << __LINE__
      //          << "firstIndex:" << firstIndex
      //          << "lastIndex:" << lastIndex;

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

  int firstIdx = firstIndex;

  // lastIndex can be < 0 (that is -1) if the oligomer to remove
  // must go up to the last oligomer in the list.
  if(lastIndex < -1 || lastIndex >= oligomerCount)
    {
      // qDebug() << __FILE__ << __LINE__
      //          << "firstIndex:" << firstIndex
      //          << "lastIndex:" << lastIndex;

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

  int lastIdx = 0;

  if(lastIndex == -1)
    lastIdx = (oligomerCount - 1);
  else
    lastIdx = lastIndex;

  beginRemoveRows(QModelIndex(), firstIdx, lastIdx);

  int removedOligomerCount = 0;

  int iter = lastIdx;

  while(iter >= firstIdx)
    {
      libXpertMassCore::OligomerSPtr oligomer_sp = mp_oligomerList->takeAt(iter);

      oligomer_sp.reset();

      ++removedOligomerCount;

      --iter;
    }

  endRemoveRows();

  return removedOligomerCount;
}


} // namespace MassXpert

} // namespace MsXpS
