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


/////////////////////// Local includes
#include <massxpert/gui/FragmentOligomerTableViewModel.hpp>


namespace msXpSmassXpert
{

FragmentOligomerTableViewModel::FragmentOligomerTableViewModel(
  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<FragmentationDlg *>(parent);

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


FragmentOligomerTableViewModel::~FragmentOligomerTableViewModel()
{
}


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


FragmentationDlg *
FragmentOligomerTableViewModel::parentDlg()
{
  return mp_parentDlg;
}


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

  mp_tableView = tableView;
}


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

  return mp_oligomerList->size();
}


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

  return FRAGMENT_OLIGO_TOTAL_COLUMNS;
}


QVariant
FragmentOligomerTableViewModel::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 FRAGMENT_OLIGO_PATTERN_COLUMN:
            return tr("Frag. Pattern");
          case FRAGMENT_OLIGO_NAME_COLUMN:
            return tr("Name");
          case FRAGMENT_OLIGO_COORDINATES_COLUMN:
            return tr("Coords");
          case FRAGMENT_OLIGO_MONO_COLUMN:
            return tr("Mono");
          case FRAGMENT_OLIGO_AVG_COLUMN:
            return tr("Avg");
          case FRAGMENT_OLIGO_CHARGE_COLUMN:
            return tr("Charge");
          case FRAGMENT_OLIGO_MODIF_COLUMN:
            return tr("Modif?");
          default:
            qFatal("Fatal error at %s@%d. Aborting.", __FILE__, __LINE__);
        }
    }
  // Should never get here.
  return QVariant();
}


QVariant
FragmentOligomerTableViewModel::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 == FRAGMENT_OLIGO_PATTERN_COLUMN)
        {
          valueString = oligomer->description();
        }
      else if(column == FRAGMENT_OLIGO_NAME_COLUMN)
        {
          valueString = oligomer->name();
        }
      else if(column == FRAGMENT_OLIGO_COORDINATES_COLUMN)
        {
          int oligomerCount = static_cast<CoordinateList *>(oligomer)->size();

          for(int jter = 0; jter < oligomerCount; ++jter)
            {
              Coordinates *coordinates =
                static_cast<CoordinateList *>(oligomer)->at(jter);

              valueString += QString("[%1-%2]")
                               .arg(coordinates->start() + 1)
                               .arg(coordinates->end() + 1);
            }
        }
      else if(column == FRAGMENT_OLIGO_MONO_COLUMN)
        {
          valueString = oligomer->monoString(msXpS::OLIGOMER_DEC_PLACES);
        }
      else if(column == FRAGMENT_OLIGO_AVG_COLUMN)
        {
          valueString = oligomer->avgString(msXpS::OLIGOMER_DEC_PLACES);
        }
      else if(column == FRAGMENT_OLIGO_CHARGE_COLUMN)
        {
          int charge = oligomer->charge();

          valueString.setNum(charge);
        }
      else if(column == FRAGMENT_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->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
FragmentOligomerTableViewModel::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 FragmentationDlg instance that is the "owner" of this
  // model. But since we want to add the oligomers to the list of
  // oligomers belonging to the FragmentationDlg 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
FragmentOligomerTableViewModel::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
  // FragmentationDlg instance that is the "owner" of this model. But
  // since we want to remove the oligomers from the list of
  // oligomers belonging to the FragmentationDlg 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 msXpSmassXpert
