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


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


namespace msxps
{

	namespace massxpert
	{




MassSearchOligomerTableViewModel::MassSearchOligomerTableViewModel(
  OligomerList *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<MassSearchDlg *>(parent);

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


MassSearchOligomerTableViewModel::~MassSearchOligomerTableViewModel()
{
}


const OligomerList *
MassSearchOligomerTableViewModel::oligomerList()
{
  return mp_oligomerList;
}


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_oligomerList->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.

      Oligomer *oligomer = 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 == MASS_SEARCH_OLIGO_SEARCHED_COLUMN)
        {
          // The searched m/z value is stored in a Prop. Get it
          // right away.

          Prop *prop = oligomer->prop("SEARCHED_MZ");
          Q_ASSERT(prop);

          double value = 0;

          value = *static_cast<const double *>(prop->data());
          valueString.setNum(value, 'f', OLIGOMER_DEC_PLACES);
        }
      else if(column == MASS_SEARCH_OLIGO_NAME_COLUMN)
        {
          valueString = oligomer->name();
        }
      else if(column == MASS_SEARCH_OLIGO_COORDINATES_COLUMN)
        {
          valueString = QString("[%1-%2]")
                          .arg(oligomer->startIndex() + 1)
                          .arg(oligomer->endIndex() + 1);
        }
      else if(column == MASS_SEARCH_OLIGO_ERROR_COLUMN)
        {
          // The error m/z value is stored in a Prop. Get it right
          // away.

          Prop *prop = oligomer->prop("ERROR_MZ");
          Q_ASSERT(prop);

          double value = 0;

          value = *static_cast<const double *>(prop->data());
          valueString.setNum(value, 'f', OLIGOMER_DEC_PLACES);
        }
      else if(column == MASS_SEARCH_OLIGO_MONO_COLUMN)
        {
          valueString = oligomer->monoString(OLIGOMER_DEC_PLACES);
        }
      else if(column == MASS_SEARCH_OLIGO_AVG_COLUMN)
        {
          valueString = oligomer->avgString(OLIGOMER_DEC_PLACES);
        }
      else if(column == MASS_SEARCH_OLIGO_MODIF_COLUMN)
        {
          if(oligomer->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(Oligomer *oligomer)
{
  if(!oligomer)
    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 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_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);

      ++addedOligomerCount;
    }

  endInsertRows();

  return addedOligomerCount;
}


int
MassSearchOligomerTableViewModel::addOligomers(const OligomerList &oligomerList)
{
  // 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_oligomerList.

  int oligomerCount = mp_oligomerList->size();

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

  int oligomersToAdd = oligomerList.size();

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

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

  for(int iter = 0; iter < oligomersToAdd; ++iter)
    {
      Oligomer *oligomer = oligomerList.at(iter);

      mp_oligomerList->append(oligomer);

      ++addedOligomerCount;
    }

  endInsertRows();

  return addedOligomerCount;
}


int
MassSearchOligomerTableViewModel::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
  // 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.

  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)
    {
      Oligomer *oligomer = mp_oligomerList->takeAt(iter);

      delete oligomer;

      ++removedOligomerCount;

      --iter;
    }

  endRemoveRows();

  return removedOligomerCount;
}


} // namespace massxpert

} // namespace msxps
