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


/////////////////////// libmass includes
#include <libXpertMass/Oligomer.hpp>


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



namespace MsXpS
{

namespace massxpert
{


  MzLabInputOligomerTableViewModel::MzLabInputOligomerTableViewModel(
    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<MzLabInputOligomerTableViewDlg *>(parent);

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


  MzLabInputOligomerTableViewModel::~MzLabInputOligomerTableViewModel()
  {
  }


  const OligomerList *
  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.

        libXpertMass::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(libXpertMass::OLIGOMER_DEC_PLACES));

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

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

            valueString.setNum(
              oligomer_sp->mass(massType), 'f', libXpertMass::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(libXpertMass::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)
      {
        libXpertMass::OligomerSPtr oligomer_sp = mp_oligomerList->takeAt(iter);

        oligomer_sp.reset();

        ++removedOligomerCount;

        --iter;
      }

    endRemoveRows();

    return removedOligomerCount;
  }


} // namespace massxpert

} // namespace MsXpS
