/* 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 "IsoSpecTableViewModel.hpp"
#include "../../libmass/IsoSpecEntity.hpp"


namespace msXpSmineXpert
{


IsoSpecTableViewModel::IsoSpecTableViewModel(
  QList<msXpSlibmass::IsoSpecEntity *> *isoSpecEntityList, QObject *parent)
  : QAbstractTableModel{parent}, mp_isoSpecEntityList{isoSpecEntityList}
{
  if(!parent)
    qFatal("Fatal error at %s@%d. Aborting.", __FILE__, __LINE__);

  mp_parentDlg = static_cast<IsoSpecDlg *>(parent);
}


IsoSpecTableViewModel::~IsoSpecTableViewModel()
{
}


const QList<msXpSlibmass::IsoSpecEntity *> *
IsoSpecTableViewModel::getIsoSpecEntityList() const
{
  return mp_isoSpecEntityList;
}


const IsoSpecDlg *
IsoSpecTableViewModel::parentDlg() const
{
  return mp_parentDlg;
}


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

  mp_tableView = tableView;
}


IsoSpecTableView *
IsoSpecTableViewModel::tableView()
{
  return mp_tableView;
}


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

  return mp_isoSpecEntityList->size();
}


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

  return static_cast<int>(IsoSpecTableViewColumns::TOTAL_COLUMNS);
}


QVariant
IsoSpecTableViewModel::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 static_cast<int>(IsoSpecTableViewColumns::ELEMENT):
            return "Element";

          case static_cast<int>(IsoSpecTableViewColumns::SYMBOL):
            return "Symbol";

          case static_cast<int>(IsoSpecTableViewColumns::ATOMIC_NO):
            return "Atomic No";

          case static_cast<int>(IsoSpecTableViewColumns::MASS):
            return "Mass";

          case static_cast<int>(IsoSpecTableViewColumns::MASS_NO):
            return "Mass No";

          case static_cast<int>(IsoSpecTableViewColumns::EXTRA_NEUTRONS):
            return "Extra neutrons";

          case static_cast<int>(IsoSpecTableViewColumns::PROBABILITY):
            return "Prob.";

          case static_cast<int>(IsoSpecTableViewColumns::LOG_PROBABILITY):
            return "Log prob.";

          case static_cast<int>(IsoSpecTableViewColumns::RADIOACTIVE):
            return "Radio.";
          default:
            qFatal("Fatal error at %s@%d. Aborting.", __FILE__, __LINE__);
        }
    }

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


QVariant
IsoSpecTableViewModel::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
      // entity.

      msXpSlibmass::IsoSpecEntity *isoSpecEntity =
        static_cast<msXpSlibmass::IsoSpecEntity *>(
          mp_isoSpecEntityList->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 == static_cast<int>(IsoSpecTableViewColumns::ELEMENT))
        {
          valueString = isoSpecEntity->getElement();
        }
      else if(column == static_cast<int>(IsoSpecTableViewColumns::SYMBOL))
        {
          valueString = isoSpecEntity->getSymbol();
        }
      else if(column == static_cast<int>(IsoSpecTableViewColumns::ATOMIC_NO))
        {
          valueString.setNum(isoSpecEntity->getAtomicNo());
        }
      else if(column == static_cast<int>(IsoSpecTableViewColumns::MASS))
        {
          valueString = QString("%1").arg(isoSpecEntity->getMass(), 0, 'f', 12);
        }
      else if(column == static_cast<int>(IsoSpecTableViewColumns::MASS_NO))
        {
          valueString.setNum(isoSpecEntity->getMassNo());
        }
      else if(column ==
              static_cast<int>(IsoSpecTableViewColumns::EXTRA_NEUTRONS))
        {
          valueString.setNum(isoSpecEntity->getExtraNeutrons());
        }
      else if(column == static_cast<int>(IsoSpecTableViewColumns::PROBABILITY))
        {
          valueString =
            QString("%1").arg(isoSpecEntity->getProbability(), 0, 'f', 60);
        }
      else if(column ==
              static_cast<int>(IsoSpecTableViewColumns::LOG_PROBABILITY))
        {
          valueString =
            QString("%1").arg(isoSpecEntity->getLogProbability(), 0, 'f', 60);
        }
      else if(column == static_cast<int>(IsoSpecTableViewColumns::RADIOACTIVE))
        {
          valueString = isoSpecEntity->getRadioactive() ? "true" : "false";
        }
      else
        {
          qFatal("Fatal error at %s@%d. Aborting.", __FILE__, __LINE__);
        }

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

  return QVariant();
}

int
IsoSpecTableViewModel::addIsoSpecEntities(
  const QList<msXpSlibmass::IsoSpecEntity *> &entityList)
{

  // We receive a list of IsoSpecEntity elements which we are asked to add to
  // the list of IsoSpecEntity element that we actually do not own, since they
  // are owned by the IsoSpecDlg instance that is the "owner" of this model. But
  // since we want to add the entities to the list of entities belonging to
  // the IsoSpecDlg 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 entities from
  // the entity list to this model's m_isoSpecEntityList.

  int entityCount = mp_isoSpecEntityList->size();

  //qDebug() << __FILE__ << __LINE__ << "currently present model entities:" << entityCount;

  int entitiesToAddCount = entityList.size();

  //qDebug() << __FILE__ << __LINE__ << "entities to add to model:" << entitiesToAddCount;

  int addedEntityCount = 0;

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

  beginInsertRows(
    QModelIndex(), entityCount, (entityCount + entitiesToAddCount - 1));

  for(int iter = 0; iter < entitiesToAddCount; ++iter)
    {
      msXpSlibmass::IsoSpecEntity *entity =
        static_cast<msXpSlibmass::IsoSpecEntity *>(entityList.at(iter));

      mp_isoSpecEntityList->append(entity);

      ++addedEntityCount;
    }

  endInsertRows();

  //qDebug() << __FILE__ << "@" << __LINE__ << __FUNCTION__ << "()"
           //<< "Entities added:" << addedEntityCount;

  return addedEntityCount;
}


int
IsoSpecTableViewModel::removeIsoSpecEntities(int firstIndex, int lastIndex)
{
  //qDebug() << __FILE__ << "@" << __LINE__ << "ENTER" << __FUNCTION__ << "()";

  // We are asked to remove the entities [firstIndex--lastIndex].

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

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

  int entityCount = mp_isoSpecEntityList->size();

  if(!entityCount)
    return 0;

  if(firstIndex < 0 || firstIndex >= entityCount)
    {
      // 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 entity to remove
  // must go up to the last entity in the list.
  if(lastIndex < -1 || lastIndex >= entityCount)
    {
      // qDebug() << __FILE__ << __LINE__
      //          << "firstIndex:" << firstIndex
      //          << "lastIndex:" << lastIndex;

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

  int lastIdx = 0;

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

  beginRemoveRows(QModelIndex(), firstIdx, lastIdx);

  int removedEntityCount = 0;

  int iter = lastIdx;

  while(iter >= firstIdx)
    {
      msXpSlibmass::IsoSpecEntity *entity =
        static_cast<msXpSlibmass::IsoSpecEntity *>(
          mp_isoSpecEntityList->takeAt(iter));

      delete entity;

      ++removedEntityCount;

      --iter;
    }

  endRemoveRows();

  //qDebug() << __FILE__ << "@" << __LINE__ << __FUNCTION__ << "()"
           //<< "Removed entities:" << removedEntityCount;

  //qDebug() << __FILE__ << "@" << __LINE__ << "EXIT" << __FUNCTION__ << "()";

  return removedEntityCount;
}


} // namespace msXpSmineXpert
