/**
 * \file gui/export/export_masschroq_dialog/exportmasschroqdialog.cpp
 * \date 25/01/2019
 * \author Olivier Langella
 * \brief choose ODS export options
 */

/*******************************************************************************
 * Copyright (c) 2019 Olivier Langella <olivier.langella@u-psud.fr>.
 *
 * This file is part of i2MassChroQ.
 *
 *     i2MassChroQ 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.
 *
 *     i2MassChroQ 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 i2MassChroQ.  If not, see <http://www.gnu.org/licenses/>.
 *
 ******************************************************************************/

#include "ui_masschroq_run_dialog.h"
#include "masschroqrundialog.h"
#include <QDebug>
#include <QSettings>
#include <QMessageBox>
#include <QFileDialog>
#include <pappsomspp/core/msrun/alignment/msrunretentiontime.h>
#include <pappsomspp/core/msfile/msfileaccessor.h>
#include <pappsomspp/core/pappsoexception.h>
#include "../mainwindow.h"
#include "../../utils/utils.h"
#include "../../utils/types.h"
#include "../../config.h"
#include "../../output/masschroqjson.h"

MassChroqRunDialog::MassChroqRunDialog(MainWindow *parent,
                                       WorkerThread *p_worker)
  : QDialog(parent), ui(new Ui::MassChroqRunDialog)
{
  qDebug();
  mp_main = parent;
  ui->setupUi(this);
  this->setModal(true);

  ui->listWidget->clear();
  ui->msrun_list_combo_box->clear();

  ui->label_msrun_not_found->setHidden(true);
  ui->toolButton_2->setHidden(true);
  ui->label_check->setHidden(true);
  ui->label_msrun_not_found->setStyleSheet("QLabel { color : red; }");
  ui->msrunRefLabel->setText("");

  QSettings settings;
  ui->use_ht_condor_group->setChecked(
    settings.value("masschroq/use_htcondor", "false").toBool());
  ui->ht_condor_memory_spin->setValue(
    settings.value("masschroq/condor_request_memory", "5000").toInt());

  if(settings.value("masschroq/bin_path", "").toString().isEmpty())
    { // guess masschroq file path
      // check for masschroq.exe in i2masschroq directory
      QFileInfo file(QCoreApplication::applicationDirPath() + "/masschroq.exe");
      if(file.exists())
        { // for Windows
          settings.setValue("masschroq/bin_path", file.absoluteFilePath());
        }
      else
        { // for linux
          file.setFile("/usr/bin/masschroq");
          if(file.exists())
            { // for Linux
              settings.setValue("masschroq/bin_path", file.absoluteFilePath());
            }
          else
            {
              file.setFile("/usr/local/bin/masschroq");
              if(file.exists())
                { // for Linux
                  settings.setValue("masschroq/bin_path",
                                    file.absoluteFilePath());
                }
            }
        }
    }

  try
    {

      msp_masschroqFileParametersSp =
        parent->getProjectSp().get()->getMasschroqFileParametersSp();

      setMassChroQRunParamTab();
      mp_poModel = new QStandardItemModel(ui->group_listView);

      connect(this,
              &MassChroqRunDialog::accepted,
              mp_main,
              &MainWindow::doAcceptedMassChroqRunDialog);
      connect(this,
              &MassChroqRunDialog::operateAcceptedMassChroqRunDialog,
              mp_main,
              &MainWindow::doAcceptedMassChroqRunDialog);

      connect(this,
              &MassChroqRunDialog::operateFindBestMsrunForAlignment,
              p_worker,
              &WorkerThread::doFindBestMsrunForAlignment);
      connect(p_worker,
              &WorkerThread::findingBestMsrunForAlignmentFinished,
              this,
              &MassChroqRunDialog::setBestMsrunForAlignment);


      connect(this,
              &MassChroqRunDialog::operateCheckingMsrunFilePath,
              p_worker,
              &WorkerThread::doCheckMsrunFilePath);
      connect(p_worker,
              &WorkerThread::checkingMsrunFilePathFinished,
              this,
              &MassChroqRunDialog::setCheckMsrunFilePathOk);
    }
  catch(const pappso::PappsoException &error)
    {
      throw pappso::PappsoException(tr("Error in %1 %2 %3 :\n%4")
                                      .arg(__FUNCTION__)
                                      .arg(__FILE__)
                                      .arg(__LINE__)
                                      .arg(error.qwhat()));
    }


#if EXPERIMENTAL_FEATURES_ON == 1
#else
  ui->tabWidget->setCurrentWidget(ui->mcq3_tab);
  ui->tabWidget->setTabVisible(ui->tabWidget->currentIndex(), false);
  ui->tabWidget->setCurrentWidget(ui->sample_association_tab);
#endif
  qDebug();
}

MassChroqRunDialog::~MassChroqRunDialog()
{
  delete ui;
}

void
MassChroqRunDialog::setProjectSPtr(ProjectSp p_project)
{
  try
    {
      msp_project = p_project;
      msp_masschroqFileParametersSp =
        p_project.get()->getMasschroqFileParametersSp();

      msp_masschroqFileParametersSp.get()->load(
        p_project.get()->getProposedMassSpectrometerParameterFamily());
    }
  catch(const pappso::PappsoException &error)
    {
      throw pappso::PappsoException(tr("Error in %1 %2 %3 :\n%4")
                                      .arg(__FUNCTION__)
                                      .arg(__FILE__)
                                      .arg(__LINE__)
                                      .arg(error.qwhat()));
    }
}

void
MassChroqRunDialog::setMasschroqFileParameters()
{
  try
    {

      const pappso::TraceDetectionZivy *detection_zivy =
        dynamic_cast<const pappso::TraceDetectionZivy *>(
          msp_masschroqFileParametersSp.get()
            ->m_quantificationMethod.getTraceDetectionInterfaceCstSPtr()
            .get());

      if(detection_zivy == nullptr)
        {
          throw pappso::PappsoException(
            "m_quantificationMethod.getTraceDetectionInterfaceCstSPtr().get() "
            "== "
            "nullptr");
        }

      ui->zivyParamWidget->setZivyParams(*detection_zivy);


      ui->ms2TendencySpinBox->setValue(
        msp_masschroqFileParametersSp.get()
          ->m_alignmentMethod.getMs2TendencyWindow());
      ui->ms2SmoothingSpinBox->setValue(
        msp_masschroqFileParametersSp.get()
          ->m_alignmentMethod.getMs2SmoothingWindow());
      ui->ms1SmoothingSpinBox->setValue(
        msp_masschroqFileParametersSp.get()
          ->m_alignmentMethod.getMs1SmoothingWindow());

      ui->xicRangeWidget->setPrecision(
        msp_masschroqFileParametersSp.get()
          ->m_quantificationMethod.getXicExtractionUppersPrecisionPtr());

      if(msp_masschroqFileParametersSp.get()
           ->m_quantificationMethod.getFilterSuiteStringSPtr()
           .get() != nullptr)
        {
          ui->xic_background_noise_filter_edit_label->setText(
            msp_masschroqFileParametersSp.get()
              ->m_quantificationMethod.getXicFilter()
              .get()
              ->toString());
        }

      ui->match_between_run_checkBox->setChecked(
        msp_masschroqFileParametersSp.get()
          ->m_quantificationMethod.getMatchBetweenRun());
      ui->isotope_ratio_doubleSpinBox->setValue(
        msp_masschroqFileParametersSp.get()
          ->m_quantificationMethod.getIsotopeMinimumRatio());
    }
  catch(const pappso::PappsoException &error)
    {
      throw pappso::PappsoException(tr("Error in %1 %2 %3 :\n%4")
                                      .arg(__FUNCTION__)
                                      .arg(__FILE__)
                                      .arg(__LINE__)
                                      .arg(error.qwhat()));
    }
}


void
MassChroqRunDialog::updateMasschroqFileParameters() const
{
  qDebug();

  MasschroqFileParameters &params = *(msp_masschroqFileParametersSp.get());

  std::shared_ptr<pappso::TraceDetectionZivy> sp_detection_zivy =
    std::make_shared<pappso::TraceDetectionZivy>(
      ui->zivyParamWidget->getZivyParams());
  params.m_quantificationMethod.setTraceDetectionInterfaceCstSPtr(
    sp_detection_zivy);


  qDebug();

  params.result_file_format = TableFileFormat::tsv;

  params.alignment_groups.clear();
  if(mp_poModel->item(0, 0)->checkState() == Qt::CheckState(Qt::Checked))
    {
      qDebug() << msp_allMsrunAlignmentGroup->getMsRunAlignmentGroupName();
      params.alignment_groups.push_back(msp_allMsrunAlignmentGroup);
    }
  for(int i = 1; i < mp_poModel->rowCount(); i++)
    {
      qDebug() << mp_poModel->item(i, 0)->checkState();
      if(mp_poModel->item(i, 0)->checkState() == Qt::CheckState(Qt::Checked))
        {
          params.alignment_groups.push_back(msp_alignmentGroups.at(i - 1));
        }
    }


  params.m_alignmentMethod.setMs2TendencyWindow(
    ui->ms2TendencySpinBox->value());
  params.m_alignmentMethod.setMs2SmoothingWindow(
    ui->ms2SmoothingSpinBox->value());
  params.m_alignmentMethod.setMs1SmoothingWindow(
    ui->ms1SmoothingSpinBox->value());

  params.m_quantificationMethod.setXicExtractionLowerPrecisionPtr(
    ui->xicRangeWidget->getPrecision());
  params.m_quantificationMethod.setXicExtractionUpperPrecisionPtr(
    ui->xicRangeWidget->getPrecision());

  params.m_quantificationMethod.setXicExtractMethod(
    ui->xic_extraction_method_widget->getXicExtractionMethod());


  params.m_quantificationMethod.setMatchBetweenRun(
    ui->match_between_run_checkBox->isChecked());

  params.m_quantificationMethod.setIsotopeMinimumRatio(
    ui->isotope_ratio_doubleSpinBox->value());

  qDebug() << (int)params.m_quantificationMethod.getXicExtractMethod();
}


void
MassChroqRunDialog::reject()
{
  msp_project = nullptr;
  QDialog::reject();
}

void
MassChroqRunDialog::accept()
{

  m_masschroqBatchParam.isShape     = ui->shape_checkBox->isChecked();
  m_masschroqBatchParam.shapeMargin = ui->shape_rt_spinBox->value();
  m_masschroqBatchParam.isTrace     = ui->trace_checkBox->isChecked();
  if(ui->use_ht_condor_group->isChecked())
    {
      QSettings settings;
      settings.setValue("masschroq/condor_disk_usage",
                        ui->ht_condor_memory_spin->value());
    }

  QString error_message;

  if((ui->xicRangeWidget->getPrecision()->unit() ==
      pappso::Enums::PrecisionUnit::ppm) ||
     (ui->xicRangeWidget->getPrecision()->unit() ==
      pappso::Enums::PrecisionUnit::dalton))
    { // ok
    }
  else
    {
      error_message =
        QString(tr("MassChroQ can only work with ppm or dalton units. Please "
                   "change the XIC extraction precision unit."));
    }
  // read form


  // check if at least one group is checked
  int count = 0;
  for(int i = 0; i < mp_poModel->rowCount(); i++)
    {
      if(mp_poModel->item(i, 0)->checkState() == Qt::CheckState(Qt::Checked))
        {
          count++;
        }
    }
  if(count == 0)
    {
      error_message =
        QString(tr("You have to select at least one alignment group to run "
                   "MasschroQ or to save the MassChroQml!"));
    }

  if(error_message.isEmpty())
    {
      updateMasschroqFileParameters();
      // ZivyParams zivy_params = ui->zivyParamWidget->getZivyParams();
      // zivy_params.saveSettings();
      msp_project = nullptr;
      QDialog::accept();
    }
  else
    {
      QMessageBox msgBox;
      msgBox.setWindowTitle(tr("MassChroQ parameter problem"));
      msgBox.setText(error_message);
      msgBox.setIcon(QMessageBox::Critical);
      msgBox.exec();
    }
}


void
MassChroqRunDialog::doCheckMsrunFilepath()
{

  mp_main->showWaitingMessage(tr("Checking MSrun files path"));
  emit operateCheckingMsrunFilePath(msp_project);
}

void
MassChroqRunDialog::doBrowseMsrunDirectory()
{

  QSettings settings;
  QString path     = settings.value("path/mzdatadir", "").toString();
  QString filename = QFileDialog::getExistingDirectory(
    this, tr("Choose directory to look for MS runs"), QString("%1").arg(path));

  if(!filename.isEmpty())
    {
      QDir parent(filename);
      // parent.cdUp();
      settings.setValue("path/mzdatadir", parent.canonicalPath());
      doCheckMsrunFilepath();
      qDebug() << parent.absolutePath();
    }
}


void
MassChroqRunDialog::doFindBestMsrunForAlignment()
{
  if(msp_selected_group != nullptr)
    {
      mp_main->showWaitingMessage(tr("Looking for MSrun reference"));

      emit operateFindBestMsrunForAlignment(msp_project, msp_selected_group);
    }
  else
    {
      QMessageBox::warning(
        this,
        "Select a group",
        "The reference is linked to a group.\n You have to select one.");
    }
}

void
MassChroqRunDialog::setBestMsrunForAlignment(MsRunSp best_msrun_sp)
{

  mp_main->hideWaitingMessage();
  if(best_msrun_sp != nullptr)
    {
      qDebug() << best_msrun_sp.get();

      ui->msrunRefLabel->setText(best_msrun_sp.get()->getSampleName());

      int index = 0;
      for(MsRunSp ms_run : msp_selected_group->getMsRunsInAlignmentGroup())
        {
          if(ms_run == best_msrun_sp)
            {
              ui->msrun_list_combo_box->setCurrentIndex(index);
              msp_selected_group->setMsRunReference(best_msrun_sp);
            }
          index++;
        }
    }
}

void
MassChroqRunDialog::setCheckMsrunFilePathOk(MsRunSp msrun_sp)
{
  mp_main->hideWaitingMessage();
  if(msrun_sp == nullptr)
    {
      ui->label_check->setHidden(false);
      ui->label_msrun_not_found->setHidden(true);
      ui->toolButton_2->setHidden(true);
    }
  else
    {
      ui->label_msrun_not_found->setText(
        tr("\"%1\" not found : Please choose the directory")
          .arg(msrun_sp.get()->getFileName()));
      ui->label_msrun_not_found->setHidden(false);
      ui->toolButton_2->setHidden(false);
    }
}

void
MassChroqRunDialog::setAlignmentGroups()
{
  // Add all msrun stored group (automatic group)
  try
    {
      msp_allMsrunAlignmentGroup = msp_project->getAllMsRunAlignmentGroup();
      QStandardItem *all_samples_item = new QStandardItem(
        msp_allMsrunAlignmentGroup->getMsRunAlignmentGroupName());
      all_samples_item->setCheckable(true);
      all_samples_item->setData(Qt::Unchecked, Qt::CheckStateRole);
      mp_poModel->setItem(0, all_samples_item);

      // Add classical groups
      msp_alignmentGroups = msp_project->getMsRunAlignmentGroupList();
      int i               = 1;
      for(MsRunAlignmentGroupSp group : msp_alignmentGroups)
        {
          QStandardItem *new_item =
            new QStandardItem(group->getMsRunAlignmentGroupName());
          new_item->setCheckable(true);
          new_item->setData(Qt::Unchecked, Qt::CheckStateRole);
          mp_poModel->setItem(i, new_item);
          i++;
        }
      ui->group_listView->setModel(mp_poModel);
    }
  catch(const pappso::PappsoException &error)
    {
      throw pappso::PappsoException(tr("Error in %1 %2 %3 :\n%4")
                                      .arg(__FUNCTION__)
                                      .arg(__FILE__)
                                      .arg(__LINE__)
                                      .arg(error.qwhat()));
    }
}

void
MassChroqRunDialog::doShowMsRunsInAlignmentGroup(QModelIndex index)
{
  ui->listWidget->clear();
  ui->msrun_list_combo_box->clear();
  if(index.row() == 0)
    {
      msp_selected_group = msp_allMsrunAlignmentGroup;
    }
  else
    {
      msp_selected_group = msp_alignmentGroups.at(index.row() - 1);
    }
  ui->groupBox_5->setTitle("Samples (MS runs) in group \"" +
                           msp_selected_group->getMsRunAlignmentGroupName() +
                           "\"");
  for(MsRunSp ms_run : msp_selected_group->getMsRunsInAlignmentGroup())
    {
      ui->listWidget->addItem(ms_run->getSampleName());
      ui->msrun_list_combo_box->addItem(ms_run->getSampleName());
    }
  if(msp_selected_group->getMsRunReference() != nullptr)
    {
      int ref_index = 0;
      for(MsRunSp ms_run : msp_selected_group->getMsRunsInAlignmentGroup())
        {
          if(ms_run == msp_selected_group->getMsRunReference())
            {
              ui->msrun_list_combo_box->setCurrentIndex(ref_index);
            }
          ref_index++;
        }
    }
  else
    {
      ui->msrun_list_combo_box->setCurrentIndex(-1);
    }
}

void
MassChroqRunDialog::doUpdateReferenceInSelectedGroup(int index)
{
  int temp_index = 0;
  for(MsRunSp ms_run : msp_selected_group->getMsRunsInAlignmentGroup())
    {
      if(temp_index == index)
        {

          ui->msrunRefLabel->setText(ms_run.get()->getSampleName());
          msp_selected_group->setMsRunReference(ms_run);
        }
      temp_index++;
    }
  qDebug() << index;
}

void
MassChroqRunDialog::doRunMassChroQ()
{
  m_runMassChroqAsked = true;
  QSettings settings;
  settings.setValue("masschroq/use_htcondor",
                    ui->use_ht_condor_group->isChecked());
  settings.setValue("masschroq/condor_request_memory",
                    ui->ht_condor_memory_spin->value());
  accept();
}

void
MassChroqRunDialog::doSaveMassChroQML()
{
  m_runMassChroqAsked = false;
  // updateMasschroqFileParameters();
  accept();
}


void
MassChroqRunDialog::doSaveJsonFile()
{

  updateMasschroqFileParameters();
  QString filename = QFileDialog::getSaveFileName(
    this,
    tr("Save MassChroq JSON file"),
    tr("%1.json").arg(msp_project.get()->getProjectName()),
    tr("MassChroq (*.json)"));

  if(filename.isEmpty())
    {
      return;
    }


  msp_project.get()->checkPsimodCompliance();
  MassChroQjson output(msp_project);


  QJsonDocument doc;
  output.populateJsonDocument(doc);


  QFile jsonf(filename);
  if(jsonf.open(QIODevice::WriteOnly))
    {
      jsonf.write(doc.toJson());
      jsonf.close();
    }
  else
    {
      throw pappso::PappsoException(
        tr("Unable to write json file %1")
          .arg(QFileInfo(filename).absoluteFilePath()));
    }
}


void
MassChroqRunDialog::doBrowseMassChroQBin()
{
  QSettings settings;
  QString path = settings.value("masschroq/bin_path", "").toString();

  QString filename = QFileDialog::getOpenFileName(
    this, tr("Select the MassChroQ executable"), tr("%1").arg(path));

  if(!filename.isEmpty())
    {
      ui->masschroq_bin_path_line->setText(filename);
      settings.setValue("masschroq/bin_path", filename);
    }
}

void
MassChroqRunDialog::doBrowseTempDir()
{
  QSettings settings;
  QString path = settings.value("masschroq/tmp_dir_path", "").toString();

  QString filename = QFileDialog::getExistingDirectory(
    this, tr("Select the MassCHroQ executable"), tr("%1").arg(path));

  if(!filename.isEmpty())
    {
      ui->temporary_directory_line->setText(filename);
      settings.setValue("masschroq/tmp_dir_path", filename);
    }
}


bool
MassChroqRunDialog::getMassChroqRunStatus()
{
  return m_runMassChroqAsked;
}

MassChroQRunBatch
MassChroqRunDialog::getMassChroqRunBatchParam()
{
  return m_masschroqBatchParam;
}

void
MassChroqRunDialog::setMassChroqRunBatchParam(QString masschroqml_file)
{
  QSettings settings;
  m_masschroqBatchParam.masschroq_bin_path =
    ui->masschroq_bin_path_line->text().trimmed();
  settings.setValue("masschroq/bin_path",
                    m_masschroqBatchParam.masschroq_bin_path);
  m_masschroqBatchParam.masschroq_temporary_dir_path =
    ui->temporary_directory_line->text().trimmed();
  settings.setValue("masschroq/tmp_dir_path",
                    m_masschroqBatchParam.masschroq_temporary_dir_path);
  m_masschroqBatchParam.number_cpu = ui->cpu_spinbox->value();
  settings.setValue("masschroq/nbr_cpu", ui->cpu_spinbox->value());
  m_masschroqBatchParam.masschroq_json_path = masschroqml_file;

  m_masschroqBatchParam.isShape     = ui->shape_checkBox->isChecked();
  m_masschroqBatchParam.shapeMargin = ui->shape_rt_spinBox->value();
  m_masschroqBatchParam.isTrace     = ui->trace_checkBox->isChecked();
}

void
MassChroqRunDialog::setMassChroQRunParamTab()
{
  QSettings settings;
  ui->masschroq_bin_path_line->setText(
    settings.value("masschroq/bin_path").toString());
  ui->temporary_directory_line->setText(
    settings.value("masschroq/tmp_dir_path").toString());
  ui->cpu_spinbox->setMaximum(QThread::idealThreadCount());
  if(settings.value("masschroq/nbr_cpu").toInt() <= QThread::idealThreadCount())
    {
      ui->cpu_spinbox->setValue(settings.value("masschroq/nbr_cpu").toInt());
    }
  else
    {
      ui->cpu_spinbox->setValue(1);
    }
}

void
MassChroqRunDialog::updateAlignmentGroupsStatus(QString masschroqml_file)
{
  for(int i = 0; i < mp_poModel->rowCount(); i++)
    {
      if(mp_poModel->item(i, 0)->checkState() == Qt::CheckState(Qt::Checked))
        {
          MsRunAlignmentGroupSp group;
          if(i == 0) // All msruns item
            {
              group = msp_allMsrunAlignmentGroup;
            }
          else
            {
              group = msp_alignmentGroups.at(i - 1);
            }
          group->savePostMassChroqmlParameters(masschroqml_file);
          if(getMassChroqRunStatus() == true)
            {
              group->setMassChroQRunStatus(AlignmentGroupStatus::masschroq_run);
            }
          else
            {
              group->setMassChroQRunStatus(
                AlignmentGroupStatus::masschroqml_written);
            }
        }
    }
}

void
MassChroqRunDialog::checkTabToEnableMassChroQRun(int tab_position)
{
  qDebug() << tab_position;
  if(tab_position == 3) // MassChroQ execution tab_position
    {
      ui->runMCQButton->setEnabled(true);
    }
  else
    {
      ui->runMCQButton->setEnabled(false);
    }
}
