/* 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
#include <QDebug>
#include <QMouseEvent>
#include <QHeaderView>
#include <QMessageBox>

/////////////////////// Local includes
#include <massxpert/gui/MzLabInputOligomerTableView.hpp>
#include <massxpert/gui/MzLabInputOligomerTableViewModel.hpp>

#include <massxpert/nongui/Oligomer.hpp>

#include <massxpert/gui/MzLabInputOligomerTableViewDlg.hpp>

#include <massxpert/gui/CleaveOligomerTableViewMimeData.hpp>
#include <massxpert/gui/FragmentOligomerTableViewMimeData.hpp>
#include <massxpert/gui/MassSearchOligomerTableViewMimeData.hpp>

#include <massxpert/nongui/Coordinates.hpp>

namespace msXpSmassXpert
{

MzLabInputOligomerTableView::MzLabInputOligomerTableView(QWidget *parent)
  : QTableView(parent)
{

  setAlternatingRowColors(true);

  setSortingEnabled(true);

  setAcceptDrops(true);
  setDropIndicatorShown(true);

  QHeaderView *headerView = horizontalHeader();
  headerView->setSectionsClickable(true);
  headerView->setSectionsMovable(true);

  connect(this,
          SIGNAL(activated(const QModelIndex &)),
          this,
          SLOT(itemActivated(const QModelIndex &)));
}


MzLabInputOligomerTableView::~MzLabInputOligomerTableView()
{
}

void
MzLabInputOligomerTableView::setOligomerList(OligomerList *oligomerList)
{
  mp_oligomerList = oligomerList;
}


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


void
MzLabInputOligomerTableView::setParentDlg(MzLabInputOligomerTableViewDlg *dlg)
{
  Q_ASSERT(dlg);

  mp_parentDlg = dlg;
}


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

void
MzLabInputOligomerTableView::setMzLabWnd(MzLabWnd *wnd)
{
  Q_ASSERT(wnd);

  mp_mzLabWnd = wnd;
}


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


void
MzLabInputOligomerTableView::setSourceModel(
  MzLabInputOligomerTableViewModel *model)
{
  mp_sourceModel = model;
}


MzLabInputOligomerTableViewModel *
MzLabInputOligomerTableView::sourceModel()
{
  return mp_sourceModel;
}


void
MzLabInputOligomerTableView::mousePressEvent(QMouseEvent *mouseEvent)
{
  if(mouseEvent->buttons() & Qt::RightButton)
    {
      // contextMenu->popup(mouseEvent->globalPos());
      return;
    }

  QTableView::mousePressEvent(mouseEvent);
}


msXpS::MassType
MzLabInputOligomerTableView::pasteOrDropMassTypeCheck()
{
  // Each time a new drop/paste happens, we have to perform some
  // checks.

  // It is compulsory that the user has selected either mono or avg
  // as the mass type for the parent dialog window. Ask what's being
  // currently checked (mono and avg are two checkboxes).

  msXpS::MassType previousMassType = mp_parentDlg->previousMassType();
  msXpS::MassType massType         = mp_parentDlg->massType();

  if(massType == msXpS::MassType::MASS_NONE)
    {
      // Ask the user to clearly indicate what is the mass type
      // required).

      massType = mp_parentDlg->massTypeQuery();
    }

  // By now we have a current massType.

  // Check if one drop already occurred in the past and if it was
  // with the same mass type as now. Otherwise alert the user.

  if(previousMassType != msXpS::MassType::MASS_NONE &&
     previousMassType != massType)
    {
      int ret = QMessageBox::question(this,
                                      tr("massXpert: mzLab"),
                                      tr("You are switching mass types."
                                         "Ok to continue ?"),
                                      QMessageBox::Yes | QMessageBox::No,
                                      QMessageBox::No);

      if(ret == QMessageBox::No)
        {
          return msXpS::MassType::MASS_NONE;
        }
    }

  return massType;
}


void
MzLabInputOligomerTableView::dragEnterEvent(QDragEnterEvent *event)
{
  const CleaveOligomerTableViewMimeData *tableViewData =
    qobject_cast<const CleaveOligomerTableViewMimeData *>(event->mimeData());

  if(tableViewData)
    {
      event->acceptProposedAction();
      event->accept();
      return;
    }
  else if(event->mimeData()->hasFormat("text/plain"))
    {
      event->acceptProposedAction();
      event->accept();
      return;
    }

  event->ignore();

  return;
}


void
MzLabInputOligomerTableView::dragMoveEvent(QDragMoveEvent *event)
{
  event->setDropAction(Qt::CopyAction);
  event->accept();
}


void
MzLabInputOligomerTableView::pasteEvent()
{
  msXpS::MassType massType = pasteOrDropMassTypeCheck();

  if(massType == msXpS::MassType::MASS_NONE)
    {
      // The user did not want to answer positively to our
      // question. Conservatory behaviour.

      // qDebug() << __FILE__ << __LINE__
      //          << "massType:" << massType ;

      return;
    }

  OligomerList *oligomerList = new OligomerList("NOT_SET");

  QClipboard *clipboard = QApplication::clipboard();

  QString text = clipboard->text(QClipboard::Clipboard);

  // qDebug() << __FILE__ << __LINE__
  //          << "text:" << text;

  // Now that we have the text we can parse it. Hand over that
  // parsing to the function that handles simple text drop/paste.

  testSimpleTextDataDroppedOrPasted(text, oligomerList);

  // At this point we should have an oligomerList with oligomers in
  // it.

  if(!oligomerList->size())
    {
      delete oligomerList;

      return;
    }

  // We use a sort proxy model, but when adding oligomers to the
  // model we *have to* use the source model, that is not the proxy
  // model. Thus we have written a function in *this* TableView that
  // returns the source model.

  MzLabInputOligomerTableViewModel *tableViewModel = sourceModel();

  while(oligomerList->size())
    {
      // At this point we want to add the oligomers to the model
      // data. we simultaneously remove the oligomer from the
      // list as the ownership passes to the model.
      tableViewModel->addOligomer(oligomerList->takeFirst());
    }

  delete oligomerList;

  // Let the dialog know that we have set masses of type
  // massType. This way, we'll be able to check that later drops
  // are for the same mass type.
  mp_parentDlg->setPreviousMassType(massType);

  return;
}


void
MzLabInputOligomerTableView::dropEvent(QDropEvent *event)
{
  msXpS::MassType massType = pasteOrDropMassTypeCheck();

  if(massType == msXpS::MassType::MASS_NONE)
    {
      // The user did not want to answer positively to our
      // question. Conservatory behaviour.
      event->ignore();
      return;
    }

  OligomerList *oligomerList = new OligomerList("NOT_SET");

  // We may have data from different locations. We test them in
  // sequence. In the called functions, we get the data from the
  // dropped material into the temporary oligomerList allocated
  // right above.

  // A cleavage (CleaveOligomerTableViewMimeData).
  if(testCleaveOligomerDataDropped(event, oligomerList) == -1)
    {
      // A mass search (MassSearchOligomerTableViewMimeData).
      if(testMassSearchOligomerDataDropped(event, oligomerList) == -1)
        {
          // An arbitrary text.
          testSimpleTextDataDropped(event, oligomerList);
        }
    }

  // At this point we should have an oligomerList with oligomers in
  // it.

  if(!oligomerList->size())
    {
      event->ignore();

      delete oligomerList;

      return;
    }


  // We use a sort proxy model, but when adding oligomers to the
  // model we *have to* use the source model, that is not the proxy
  // model. Thus we have written a function in *this* TableView that
  // returns the source model.

  MzLabInputOligomerTableViewModel *tableViewModel = sourceModel();

  while(oligomerList->size())
    {
      // At this point we want to add the oligomers to the model
      // data. we simultaneously remove the oligomer from the
      // list as the ownership passes to the model.
      tableViewModel->addOligomer(oligomerList->takeFirst());
    }

  // We want the action to be a copy action and not a move
  // action because the data come from "static" data residing in
  // the tableView of the cleavage dialog window.
  event->setDropAction(Qt::CopyAction);
  event->accept();

  delete oligomerList;

  // Let the dialog know that we have set masses of type
  // massType. This way, we'll be able to check that later drops
  // are for the same mass type.
  mp_parentDlg->setPreviousMassType(massType);

  return;
}


int
MzLabInputOligomerTableView::testCleaveOligomerDataDropped(
  QDropEvent *event, OligomerList *oligomerList)
{
  PolChemDef *polChemDef = mp_mzLabWnd->polChemDef();

  const CleaveOligomerTableViewMimeData *tableViewMimeData =
    qobject_cast<const CleaveOligomerTableViewMimeData *>(event->mimeData());

  if(!tableViewMimeData)
    return -1;

  const CleaveOligomerTableView *tableView = tableViewMimeData->tableView();

  // Perform the copying of the data... The list below will
  // receive a copy of each oligomer in the drag start tableView.

  int count = tableView->selectedOligomers(oligomerList, oligomerList->size());

  if(!count)
    {
      return 0;
    }

  // We absolutely have to change the mp_polChemDef of the oligomer,
  // as it points to the polymer chemistry definition tied to the
  // sequence editor window's polymer, but we cannot make sure that
  // polymer chemistry definition will remain there. Thus we change
  // that polymer chemistry definition pointer to point to ours.

  for(int iter = 0; iter < oligomerList->size(); ++iter)
    {
      Oligomer *oligomer = oligomerList->at(iter);

      oligomer->setPolChemDef(polChemDef);
    }

  return count;
}


int
MzLabInputOligomerTableView::testMassSearchOligomerDataDropped(
  QDropEvent *event, OligomerList *oligomerList)
{
  PolChemDef *polChemDef = mp_mzLabWnd->polChemDef();

  const MassSearchOligomerTableViewMimeData *tableViewMimeData =
    qobject_cast<const MassSearchOligomerTableViewMimeData *>(
      event->mimeData());

  if(!tableViewMimeData)
    return -1;

  const MassSearchOligomerTableView *tableView = tableViewMimeData->tableView();

  // Perform the copying of the data... The list below will
  // receive a copy of each oligomer in the drag start tableView.

  int count = tableView->selectedOligomers(oligomerList, oligomerList->size());

  if(!count)
    {
      return 0;
    }

  // We absolutely have to change the mp_polChemDef of the oligomer,
  // as it points to the polymer chemistry definition tied to the
  // sequence editor window's polymer, but we cannot make sure that
  // polymer chemistry definition will remain there. Thus we change
  // that polymer chemistry definition pointer to point to ours.

  for(int iter = 0; iter < oligomerList->size(); ++iter)
    {
      Oligomer *oligomer = oligomerList->at(iter);

      oligomer->setPolChemDef(polChemDef);
    }

  return count;
}


int
MzLabInputOligomerTableView::testSimpleTextDataDroppedOrPasted(
  QString &text, OligomerList *oligomerList)
{
  int count = 0;

  // The text that we get might have a single item per line, in
  // which case this is the mass 'm', or two items per line
  // separated with a blank, then these are the mass 'm' and the
  // charge 'z'. Thre might be other data, like the name of the
  // oligomer and the coordinates. Especially if we have the text
  // that comes indirectly from massXpert.

  // First, split the text file into lines.
  QStringList stringList =
    text.split("\n", QString::SkipEmptyParts, Qt::CaseSensitive);
  // qDebug() << __FILE__ << __LINE__
  //          << "stringList:" << stringList;

  // At this point, each line of the file is in a single string of
  // the list . We have to parse each line to extract, either only
  // the mass, or the mass and the charge or the mass, the charge
  // and the specifications of the oligomer (coordinates and name).

  QString delimiter = mp_parentDlg->textFieldDelimiter();

  // qDebug() << __FILE__ << __LINE__
  //          << "delimiter:" << delimiter;

  for(int iter = 0; iter < stringList.size(); ++iter)
    {
      QString text = stringList.at(iter);

      // qDebug() << __FILE__ << __LINE__
      //          << "text:" << text;

      QStringList stringList = text.split(delimiter, QString::SkipEmptyParts);

      // qDebug() << __FILE__ << __LINE__
      //          << "stringList:" << stringList;


      int stringListSize = stringList.size();

      Oligomer *oligomer = 0;

      if(stringListSize == 0)
        {
          QMessageBox::warning(this,
                               tr("massXpert - Drag and drop"),
                               tr("%1@%2\n"
                                  "Failed to parse line.")
                                 .arg(__FILE__)
                                 .arg(__LINE__)
                                 .arg(stringList.first()));
          return -1;
        }
      else if(stringListSize == 1)
        {
          oligomer = parseOnlyMassLine(stringList);
        }
      else if(stringListSize == 2)
        {
          oligomer = parseMassAndChargeLine(stringList);
        }
      else // (stringListSize > 2)
        {
          oligomer = parseFullLine(stringList);
        }

      // At this point we should have one oligomer back from the
      // called function.
      if(!oligomer)
        {
          QMessageBox::warning(this,
                               tr("massXpert - Drag and drop"),
                               tr("%1@%2\n"
                                  "Failed to parse line:\n"
                                  "%3")
                                 .arg(__FILE__)
                                 .arg(__LINE__)
                                 .arg(stringList.first()));
          return -1;
        }
      else
        {
          oligomerList->append(oligomer);
          count++;
        }
    }
  // End of
  // for (int iter = 0; iter < stringList.size(); ++iter)

  return count;
}


int
MzLabInputOligomerTableView::testSimpleTextDataDropped(
  QDropEvent *event, OligomerList *oligomerList)
{
  if(!event->mimeData()->hasText())
    {
      return -1;
    }

  QString text = event->mimeData()->text();

  int count = testSimpleTextDataDroppedOrPasted(text, oligomerList);
  if(count == -1)
    {
      event->ignore();
      return -1;
    }

  return count;
}


Oligomer *
MzLabInputOligomerTableView::parseOnlyMassLine(QStringList &stringList)
{
  // There is only the mass in the line.

  PolChemDef *polChemDef = mp_mzLabWnd->polChemDef();

  msXpSlibmass::IonizeRule ionizeRule = mp_mzLabWnd->ionizeRule();
  int totalCharge = ionizeRule.charge() * ionizeRule.level();

  bool ok     = false;
  double mass = stringList.first().toDouble(&ok);

  if(!mass && !ok)
    {
      QMessageBox::warning(this,
                           tr("massXpert - Drag and drop"),
                           tr("%1@%2\n"
                              "Failed to convert %3 to double.\n"
                              "Make sure the proper delimiter is used.")
                             .arg(__FILE__)
                             .arg(__LINE__)
                             .arg(stringList.first()));
      return 0;
    }

  Oligomer *oligomer = 0;

  // Note that if the ionizeRule has a total charge
  //(charge() * level()), then we have to set this
  // oligomer to charged state(totalCharge ? true : false).
  oligomer = new Oligomer(polChemDef,
                          "NOT_SET" /*name*/,
                          QString("") /*description*/,
                          false /*modified*/,
                          msXpSlibmass::Ponderable(mass, mass),
                          ionizeRule,
                          CalcOptions(),
                          (totalCharge ? true : false),
                          -1,
                          -1);

  // qDebug() << __FILE__ << __LINE__
  //          << "ionizerule:" << ionizeRule.charge()
  //          << ionizeRule.level();

  // qDebug() << __FILE__ << __LINE__
  //          << "charge:" << oligomer->charge();
  // oligomer->ionizeRule().debugPutStdErr();

  return oligomer;
}


Oligomer *
MzLabInputOligomerTableView::parseMassAndChargeLine(QStringList &stringList)
{
  // One mass, one charge.

  PolChemDef *polChemDef = mp_mzLabWnd->polChemDef();

  msXpSlibmass::IonizeRule ionizeRule = mp_mzLabWnd->ionizeRule();
  int totalCharge = ionizeRule.charge() * ionizeRule.level();

  bool ok     = false;
  double mass = stringList.first().toDouble(&ok);

  if(!mass && !ok)
    {
      QMessageBox::warning(this,
                           tr("massXpert - Drag and drop"),
                           tr("%1@%2\n"
                              "Failed to convert %3 to double")
                             .arg(__FILE__)
                             .arg(__LINE__)
                             .arg(stringList.first()));
      return 0;
    }

  // Work with the charge
  int charge = stringList.at(1).toInt(&ok);

  if(!charge && !ok)
    {
      QMessageBox::warning(this,
                           tr("massXpert - Drag and drop"),
                           tr("%1@%2\n"
                              "Failed to convert %3 to int")
                             .arg(__FILE__)
                             .arg(__LINE__)
                             .arg(stringList.at(1)));
      return 0;
    }

  ionizeRule.setLevel(charge);

  // Note that if the ionizeRule has a total charge
  //(charge() * level()), then we have to set this
  // oligomer to charged state(totalCharge ? true : false).
  Oligomer *oligomer = new Oligomer(polChemDef,
                                    "NOT_SET" /*name*/,
                                    QString("") /*description*/,
                                    false /*modified*/,
                                    msXpSlibmass::Ponderable(mass, mass),
                                    ionizeRule,
                                    CalcOptions(),
                                    (totalCharge ? true : false),
                                    -1,
                                    -1);

  // qDebug() << __FILE__ << __LINE__
  //          << "charge:" << oligomer->charge();
  // oligomer->ionizeRule().debugPutStdErr();

  return oligomer;
}


Oligomer *
MzLabInputOligomerTableView::parseFullLine(QStringList &stringList)
{
  // One mass, one charge and even the name of the oligomer and of the
  // coordinates of that oligomer.

  PolChemDef *polChemDef = mp_mzLabWnd->polChemDef();

  msXpSlibmass::IonizeRule ionizeRule = mp_mzLabWnd->ionizeRule();
  int totalCharge = ionizeRule.charge() * ionizeRule.level();

  bool ok     = false;
  double mass = stringList.first().toDouble(&ok);

  if(!mass && !ok)
    {
      QMessageBox::warning(this,
                           tr("massXpert - Drag and drop"),
                           tr("%1@%2\n"
                              "Failed to convert %3 to double")
                             .arg(__FILE__)
                             .arg(__LINE__)
                             .arg(stringList.first()));
      return 0;
    }

  // Work with the charge
  int charge = stringList.at(1).toInt(&ok);

  if(!charge && !ok)
    {
      QMessageBox::warning(this,
                           tr("massXpert - Drag and drop"),
                           tr("%1@%2\n"
                              "Failed to convert %3 to int")
                             .arg(__FILE__)
                             .arg(__LINE__)
                             .arg(stringList.at(1)));

      return 0;
    }

  // Now get all we can get out of the remaining items. We know that
  // the oligomer name must have a '#' in it, and that the
  // coordinates must have [ and ] in them.

  QString oligoName = "NOT_SET";
  QString coordinates;

  int listSize = stringList.size();

  for(int iter = 2; iter < listSize; ++iter)
    {
      QString stringItem = stringList.at(iter);

      if(stringItem.contains("#"))
        {
          // That must be the name of the oligomer,
          oligoName = stringItem;
        }
      else if(stringItem.contains("["))
        {
          // That must be the name of the oligomer, Since we know
          // the coordinates, set these into the oligomer.

          // qDebug() << __FILE__ << __LINE__
          //          << "stringItem:" << stringItem;

          coordinates = stringItem;

          // qDebug() << __FILE__<< __LINE__
          //          << "coordinates:" << coordinates;
        }
    }

  ionizeRule.setLevel(charge);

  // Note that if the ionizeRule has a total charge
  //(charge() * level()), then we have to set this
  // oligomer to charged state(totalCharge ? true : false).
  Oligomer *oligomer = new Oligomer(polChemDef,
                                    oligoName,
                                    QString("") /*description*/,
                                    false /*modified*/,
                                    msXpSlibmass::Ponderable(mass, mass),
                                    ionizeRule,
                                    CalcOptions(),
                                    (totalCharge ? true : false),
                                    -1,
                                    -1);

  // Set coordinates as previously characterized.
  oligomer->setCoordinates(coordinates);

  return oligomer;
}


QString *
MzLabInputOligomerTableView::selectedDataAsPlainText()
{
  // Provide a most simple list of (m/z,z) pairs like 1021.21 2 for
  // all the selected items in the table view.

  QString *resultString = new QString();

  // Get the delimiter to separate the text fields.
  QString delimiter = mp_parentDlg->textFieldDelimiter();

  // We first have to get the selection model for the proxy model.

  QItemSelectionModel *selModel = selectionModel();

  MzLabInputOligomerTableViewModel *modelSource = sourceModel();

  // Now get the selection ranges.

  QItemSelection proxyItemSelection = selModel->selection();

  QSortFilterProxyModel *sortModel =
    static_cast<QSortFilterProxyModel *>(model());

  QItemSelection sourceItemSelection =
    sortModel->mapSelectionToSource(proxyItemSelection);

  QModelIndexList modelIndexList = sourceItemSelection.indexes();

  int modelIndexListSize = modelIndexList.size();

  // Attention, if we select one single row, our modelIndexList will
  // be of size 2, because in one single row there are two cells:
  // each cell for each column, and there are 2 columns. Thus, when
  // we iterate in the modelIndexList, we'll have to take care of
  // this and make sure we are not putting each selected row's data
  // two times. For this, we make sure we are not handling the same
  // row twice or more, by storing the processed rows in a list of
  // integers and by checking for existence of that row each time a
  // new index is processed.

  QList<int> processedRowList;

  for(int iter = 0; iter < modelIndexListSize; ++iter)
    {
      QModelIndex index = modelIndexList.at(iter);

      Q_ASSERT(index.isValid());

      // Get to know what's the row of the index.

      int row = index.row();

      if(processedRowList.contains(row))
        continue;
      else
        processedRowList.append(row);

      // Get the m/z and the z values through the use of the model's
      // data function, which expects an index, which we create
      // using the model's index() function below.

      // First the m/z.
      QModelIndex itemModelIndex =
        modelSource->index(row, MZ_LAB_INPUT_OLIGO_MASS_COLUMN);

      Q_ASSERT(itemModelIndex.isValid());

      QString massString =
        modelSource->data(itemModelIndex, Qt::DisplayRole).toString();

      // Second the charge.
      itemModelIndex =
        modelSource->index(row, MZ_LAB_INPUT_OLIGO_CHARGE_COLUMN);

      Q_ASSERT(itemModelIndex.isValid());

      QString chargeString =
        modelSource->data(itemModelIndex, Qt::DisplayRole).toString();

      // Third the name.
      itemModelIndex = modelSource->index(row, MZ_LAB_INPUT_OLIGO_NAME_COLUMN);

      Q_ASSERT(itemModelIndex.isValid());

      QString nameString =
        modelSource->data(itemModelIndex, Qt::DisplayRole).toString();

      // Fourth the coords.
      itemModelIndex =
        modelSource->index(row, MZ_LAB_INPUT_OLIGO_COORDS_COLUMN);

      Q_ASSERT(itemModelIndex.isValid());

      QString coordsString =
        modelSource->data(itemModelIndex, Qt::DisplayRole).toString();

      // Bug: if the delimiter is '%', then there is a problem.
      // resultString->append(QString("%1%2%3%4%5%6%7\n")
      //                      .arg(massString)
      //                      .arg(delimiter)
      //                      .arg(chargeString)
      //                      .arg(delimiter)
      //                      .arg(nameString)
      //                      .arg(delimiter)
      //                      .arg(coordsString));

      resultString->append(massString);
      resultString->append(delimiter);

      resultString->append(chargeString);
      resultString->append(delimiter);

      resultString->append(nameString);
      resultString->append(delimiter);

      resultString->append(coordsString);

      // Finally go to a new line.
      resultString->append("\n");
    }

  return resultString;
}


void
MzLabInputOligomerTableView::itemActivated(const QModelIndex &index)
{
  // We want to allow the user to highlight the sequence
  // corresponding to the activated item in the sequence
  // editor. This will work in two situations:

  // 1. If the item has been created upon dropping an item from a
  // massXpert clevage/fragmentation/massSearch dialog window.

  // 2. If the item originated as a text drag-and-drop or clipboard
  // import for which the text had a coordinates string. In this
  // case, the user must have connected the mzLab dialog window with
  // the sequence editor. If that connection went well, the
  // mp_parentDlg->mp_seqEditorWnd (in the parent dialog window) is
  // non-null.

  // As a general rule, we first try to get the sequence editor
  // window pointer through the oligomer. If that fails, then try
  // the mp_parentDlg->mp_seqEditorWnd.

  if(!index.isValid())
    return;

  MzLabInputOligomerTableViewSortProxyModel *sortModel =
    static_cast<MzLabInputOligomerTableViewSortProxyModel *>(model());

  QModelIndex sourceIndex = sortModel->mapToSource(index);

  int row = sourceIndex.row();

  Oligomer *oligomer = mp_oligomerList->at(row);

  // We may have two situations here with the data in the table view
  //  coming
  //
  // 1. from a drag and drop of items directly from massXpert (tree
  // views of cleavage, fragmentation or mass search). In this case,
  // the oligomer will have a polymer pointer and a property
  // providing the pointer to the sequence editor window.
  //
  // 2. from a drag and drop or a copy paste from the clipboard of
  // textual data. In this case, the oligomer that were created
  // cannot hold a polymer pointer, neither a property that provides
  // the pointer to the sequence editor window.

  SequenceEditorWnd *editorWnd = 0;

  const Polymer *polymer = oligomer->polymer();

  if(polymer)
    {
      // There must be a pointer to the sequence editor window.

      Prop *prop =
        const_cast<Oligomer *>(oligomer)->prop("SEQUENCE_EDITOR_WND");

      if(prop)
        {
          editorWnd = static_cast<SequenceEditorWnd *>(prop->data());
        }
      else
        {
          editorWnd = mp_parentDlg->sequenceEditorWnd();
        }
    }
  else
    {
      // We do not have access to the polymer. However, if we can
      // get the pointer to the sequence editor window, we can get
      // the pointer to the polymer.
      editorWnd = mp_parentDlg->sequenceEditorWnd();
      if(editorWnd)
        {
          polymer = editorWnd->polymer();
        }
    }

  if(!editorWnd || !polymer)
    {
      return;
    }

  CoordinateList *coordinateList =
    static_cast<CoordinateList *>(const_cast<Oligomer *>(oligomer));

  // Remove the previous selection, so that we can start fresh.
  editorWnd->mpa_editorGraphicsView->resetSelection();

  for(int iter = 0; iter < coordinateList->size(); ++iter)
    {
      Coordinates *coordinates = coordinateList->at(iter);

      int start = coordinates->start();
      int end   = coordinates->end();

      // qDebug() << __FILE__ << __LINE__
      //          << "oligomer:" << oligomer
      //          << "polymer:" << polymer
      //          << "start:" << start << "end:" << end;

      if(start >= polymer->size() || end >= polymer->size())
        {
          QMessageBox::warning(this,
                               tr("massXpert - Cleavage"),
                               tr("%1@%2\n"
                                  "The monomer indices do not correspond "
                                  "to a valid polymer sequence range.\n"
                                  "Avoid modifying the sequence while "
                                  "working with oligomers.")
                                 .arg(__FILE__)
                                 .arg(__LINE__),
                               QMessageBox::Ok);

          return;
        }

      editorWnd->mpa_editorGraphicsView->setSelection(
        *coordinates, true, false);
    }

  editorWnd->updateSelectedSequenceMasses();
}

} // namespace msXpSmassXpert
