/***************************************************************************
**
**  This file is part of dinver.
**
**  dinver 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.
**
**  dinver 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 Foobar.  If not, see <http://www.gnu.org/licenses/>
**
**  See http://www.geopsy.org for more information.
**
**  Created: 2005-10-31
**  Copyright: 2005-2019
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include <DinverCore.h>
#include <QGpGuiTools.h>

#include "MainWindow.h"
#include "ThreadLauncher.h" 
#include "ProcessStatus.h"
#include "InversionThread.h"
#include "PluginSelector.h"
#include "SelectPSViewer.h"
#include "WindowEnvironment.h"
#include "dinverInstallPath.h"
#include "dinverVersion.h"
#include "XMLDinverGui.h"

MainWindow::MainWindow(QWidget *parent)
    : MultiDocumentWindow(parent)
{
  TRACE;
  resize(QSize(778, 533).expandedTo(minimumSizeHint()));
  Settings::getRect(this, "MainWindow");
  setIconSize(QSize(24, 24));
  setWindowTitle(tr("dinver"));

  connect(this, SIGNAL(pluginsReady()), this, SLOT(setToolsMenuState()), Qt::QueuedConnection);

  UpdateIcon * liveUpdate=new UpdateIcon(statusBar());
  liveUpdate->setName("dinver");
  liveUpdate->setVersion(DINVER_VERSION);
  liveUpdate->setVersionType(DINVER_VERSION_TYPE);

  DinverPluginSettings reg;
  liveUpdate->addPlugins(reg.pluginList());
  statusBar()->addPermanentWidget(liveUpdate, 0);

  // Progress and status bar
  _progressBar=new ProgressBar(statusBar());
  _progressBar->setMaximumWidth(100);
  _progressBar->setMaximumHeight(20);
  _progressBar->setMaximum(100);
  _progressBar->setValue(0);
  statusBar()->addPermanentWidget(_progressBar, 0);
  statusBar()->setSizeGripEnabled(true);
  statusBar()->showMessage(tr("Ready"), 2000);

  _logs=nullptr;
  _runs=nullptr;
  _status=nullptr;
  _targetDock=nullptr;
  _paramDock=nullptr;
  _plugin=nullptr;
  _modified=false;
}

MainWindow::~MainWindow()
{
  TRACE;
  Settings::setRect(this, "MainWindow");
  for(ThreadList::iterator it=_threads.begin();it!=_threads.end();++it) {
    delete *it;
  }
  if(_plugin) {
    QSettings& reg=CoreApplication::instance()->settings();
    reg.beginGroup("Workspace");
    reg.setValue(_plugin->tag(), saveState(0));
  }
}

void MainWindow::closeEvent(QCloseEvent * e)
{
  TRACE;
  if(stopAll() && warnBeforeClose()) {
    e->accept();
  } else {
    e->ignore();
  }
}

bool MainWindow::stopAll()
{
  TRACE;
  int runningProcess=0;
  for(ThreadList::iterator it=_threads.begin();it!=_threads.end();++it) {
    if((*it)->isRunning()) runningProcess++;
  }
  if(runningProcess>0 || _runs->isQueued()) {
    if(Message::warning(MSG_ID, tr("Closing dinver"),
                            tr("There are still %1 running or queued processes. "
                               "Do you want to stop them?").arg(runningProcess),
                               Message::no(), Message::yes())==Message::Answer0) {
      return false;
    }
    // Dequeue all runs
    _runs->dequeueAll();
    for(ThreadList::iterator it=_threads.begin(); it!=_threads.end(); ++it) {
      if((*it)->isRunning()) {
        (*it)->terminate();
      }
    }
  }
  return true;
}

bool MainWindow::warnBeforeClose()
{
  TRACE;
  TRACE_BUG
  if(isModified() && _plugin->xmlSupport()) {
    TRACE_BUG_N(10)
    switch (Message::warning(MSG_ID, tr("Closing dinver"),
                             tr( "Do you want to save the changes to the current environment?"),
                             Message::yes(), Message::no(), Message::cancel())) {
    case Message::Answer0: {
        TRACE_BUG_N(1)
        save();
        TRACE_BUG_N(2)
        if(isModified()) {
          return false;
        }
      }
      break;
    case Message::Answer1:
      break;
    default:
      return false;
    }
  } else if(Message::question(MSG_ID, tr("Closing dinver"), tr("Are you sure?"), Message::no(), Message::yes())==Message::Answer0) {
    TRACE_BUG_N(3)
    return false;
  }
  return true;
}

QString MainWindow::pluginSelector(QString tag, bool debug)
{
  TRACE;
  QString plugin;
  if(tag.isEmpty()) {
    // Init of _plugin to load the correct one
    PluginSelector * d=new PluginSelector(nullptr, debug);
    Settings::getWidget(d);
    if(d->exec()==QDialog::Accepted) {
      Settings::setWidget(d);
      plugin=d->file();
    }
    delete d;
  } else {
    plugin=DinverCoreEngine::pluginFile(tag, debug);
  }
  return plugin;
}

bool MainWindow::setPlugin(QString pluginFile)
{
  TRACE;
  if(pluginFile.isEmpty()) return false;
  _plugin=DinverCoreEngine::loadPlugin(pluginFile, false);
  if(!_plugin) {
    return false;
  }

  QWidget * target=_plugin->createTargetWidget();
  if(target) {
    _targetDock=new DockWidget(this);
    connect(_targetDock, SIGNAL(hidden()), this, SLOT(setToolsMenuState()), Qt::QueuedConnection);
    _targetDock->setObjectName( "Targets" );
    _targetDock->setWindowTitle(tr("Targets"));
    _targetDock->setFeatures (QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetClosable);
    _targetDock->setAllowedAreas(Qt::AllDockWidgetAreas);
    _targetDock->setWidget(target);
    addDockWidget(Qt::LeftDockWidgetArea, _targetDock);
  }

  QWidget * param=_plugin->createParamWidget();
  if(param) {
    _paramDock=new DockWidget(this);
    connect(_paramDock, SIGNAL(hidden()), this, SLOT(setToolsMenuState()), Qt::QueuedConnection);
    _paramDock->setObjectName("Parameters");
    _paramDock->setWindowTitle(tr("Parameters"));
    _paramDock->setFeatures (QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetClosable);
    _paramDock->setAllowedAreas(Qt::AllDockWidgetAreas);
    _paramDock->setWidget(param);
    addDockWidget(Qt::BottomDockWidgetArea, _paramDock);
  }

  addDocks(_targetDock, _paramDock);
  addActions();

  // Add at least one tab (after windows menu is created)
  addTab();

  QSettings& reg=CoreApplication::instance()->settings();
  reg.beginGroup("Workspace");
  if(!restoreState(reg.value(_plugin->tag()).toByteArray(),0)) {
    App::log(tr("Cannot restore main window state\n") );
  }
  setToolsMenuState();

  Settings::getRect(this, "dinver Root");
  emit pluginsReady();

  return true;
}

void MainWindow::addDocks(DockWidget * leftDock, DockWidget * bottomDock)
{
  TRACE;
  DockWidget * logDock=new DockWidget(this);
  connect(logDock, SIGNAL(hidden()), this, SLOT(setToolsMenuState()), Qt::QueuedConnection);
  logDock->setObjectName("Log");
  logDock->setWindowTitle(tr("Log"));
  logDock->setFeatures (QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetClosable);
  logDock->setAllowedAreas(Qt::AllDockWidgetAreas);
  _logs=new LogWidget (logDock);
  logDock->setWidget(_logs);
  addDockWidget (Qt::LeftDockWidgetArea, logDock);
  if(leftDock)
    tabifyDockWidget(leftDock, logDock);

  DockWidget * runDock=new DockWidget(this);
  connect(runDock, SIGNAL(hidden()), this, SLOT(setToolsMenuState()), Qt::QueuedConnection);
  runDock->setObjectName("Runs");
  runDock->setWindowTitle(tr("Runs"));
  runDock->setFeatures (QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetClosable);
  runDock->setAllowedAreas(Qt::AllDockWidgetAreas);
  _runs=new ThreadLauncher (runDock);
  runDock->setWidget(_runs);
  addDockWidget (Qt::LeftDockWidgetArea, runDock);
  connect(_runs, SIGNAL(setCurrentRuns(ThreadList)), this, SLOT(setCurrentRuns(ThreadList)));
  tabifyDockWidget(logDock, runDock);

  DockWidget * statusDock=new DockWidget(this);
  connect(statusDock, SIGNAL(hidden()), this, SLOT(setToolsMenuState()), Qt::QueuedConnection);
  statusDock->setObjectName("Status");
  statusDock->setWindowTitle(tr("Status"));
  statusDock->setFeatures (QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetClosable);
  statusDock->setAllowedAreas(Qt::AllDockWidgetAreas);
  _status=new ProcessStatus(statusDock);
  statusDock->setWidget(_status);
  addDockWidget (Qt::BottomDockWidgetArea, statusDock);
  if(bottomDock) {
    tabifyDockWidget(bottomDock, statusDock);
  }
  connect(&_bigBen, SIGNAL(synchroTimeout()), _status, SLOT(synchronize()));
}

void MainWindow::addActions()
{
  TRACE;
  addFileActions();
  addViewActions();
  addRunsActions();
  addToolsActions();
  addWindowsActions();
  addHelpActions();
}

void MainWindow::addFileActions()
{
  TRACE;
  QAction * a;
  QMenu * m;

  m=menuBar()->addMenu(tr("&File"));
  m->setTearOffEnabled(true);

  if(_plugin->xmlSupport()) {
    a=new QAction(QIcon(":filenew.png"), tr("New"), this);
    a->setStatusTip(tr("Close current environment (if any) and clean everything"));
    connect(a, SIGNAL(triggered()), this, SLOT(clear()));
    m->addAction(a);

    a=new QAction(QIcon(":fileopen.png"), tr("Open"), this);
    a->setStatusTip(tr("Open an environment (targets, parameters and runs)"));
    connect(a, SIGNAL(triggered()), this, SLOT(open()));
    m->addAction(a);

    a=WindowEnvironment::instance()->_fileOpenRecentAction;
    a->setStatusTip(tr("Open an environment"));
    m->addAction(a);

    a=new QAction(QIcon(":filesave.png"), tr("Save"), this);
    a->setStatusTip(tr( "Save current environment (targets, parameters and runs)"));
    connect(a, SIGNAL(triggered()), this, SLOT(save()));
    m->addAction(a);

    a=new QAction(tr("Save as ..."), this);
    a->setStatusTip(tr("Save current environment (targets, parameters and runs)"));
    connect(a, SIGNAL(triggered()), this, SLOT(saveAs()));
    m->addAction(a);
  }

  if(_targetDock || _paramDock) {
    m->addSeparator();

    if(_targetDock) {
      a=new QAction(tr("Import targets"), m);
      a->setStatusTip(tr("Import current targets"));
      connect(a, SIGNAL(triggered()), this, SLOT(importTargets()));
      m->addAction(a);
      _importTargetsAction=a;

      a=new QAction(tr("Export targets"), m);
      a->setStatusTip(tr("Export current targets"));
      connect(a, SIGNAL(triggered()), this, SLOT(exportTargets()));
      m->addAction(a);
    }
    if(_paramDock) {
      a=new QAction(tr("Import parameterization"), m);
      a->setStatusTip(tr("Import current parameterization"));
      connect(a, SIGNAL(triggered()), this, SLOT(importParameters()));
      m->addAction(a);
      _importParamsAction=a;

      a=new QAction(tr("Export parameterization"), m);
      a->setStatusTip(tr("Export current parameterization"));
      connect(a, SIGNAL(triggered()), this, SLOT(exportParameters()));
      m->addAction(a);
    }
  }

  m->addSeparator();

  a=new QAction(tr("&Quit"), this);
  a->setShortcut(tr("Ctrl+Q"));
  a->setStatusTip(tr("Quit Geopsy"));
  connect(a, SIGNAL(triggered()), this, SLOT(close()));
  m->addAction(a);
}

void MainWindow::addViewActions()
{
  TRACE;
  QAction * a;
  QMenu * m;

  m=menuBar()->addMenu(tr("&View"));
  m->setTearOffEnabled(true);

  a=new QAction(tr("&Parameter Space"), this);
  a->setStatusTip(tr("Create a new sheet with customizable projections of multidimensional parameter spaces."));
  connect(a, SIGNAL(triggered()), this, SLOT(viewPS()));
  m->addAction(a);

  m->addSeparator();
  _plugin->addViewMenu(m);
}

void MainWindow::addRunsActions()
{
  TRACE;
  QAction * a;
  QMenu * m;

  m=menuBar()->addMenu(tr("&Runs"));
  m->setTearOffEnabled (true);

  a=new QAction(tr("&Add"), this);
  a->setStatusTip(tr("Add a new process with the current parameterization and a new random seed."));
  connect(a, SIGNAL(triggered()), _runs, SLOT(newThread()));
  m->addAction(a);

  a=new QAction(tr("&Remove"), this);
  a->setStatusTip(tr("Remove selected run from the current environment"));
  connect(a, SIGNAL(triggered()), _runs, SLOT(remove()));
  m->addAction(a);

  m->addSeparator();

  a=new QAction(tr("Star&t"), this);
  a->setStatusTip(tr("Start selected runs."));
  connect(a, SIGNAL(triggered()), _runs, SLOT(start()));
  m->addAction(a);

  a=new QAction(tr("&Stop"), this);
  a->setStatusTip(tr("Stop currently selected runs (no effect if no process is running)."));
  connect(a, SIGNAL(triggered()), _runs, SLOT(stop()));
  m->addAction(a);

  a=new QAction(tr("&Clear"), this);
  a->setStatusTip(tr("Remove all generated models from selected runs, remove .report file if any."));
  connect(a, SIGNAL(triggered()), _runs, SLOT(clear()));
  m->addAction(a);

  a=new QAction(tr("&Show"), this);
  a->setStatusTip(tr("In this mode, the target and the parameterization of each run can be viewed (export/import)."));
  a->setCheckable(true);
  a->setChecked(false);
  connect(a, SIGNAL(toggled(bool)), _runs, SLOT(show(bool)));
  m->addAction(a);
  _showRunAction=a;

  m->addSeparator();

  a=new QAction(tr("&Import models"), this);
  a->setStatusTip(tr("Populate the parameter space of selected run with models saved in .report file "
                     "(generated by another run)"));
  connect(a, SIGNAL(triggered()), _runs, SLOT(importModels()));
  m->addAction(a);
}

void MainWindow::addToolsActions()
{
  TRACE;
  QAction * a;
  QMenu * m;

  m=menuBar() ->addMenu(tr("&Tools"));
  m->setTearOffEnabled (true);
  _toolsMenu=m;

  QList<DockWidget *> dockwidgetList=findChildren<DockWidget *>();
  for(QList<DockWidget *>::iterator it=dockwidgetList.begin(); it!=dockwidgetList.end(); ++it) {
    a=new QAction((*it)->windowTitle(), this);
    QByteArray wPtr((const char *) & (*it), sizeof(DockWidget *));
    a->setData(wPtr);
    a->setCheckable(true);
    connect(a, SIGNAL(toggled(bool)), *it, SLOT(setVisible(bool)));
    m->addAction(a);
  }

  m->addSeparator();

  QList<QToolBar *> toolBarList=findChildren<QToolBar *>();
  for(QList<QToolBar *>::iterator it=toolBarList.begin(); it!=toolBarList.end(); ++it) {
    a=new QAction((*it)->windowTitle(), this);
    QByteArray wPtr((const char *) & (*it), sizeof(QToolBar *));
    a->setData(wPtr);
    a->setCheckable(true);
    connect(a, SIGNAL(toggled(bool)), *it, SLOT(setVisible(bool)));
    m->addAction(a);
  }
}

bool MainWindow::clear()
{
  TRACE;
  if(!stopAll()) return false;
  if(!closeAllPSViewers()) return false;
  emit beginClearThreads();
  qDeleteAll(_threads);
  _threads.clear();
  emit endClearThreads();
  _status->clear();
  _plugin->clear();
  _currentFile="";
  setWindowTitle("dinver");
  return true;
}

void MainWindow::open(QString fileName)
{
  TRACE;
  if(fileName.isEmpty()) {
    fileName=Message::getOpenFileName(tr("Open an existing environment"),
                                      tr("Dinver environment (*.dinver)"));
  } else {
    QFileInfo fi(fileName);
    fileName=fi.absoluteFilePath();
  }
  if(fileName.length()>0) {
    if(!clear()) return;

    MessageContext mc;
    XMLDinverContext dinverContext(XMLDinverContext::All);
    XMLDinverGui dinver(this);
    XMLDinverHeader hdr(&dinver);
    XMLErrorReport xmler(XMLErrorReport::Read);
    xmler.setTitle(tr("Opening environment"));
    xmler.setFileName(fileName);
    if(xmler.exec(hdr.xml_restoreFile(fileName, &dinverContext))) {
      _currentFile=fileName;
      setWindowTitle(_currentFile+" - dinver");
      WindowEnvironment::instance()->addRecentDocument(_currentFile);
      QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
      // Load the models
      int n=_threads.count();
      for(int i=0;i<n; i++) {
        InversionThread * t=_threads.at(i);
        t->checkImportModels(); // Do not import models but it makes sure that paths are still
                                // valid, use path translator if necessary.
        t->setImportOnly();
        t->start();
      }
    } else {
      _currentFile=QString();
      setWindowTitle("dinver");
    }
    _status->synchronize();
  }
}

void MainWindow::saveAs(QString fileName)
{
  TRACE;
  if(fileName.isEmpty()) {
    fileName=Message::getSaveFileName(tr("Save current environment"),
                                      tr( "Dinver environment (*.dinver)"));
  }
  if(fileName.length()>0) {
    _currentFile=fileName;
    save();
    WindowEnvironment::instance()->addRecentDocument(_currentFile);
    setWindowTitle(_currentFile+" - dinver");
  }
}

/*!
  Save parameters, target and all runs. If reports are stored in temp directory, save them in the same directory as
  the environment file.
*/
void MainWindow::save()
{
  TRACE;
  if(_currentFile.isEmpty()) {
    saveAs();
    return;
  }
  if(_currentFile.length()>0) {
    // Tranfer report files to the directory of dinver environment file
    QDir d=currentReportDir();
    bool unsavedInTempDir=false;  // check meanwhile if there are still reports in the temp file
    int n=_threads.count();
    setProgressMaximum(n-1);
    for(int i=0;i<n; i++) {
      InversionThread * t=_threads.at(i);
      if(t->setReportDir(d)) {
        if(t->reportDir().absolutePath()==_tempDir.absolutePath()) {
          // Clean temp directory
          d.setPath(_tempDir.absolutePath());
          d.remove(t->objectName());
        }
      } else {
        unsavedInTempDir=true;
      }
      setProgressValue(i);
      QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
    }
    XMLDinverContext dinverContext(XMLDinverContext::All);
    XMLDinverGui dinver(this);
    XMLDinverHeader hdr(&dinver);
    if(hdr.xml_saveFile(_currentFile, &dinverContext)==XMLClass::NoError) {
      if(unsavedInTempDir) {
        Message::warning(MSG_ID, tr("Saving environment"), tr("Some inversion process are still running and their models are stored "
                                                              "in temporary directory. Stop these processes or wait for their " 
                                                              "completion before saving again. When quitting, the temporary "
                                                              "directory is cleaned and models (directly stored in .report file) " 
                                                              "will be lost. See log for details."));
      } else {
        setModified(false);
      }
    } else {
      Message::warning(MSG_ID, "Saving environment",
                       "Cannot open file for writing", Message::cancel());
    }
  }
}

void MainWindow::importTargets(QString fileName)
{
  if(fileName.isEmpty()) {
    fileName=Message::getOpenFileName(tr("Import targets"),
                                      tr("Dinver targets (*.target);;"
                                         "Dinver environment (*.dinver)"));
  }
  if(!fileName.isEmpty()) {
    XMLDinverContext dinverContext(XMLDinverContext::Targets);
    XMLDinver dinver(_plugin);
    XMLDinverHeader hdr(&dinver);
    if(hdr.xml_restoreFile(fileName, &dinverContext)!=XMLClass::NoError) {
      Message::warning(MSG_ID, tr("Import targets"), tr("Error parsing file"), Message::cancel());
    }
  }
}
void MainWindow::exportTargets(QString fileName)
{
  if(fileName.isEmpty()) {
    fileName=Message::getSaveFileName(tr("Export targets"),
                                      tr("Dinver targets (*.target)"),
                                      QString());
  }
  if(!fileName.isEmpty()) {
    XMLDinverContext dinverContext(XMLDinverContext::Targets);
    XMLDinver dinver(_plugin);
    XMLDinverHeader hdr(&dinver);
    if(hdr.xml_saveFile(fileName, &dinverContext)==XMLClass::ErrorFileNotOpen) {
      Message::warning(MSG_ID, tr("Export targets"), tr("Cannot open file for writing"), Message::cancel());
    }
  }
}

void MainWindow::importParameters(QString fileName)
{
  if(fileName.isEmpty()) {
    fileName=Message::getOpenFileName(tr("Import paramters" ),
                                      tr("Dinver parameters (*.param);;"
                                         "Dinver environment (*.dinver)"));
  }
  if( !fileName.isEmpty()) {
    XMLDinverContext dinverContext(XMLDinverContext::Parameters);
    XMLDinver dinver(_plugin);
    XMLDinverHeader hdr(&dinver);
    if(hdr.xml_restoreFile(fileName, &dinverContext)!=XMLClass::NoError) {
      Message::warning(MSG_ID, tr("Import parameters"), tr("Error parsing file"), Message::cancel());
    }
  }
}

void MainWindow::exportParameters(QString fileName)
{
  if(fileName.isEmpty()) {
    fileName=Message::getSaveFileName(tr("Export parameters"),
                                      tr( "Dinver parameters (*.param)"),
                                      QString());
  }
  if( !fileName.isEmpty()) {
    XMLDinverContext dinverContext(XMLDinverContext::Parameters);
    XMLDinver dinver(_plugin);
    XMLDinverHeader hdr(&dinver);
    if(hdr.xml_saveFile(fileName, &dinverContext)==XMLClass::ErrorFileNotOpen) {
      Message::warning(MSG_ID, tr("Export parameters"), tr("Cannot open file for writing"), Message::cancel());
    }
  }
}

QDir MainWindow::currentReportDir() const
{
  TRACE;
  if(_currentFile.isEmpty()) {
    return _tempDir.absolutePath();
  } else {
    QFileInfo fi(_currentFile);
    QDir d=fi.absoluteDir();
    if(!d.exists()) {
      d.mkpath(d.absolutePath());
    }
    QString reportDir=fi.baseName()+"_reports";
    if(!d.exists(reportDir)) {
      d.mkdir(reportDir);
    }
    d.cd(reportDir);
    return d;
  }
}


void MainWindow::addThread(InversionThread * t)
{
  TRACE;
  emit beginNewThread();
  _threads.append(t);
  connect(t, SIGNAL(started()), &_bigBen, SLOT(threadStarted()) );
  connect(t, SIGNAL(stopped()), &_bigBen, SLOT(threadStopped()) );
  connect(t, SIGNAL(stopped()), _runs, SLOT(processFinished()));
  emit endNewThread();
}

void MainWindow::setCurrentRuns(ThreadList tList)
{
  if(!_showRunAction->isChecked()) {
    tList.clear();
  }
  if(tList.count()==1) {
    InversionThread * t=tList.first();
    plugin()->setCurrentForward(t->forward(), t->objectName());
    if(_paramDock) {
      _importParamsAction->setEnabled(false);
      _paramDock->setWindowTitle(tr("Parameters of %1").arg(t->objectName()));
    }
    if(_targetDock) {
      _importTargetsAction->setEnabled(false);
      _targetDock->setWindowTitle(tr("Targets of %1").arg(t->objectName()));
    }
  } else {
    plugin()->setCurrentForward(0, QString());
    if(_paramDock) {
      _paramDock->setWindowTitle(tr("Parameters"));
      _importParamsAction->setEnabled(true);
    }
    if(_targetDock) {
      _importTargetsAction->setEnabled(true);
      _targetDock->setWindowTitle(tr("Targets"));
    }
  }
}

void MainWindow::viewPS()
{
  TRACE;
  bool allOfTheSameType;
  ThreadList selThreads=selectedThreads(allOfTheSameType);
  if(selThreads.isEmpty()) return ;
  if(allOfTheSameType) {
    SelectPSViewer * d=new SelectPSViewer (this);
    d->init(psViewerList(), selThreads.first());
    d->setWindowTitle(tr("View all selected runs"));
    if(d->exec()==QDialog::Accepted) {
      PSViewer * psViewer=d->result();
      for(ThreadList::iterator it=selThreads.begin(); it!=selThreads.end(); ++it) {
        psViewer->addThreadData( *it);
      }
      psViewer->synchronize();
      psViewer->setLimits();
    }
  } else {
    for(ThreadList::iterator it=selThreads.begin(); it!=selThreads.end(); ++it) {
      SelectPSViewer * d=new SelectPSViewer (this);
      d->init(psViewerList(), *it);
      d->setWindowTitle(tr("View run %1").arg((*it)->objectName()));
      if(d->exec()==QDialog::Accepted) {
        PSViewer * psViewer=d->result();
        psViewer->addThreadData( *it);
        psViewer->synchronize();
        psViewer->setLimits();
      }
    }
  }
}

ThreadList MainWindow::selectedThreads(bool& allOfTheSameType) const
{
  TRACE;
 return _runs->threadTable->selectedThreads(allOfTheSameType);
}

ThreadList MainWindow::threads(QString t) const
{
  TRACE;
  ThreadList l;
  QRegExp trexp(t, Qt::CaseSensitive, QRegExp::Wildcard);
  for(ThreadList::const_iterator it=_threads.begin(); it<_threads.end(); it++ ) {
    if(trexp.exactMatch((*it)->objectName()) ) {
      l.append( *it);
    }
  }
  return l;
}

QStringList MainWindow::selectedReports(const QString& title) const
{
  TRACE;
  QStringList reportList;
  bool allOfTheSameType;
  ThreadList selThreads=selectedThreads(allOfTheSameType);
  if(selThreads.isEmpty()) {
    if(Message::question(MSG_ID, title,
                         tr("No run selected, do you want to select all of them?"),
                         Message::yes(), Message::cancel())==Message::Answer1) {
      return reportList;
    }
    selThreads=threads();
  }
  for(ThreadList::iterator it=selThreads.begin(); it!=selThreads.end(); ++it) {
    reportList.append((*it)->reportFileName());
  }
  return reportList;
}

/*!
  For a list of report file names return the associated targets
*/
QList<const AbstractForward *> MainWindow::forwardList(const QStringList& reports) const
{
  TRACE;
  QList<const AbstractForward *> f;
  for(int i=0; i<reports.count(); i++) {
    const QString& r=reports.at(i);
    for(ThreadList::const_iterator it=_threads.begin(); it<_threads.end(); it++ ) {
      InversionThread * t=*it;
      if(t->reportFileName()==r) {
        f.append(t->forward());
        break;
      }
    }
  }
  return f;
}

void MainWindow::setProgressMaximum(int value)
{
  TRACE;
  if(value>0) _progressBar->setMaximum(value);
}

int MainWindow::progressMaximum()
{
  TRACE;
  return _progressBar->maximum();
}

void MainWindow::setProgressValue(int value)
{
  TRACE;
  _progressBar->setValue(value);
}

void MainWindow::showMessage(QString message)
{
  TRACE;
  statusBar() ->showMessage(message);
}

QList<PSViewer *> MainWindow::psViewers()
{
  QWidgetList wList=WindowEnvironment::subWindowList(this);
  QList<PSViewer *> psViewers;
  for(QWidgetList::iterator it=wList.begin();it!=wList.end();++it) {
    if((*it)->inherits("PSViewer")) {
      psViewers << qobject_cast<PSViewer *>(*it);
    }
  }
  return psViewers;
}

bool MainWindow::closeAllPSViewers()
{
  TRACE;
  QList<PSViewer *> list=psViewers();
  if(!list.isEmpty()) {
    if(Message::warning(MSG_ID, tr("Closing all PS viewers"),
                        tr("This operation requires all PS viewers to be closed. Do you want to continue?"),
                        Message::no(), Message::yes())
       ==Message::Answer0) return false;
    for(QList<PSViewer *>::iterator it=list.begin(); it!=list.end(); ++it) {
      (*it)->parentWidget()->close();
    }
    return true;
  } else return true;
}

void MainWindow::clearThread(InversionThread * t)
{
  TRACE;
  QList<PSViewer *> list=psViewers();
  for(QList<PSViewer *>::iterator it=list.begin(); it!=list.end(); ++it) {
    (*it)->lockPlots();
  }
  t->clear();
  _status->clearThread(t);
  for(QList<PSViewer *>::iterator it=list.begin(); it!=list.end(); ++it) {
    (*it)->unlockPlots();
  }
}

void MainWindow::removeThread(InversionThread * t)
{
  TRACE;
  int i=_threads.indexOf(t);
  emit beginRemoveThread(i);
  _threads.removeAt(i);
  t->removeFiles(false);
  delete t;
  _status->removeThread(t);
  emit endRemoveThread();
}

/*!
  Set the state of one particular tool if the sender is a DockWidget. DockWidget emit a signal every time the user click on the
  close box. If the function is called without signal/slot, all states are checked.
*/
void MainWindow::setToolsMenuState()
{
  TRACE;
  DockWidget * w=qobject_cast<DockWidget *>(sender());
  QList<QAction *> aList=_toolsMenu->actions();
  int n=aList.count();
  if(w) {
    for(int i=0;i < n;i++ ) {
      QAction * a=aList.at(i);
      if(w==*reinterpret_cast<DockWidget **>(a->data().toByteArray().data())) {
        a->setChecked(w->isVisible());
      }
    }
  } else {
    for(int i=0;i < n;i++ ) {
      QAction * a=aList.at(i);
      if(!a->isSeparator()) {
        w=*reinterpret_cast<DockWidget **>(a->data().toByteArray().data());
        a->setChecked(w->isVisible());
      }
    }
  }
}
