
/*******************************************************************************
 * Copyright (c) 2019 Olivier Langella <olivier.langella@u-psud.fr>.
 *
 * This file is part of XTPcpp.
 *
 *     XTPcpp 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.
 *
 *     XTPcpp 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 XTPcpp.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Contributors:
 *     Thomas Renne <thomas.renne@u-psud.fr>. - initial API and
 *implementation
 ******************************************************************************/

#include "msidentificationtablemodel.h"

#include <QDebug>
#include <pappsomspp/pappsoexception.h>
#include "msidentificationlistwindow.h"
#include "../../../utils/identificationdatasourcestore.h"

MsIdentificationTableModel::MsIdentificationTableModel(
  MsIdentificationListWindow *ms_id_list_window, Project *project)
  : QAbstractTableModel(ms_id_list_window)
{
  m_ms_id_list_window = ms_id_list_window;
  mp_project          = project;
  msp_workerStatus    = std::make_shared<MsIdListWorkerStatus>();

  mp_movie = new QMovie(":icons/resources/icons/icon_wait.gif");
  mp_movie->start();
  mp_movie->setScaledSize(QSize(20, 20));

  connect(mp_movie,
          &QMovie::frameChanged,
          this,
          &MsIdentificationTableModel::onMsIdentificationDataChanged);
  connect(this,
          &MsIdentificationTableModel::alignmentGroupChanged,
          this,
          &MsIdentificationTableModel::onMsIdentificationDataChanged);
}

MsIdentificationTableModel::~MsIdentificationTableModel()
{
  delete mp_movie;
}

void
MsIdentificationTableModel::setIdentificationDataSourceSpList(
  std::vector<IdentificationDataSourceSp> &identificationDataSourceSpList)
{
  qDebug() << " identificationDataSourceSpList.size()="
           << identificationDataSourceSpList.size();

  beginResetModel();
  m_identificationDataSourceSpList = identificationDataSourceSpList;
  endResetModel();

  m_ms_id_list_window->resizeColumnsToContents();
}

std::vector<IdentificationDataSourceSp>
MsIdentificationTableModel::getIdentificationDataSourceSpList() const
{
  return m_identificationDataSourceSpList;
}

int
MsIdentificationTableModel::rowCount(const QModelIndex &parent
                                     [[maybe_unused]]) const
{
  // qDebug() << "MsIdentificationTableModel::rowCount begin ";
  if(m_identificationDataSourceSpList.size() != 0)
    {
      return (int)m_identificationDataSourceSpList.size();
    }
  return 0;
}
int
MsIdentificationTableModel::columnCount(const QModelIndex &parent
                                        [[maybe_unused]]) const
{
  return (std::int8_t)msIdentificationListColumn::last;
}
const QString
MsIdentificationTableModel::getTitle(msIdentificationListColumn column)
{
  qDebug() << "MsIdentificationTableModel::getTitle begin ";
  return MsIdentificationTableModel::getTitle((std::int8_t)column);
  // qDebug() << "MsIdentificationTableModel::getTitle end ";
}
const QString
MsIdentificationTableModel::getDescription(msIdentificationListColumn column)
{
  qDebug() << "MsIdentificationTableModel::columnCount begin ";
  return MsIdentificationTableModel::getDescription((std::int8_t)column);
  // qDebug() << "MsIdentificationTableModel::columnCount end ";
}

const QString
MsIdentificationTableModel::getTitle(std::int8_t column)
{
  switch(column)
    {
      case(std::int8_t)msIdentificationListColumn::align_groups:
        return "Alignments Groups";
        break;
      case(std::int8_t)msIdentificationListColumn::run_id:
        return "Identification Run ID";
        break;
      case(std::int8_t)msIdentificationListColumn::id_file:
        return "Identification result file";
        break;
      case(std::int8_t)msIdentificationListColumn::mzML:
        return "Ms Run File";
        break;
      case(std::int8_t)msIdentificationListColumn::engine_name:
        return "Identification engine";
        break;
      case(std::int8_t)msIdentificationListColumn::fasta_file:
        return "Fasta file(s)";
        break;
      case(std::int8_t)msIdentificationListColumn::nbr_spectrum:
        return "Total spectra used";
        break;
      case(std::int8_t)msIdentificationListColumn::nbr_peptide:
        return "Total spectra assigned";
        break;
      case(std::int8_t)msIdentificationListColumn::nbr_unique:
        return "Total unique assigned";
        break;
      case(std::int8_t)msIdentificationListColumn::percent_assign:
        return "Assignment %";
        break;
      case(std::int8_t)msIdentificationListColumn::id_msrun_id:
        return "Sample ID";
        break;
      case(std::int8_t)msIdentificationListColumn::nb_ms_1:
        return "Total MS1";
        break;
      case(std::int8_t)msIdentificationListColumn::nb_ms_2:
        return "Total MS2";
        break;
      case(std::int8_t)msIdentificationListColumn::nb_ms_3:
        return "Total MS3";
        break;
      case(std::int8_t)msIdentificationListColumn::nb_tic_1:
        return "TIC MS1";
        break;
      case(std::int8_t)msIdentificationListColumn::nb_tic_2:
        return "TIC MS2";
        break;
      case(std::int8_t)msIdentificationListColumn::nb_tic_3:
        return "TIC MS3";
        break;
    }
  return "";
}

const QString
MsIdentificationTableModel::getDescription(std::int8_t column)
{

  qDebug() << "begin " << column;
  switch(column)
    {
      case(std::int8_t)msIdentificationListColumn::align_groups:
        return "Groups for Quantification (Right click to add a MsRun "
               "Alignment group)";
        break;
      case(std::int8_t)msIdentificationListColumn::run_id:
        return "Identification of RunID";
        break;
      case(std::int8_t)msIdentificationListColumn::id_file:
        return "Identification File pathway";
        break;
      case(std::int8_t)msIdentificationListColumn::mzML:
        return "peak-list data file pathway";
        break;
      case(std::int8_t)msIdentificationListColumn::engine_name:
        return "Engine Name";
        break;
      case(std::int8_t)msIdentificationListColumn::fasta_file:
        return "Fasta files used in identification";
        break;
      case(std::int8_t)msIdentificationListColumn::nbr_spectrum:
        return "Number of Spectrum";
        break;
      case(std::int8_t)msIdentificationListColumn::nbr_peptide:
        return "Number of peptide identified";
        break;
      case(std::int8_t)msIdentificationListColumn::nbr_unique:
        return "Number of unique peptide";
        break;
      case(std::int8_t)msIdentificationListColumn::percent_assign:
        return "Percentage of Assignation";
        break;
      case(std::int8_t)msIdentificationListColumn::id_msrun_id:
        return "Identification of MsRunID";
        break;
      case(std::int8_t)msIdentificationListColumn::nb_ms_1:
        return "Number of ms1 spectra";
        break;
      case(std::int8_t)msIdentificationListColumn::nb_ms_2:
        return "Number of ms2 spectra";
        break;
      case(std::int8_t)msIdentificationListColumn::nb_ms_3:
        return "Number of ms3 spectra";
        break;
      case(std::int8_t)msIdentificationListColumn::nb_tic_1:
        return "Number of TIC 1";
        break;
      case(std::int8_t)msIdentificationListColumn::nb_tic_2:
        return "Number of TIC 2 ";
        break;
      case(std::int8_t)msIdentificationListColumn::nb_tic_3:
        return "Number of TIC 3";
        break;
    }
  return "";
}

QVariant
MsIdentificationTableModel::headerData(int section,
                                       Qt::Orientation orientation,
                                       int role) const
{
  if(orientation == Qt::Horizontal)
    {
      switch(role)
        {
          case Qt::DisplayRole:
            // qDebug() << section;
            return QVariant(getTitle(section));
            break;
          case Qt::ToolTipRole:
            return QVariant(getDescription(section));
            break;
          case Qt::SizeHintRole:
            return QSize(getColumnWidth(section), 40);
            break;
        }
    }
  return QVariant();
}

int
MsIdentificationTableModel::getColumnWidth(int column)
{
  switch(column)
    {
      case(int)msIdentificationListColumn::align_groups:
        return 160;
        break;
      case(int)msIdentificationListColumn::run_id:
        return 200;
        break;
      case(int)msIdentificationListColumn::id_file:
        return 400;
        break;
      case(int)msIdentificationListColumn::mzML:
        return 400;
        break;
      case(int)msIdentificationListColumn::nbr_spectrum:
        return 160;
        break;
      case(int)msIdentificationListColumn::nbr_peptide:
        return 160;
        break;
      case(int)msIdentificationListColumn::nbr_unique:
        return 160;
        break;
      case(int)msIdentificationListColumn::engine_name:
        return 160;
        break;
    }
  return 120;
}

msIdentificationListColumn
MsIdentificationTableModel::getMsIdentificationListColumn(std::int8_t column)
{
  return static_cast<msIdentificationListColumn>(column);
}

QVariant
MsIdentificationTableModel::data(const QModelIndex &index, int role) const
{
  int row = index.row();
  int col = index.column();

  switch(role)
    {
      case Qt::SizeHintRole:
        qDebug() << MsIdentificationTableModel::getColumnWidth(col);

        return QSize(MsIdentificationTableModel::getColumnWidth(col), 30);
        break;
      case Qt::DisplayRole:
        switch(col)
          {
            case(std::int8_t)msIdentificationListColumn::run_id:
              return QVariant(
                m_identificationDataSourceSpList.at(row)->getXmlId());
          }
        if(col == (std::int8_t)msIdentificationListColumn::align_groups)
          {
            MsRunAlignmentGroupSp group =
              m_identificationDataSourceSpList.at(row)
                ->getMsRunSp()
                ->getAlignmentGroup();
            if(group == nullptr)
              {
                return QVariant("Select Group");
              }
            else
              {
                return QVariant(group.get()->getMsRunAlignmentGroupName());
              }
          }
        if(col == (std::int8_t)msIdentificationListColumn::id_file)
          {
            QFileInfo fi(
              m_identificationDataSourceSpList.at(row)->getResourceName());
            return QVariant(fi.fileName());
          }

        if(col == (std::int8_t)msIdentificationListColumn::mzML)
          {
            QFileInfo fi(m_identificationDataSourceSpList.at(row)
                           ->getMsRunSp()
                           .get()
                           ->getFileName());
            return QVariant(fi.fileName());
          }
        if(col == (std::int8_t)msIdentificationListColumn::engine_name)
          {
            return QVariant(m_identificationDataSourceSpList.at(row)
                              ->getIdentificationEngineName() +
                            " " +
                            m_identificationDataSourceSpList.at(row)
                              ->getIdentificationEngineVersion());
          }
        if(col == (std::int8_t)msIdentificationListColumn::fasta_file)
          {
            std::vector<FastaFileSp> fasta_list =
              m_identificationDataSourceSpList.at(row)->getFastaFileList();
            if(fasta_list.size() == 1)
              {
                return QVariant(fasta_list.front()->getFilename());
              }
            else if(fasta_list.size() == 0)
              {
                return QVariant("No fasta file used");
              }
            else // more than one fasta file listed
              {
                return QVariant(QString::number(fasta_list.size()) +
                                " fasta used");
              }
          }
        if(col == (std::int8_t)msIdentificationListColumn::id_msrun_id)
          {
            return QVariant(m_identificationDataSourceSpList.at(row)
                              ->getMsRunSp()
                              ->getXmlId());
          }
        if(col == (std::int8_t)msIdentificationListColumn::nbr_peptide)
          {
            return QVariant(
              m_identificationDataSourceSpList.at(row)
                ->getIdentificationEngineStatistics(
                  IdentificationEngineStatistics::total_spectra_assigned));
          }
        if(col == (std::int8_t)msIdentificationListColumn::nbr_unique)
          {
            return QVariant(
              m_identificationDataSourceSpList.at(row)
                ->getIdentificationEngineStatistics(
                  IdentificationEngineStatistics::total_unique_assigned));
          }
        if(col == (std::int8_t)msIdentificationListColumn::nbr_spectrum)
          {
            return QVariant(
              m_identificationDataSourceSpList.at(row)
                ->getIdentificationEngineStatistics(
                  IdentificationEngineStatistics::total_spectra_used));
          }
        if(col == (std::int8_t)msIdentificationListColumn::percent_assign)
          {
            return QVariant(
              m_identificationDataSourceSpList.at(row)
                ->getIdentificationEngineStatistics(
                  IdentificationEngineStatistics::total_spectra_assigned)
                .toDouble() /
              m_identificationDataSourceSpList.at(row)
                ->getIdentificationEngineStatistics(
                  IdentificationEngineStatistics::total_spectra_used)
                .toDouble());
          }
        if(col == (std::int8_t)msIdentificationListColumn::nb_ms_1)
          {
            return getMsRunStatisticsNumber(row,
                                            MsRunStatistics::total_spectra_ms1);
          }
        if(col == (std::int8_t)msIdentificationListColumn::nb_ms_2)
          {
            return getMsRunStatisticsNumber(row,
                                            MsRunStatistics::total_spectra_ms2);
          }
        if(col == (std::int8_t)msIdentificationListColumn::nb_ms_3)
          {
            return getMsRunStatisticsNumber(row,
                                            MsRunStatistics::total_spectra_ms3);
          }
        if(col == (std::int8_t)msIdentificationListColumn::nb_tic_1)
          {
            return getMsRunStatisticsNumber(row,
                                            MsRunStatistics::tic_spectra_ms1);
          }
        if(col == (std::int8_t)msIdentificationListColumn::nb_tic_2)
          {
            return getMsRunStatisticsNumber(row,
                                            MsRunStatistics::tic_spectra_ms2);
          }
        if(col == (std::int8_t)msIdentificationListColumn::nb_tic_3)
          {
            return getMsRunStatisticsNumber(row,
                                            MsRunStatistics::tic_spectra_ms3);
          }
        return QVariant();

      case Qt::ToolTipRole:
        if(col == (std::int8_t)msIdentificationListColumn::id_file)
          {
            return QVariant(
              m_identificationDataSourceSpList.at(row)->getResourceName());
          }

        if(col == (std::int8_t)msIdentificationListColumn::mzML)
          {
            return QVariant(m_identificationDataSourceSpList.at(row)
                              ->getMsRunSp()
                              .get()
                              ->getFileName());
          }
        if(col == (std::int8_t)msIdentificationListColumn::fasta_file)
          {
            std::vector<FastaFileSp> fasta_list =
              m_identificationDataSourceSpList.at(row)->getFastaFileList();
            QString tool_tip;
            for(FastaFileSp fasta_file : fasta_list)
              {
                tool_tip += fasta_file->getAbsoluteFilePath() + "\n";
              }
            return QVariant(tool_tip);
          }
        return QVariant();

      case Qt::DecorationRole:
        if(col == (std::int8_t)msIdentificationListColumn::nb_ms_1)
          {
            return showMsRunStatisticsStatus(row);
          }
        if(col == (std::int8_t)msIdentificationListColumn::nb_ms_2)
          {
            return showMsRunStatisticsStatus(row);
          }
        if(col == (std::int8_t)msIdentificationListColumn::nb_ms_3)
          {
            return showMsRunStatisticsStatus(row);
          }
        if(col == (std::int8_t)msIdentificationListColumn::nb_tic_1)
          {
            return showMsRunStatisticsStatus(row);
          }
        if(col == (std::int8_t)msIdentificationListColumn::nb_tic_2)
          {
            return showMsRunStatisticsStatus(row);
          }
        if(col == (std::int8_t)msIdentificationListColumn::nb_tic_3)
          {
            return showMsRunStatisticsStatus(row);
          }
        return QVariant();

      case Qt::FontRole:
        QFont font;
        if(m_identificationDataSourceSpList.at(row)
             ->getMsRunSp()
             ->getAlignmentGroup() != nullptr)
          {
            if(m_identificationDataSourceSpList.at(row)
                 ->getMsRunSp()
                 ->getAlignmentGroup()
                 ->getMsRunReference() ==
               m_identificationDataSourceSpList.at(row)->getMsRunSp())
              {
                font.setBold(true);
              }
          }
        return font;
    }
  return QVariant();
}

bool
MsIdentificationTableModel::setData(const QModelIndex &index,
                                    const QVariant &new_data,
                                    int role)
{
  if(role == Qt::EditRole)
    {
      if(index.column() ==
         (std::int8_t)msIdentificationListColumn::align_groups)
        {
          emit projectStatusChanged();
          if(!new_data.isNull())
            {
              foreach(MsRunAlignmentGroupSp msrun_alignment_group,
                      mp_project->getMsRunAlignmentGroupList())
                {
                  if(msrun_alignment_group->getMsRunAlignmentGroupName() ==
                     new_data)
                    {
                      MsRunAlignmentGroupSp existing_msrun_align_group =
                        m_identificationDataSourceSpList.at(index.row())
                          ->getMsRunSp()
                          ->getAlignmentGroup();
                      if(existing_msrun_align_group != nullptr)
                        {
                          existing_msrun_align_group.get()
                            ->removeMsRunFromMsRunAlignmentGroupList(
                              m_identificationDataSourceSpList.at(index.row())
                                ->getMsRunSp());
                        }
                      m_identificationDataSourceSpList.at(index.row())
                        ->getMsRunSp()
                        ->setAlignmentGroup(msrun_alignment_group);
                      return true;
                    }
                }
            }
          else
            {
              m_identificationDataSourceSpList.at(index.row())
                ->getMsRunSp()
                ->setAlignmentGroup(nullptr);
              return true;
            }
        }
      return false;
    }
  return false;
}


Qt::ItemFlags
MsIdentificationTableModel::flags(const QModelIndex &idx) const
{
  if(idx.column() == (std::int8_t)msIdentificationListColumn::align_groups)
    {
      return Qt::ItemIsEnabled | Qt::ItemIsSelectable; // | Qt::ItemIsEditable;
    }
  else
    {
      return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
    }
}

QVariant
MsIdentificationTableModel::getMsRunStatisticsNumber(
  int row, MsRunStatistics column) const
{
  WorkerStatus temp =
    msp_workerStatus.get()->getStatus(m_identificationDataSourceSpList.at(row));
  QVariant var_return;
  switch(temp)
    {
      case WorkerStatus::Ready:
        var_return = getMsRunStatisticsFollowingFormat(
          m_identificationDataSourceSpList.at(row)->getMsRunSp(), column);
        emit projectStatusChanged();
        break;
      case WorkerStatus::NotReady:
        var_return = QVariant("");
        break;
      case WorkerStatus::Error:
        var_return = QVariant("Error");
        break;

      case WorkerStatus::FileNotFound:
        var_return = QVariant("file not found!");
        break;

      case WorkerStatus::Running:
        var_return = QVariant("");
        break;

      case WorkerStatus::Waiting:
        var_return = QVariant("Wait");
        break;
      default:
        break;
    }
  return var_return;
}

QVariant
MsIdentificationTableModel::showMsRunStatisticsStatus(int row) const
{

  WorkerStatus temp =
    msp_workerStatus.get()->getStatus(m_identificationDataSourceSpList.at(row));
  QVariant var_return;
  switch(temp)
    {
      case WorkerStatus::Running:
        var_return = mp_movie->currentImage();
        break;

      default:
        break;
    }
  return var_return;
}


void
MsIdentificationTableModel::onMsIdentificationDataChanged()
{
  emit layoutAboutToBeChanged();
  emit layoutChanged();
}

void
MsIdentificationTableModel::changeWaitingQueue(int row)
{
  msp_workerStatus->changeWaitingQueue(row);
}

void
MsIdentificationTableModel::stopThreads()
{
  msp_workerStatus.get()->stopThreads();
}

QVariant
MsIdentificationTableModel::getMsRunStatisticsFollowingFormat(
  MsRunSp msrun_sp, MsRunStatistics column) const
{
  QString raw_format = QFileInfo(msrun_sp->getFileName()).completeSuffix();

  if(raw_format != "tdf")
    {
      return msrun_sp->getMsRunStatistics(column);
    }
  else
    {
      if(column == MsRunStatistics::tic_spectra_ms1 ||
         column == MsRunStatistics::tic_spectra_ms2 ||
         column == MsRunStatistics::tic_spectra_ms3)
        {
          return QVariant("NA");
        }
      else
        {
          return msrun_sp->getMsRunStatistics(column);
        }
    }
}
