/***************************************************************************
**
**  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: 2004-10-26
**  Copyright: 2004-2019
**    Marc Wathelet
**    Marc Wathelet (ULg, Liège, Belgium)
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include <QTimer>
#include <QMenuBar>

#include <QGpGuiTools.h>
#include <SciFigs.h>
#include "PSViewer.h"
#include "AddPlotParam.h"
#include "NAModelsPlotProperties.h"
#include "NAModelsPlot.h"

/*!
  \fn PSViewer::saveParameters

  If it return true, the NAModelsPlot contained in the sheet will export themselves as NAModelsPlot and
  not as XYValuePlot. The first one are used to save "canvas" for viewing particular parameter spaces while
  the last one is for viewing in figue. Set by slots PSViewer::fileSavePS() and PSViewer::fileSaveAsPS().
*/

PSViewer::PSViewer(QWidget * parent) :
    QWidget(parent)
{
  TRACE;
  setAttribute(Qt::WA_DeleteOnClose, true);
  _childLayout=new QVBoxLayout(this);
  _childLayout->setMargin(0);
  _childLayout->setSpacing(0);
#ifndef Q_OS_MAC
  _menuBar=new QMenuBar(this);
  _childLayout->addWidget(_menuBar);
#endif
  _childLayout->addWidget(&_sheet);
  resize(700, 500);
  Settings::getSize(this, "PSViewer" );

  _colorMap=new ColorMapWidget(nullptr);
  _colorMap->setObjectName("Palette");
  _colorMap->generateColorScale(20, ColorPalette::Hobi);
  _colorMap->setNumberPrecision(2);
  _colorMap->setTitle("Misfit value");
  _colorMap->setVLinear(0, 1);
  _colorMap->setPrintXAnchor(6);
  _colorMap->setPrintYAnchor(20);
  _colorMap->setPrintWidth(15);
  _colorMap->setPrintHeight(2);
  _sheet.addObject(_colorMap);

  _saveParameters=false;

  addActions();
}

PSViewer::~PSViewer()
{
  TRACE;
  Settings::setSize(this, "PSViewer");
  lockPlots();
  ViewerThreadMap::iterator it;
  for(it=_threads.begin();it!=_threads.end();++it) delete *it;
  unlockPlots();
}

void PSViewer::addActions()
{
  TRACE;
  QAction * a;
  QMenu * m;

  // File menu
  m=new QMenu(this);
  m->setTitle(tr( "&File" ));
  _sheet.addFileActions(m, nullptr);

#ifndef Q_OS_MAC
  _menuBar->addMenu(m);
#endif
  _sheet.addMenu(m);

  a=new QAction(QIcon( ":fileopen.png" ), tr( "Open PS" ), this);
  a->setStatusTip(tr( "Open an existing Parameter Space sheet (*.page files)" ));
  connect(a, SIGNAL(triggered()), this, SLOT(fileOpenPS()) );
  m->insertAction(m->actions().at(5), a);

  a=new QAction(QIcon( ":filesave.png" ), tr( "Save PS" ), this);
  a->setStatusTip(tr( "Save the current Parameter Space sheet (*.page files)" ));
  connect(a, SIGNAL(triggered()), this, SLOT(fileSavePS()) );
  m->insertAction(m->actions().at(6), a);

  a=new QAction(tr( "Save PS As..." ), this);
  a->setStatusTip(tr( "Save the current Parameter Space sheet (*.page files)" ));
  connect(a, SIGNAL(triggered()), this, SLOT(fileSaveAsPS()) );
  m->insertAction(m->actions().at(7), a);

  m->insertSeparator(m->actions().at(8));

  a=new QAction(tr( "&Close" ), this);
  connect(a, SIGNAL(triggered()), this, SLOT(close()) );
  m->addAction(a);

  // Edit menu
  m=new QMenu(this);
  m->setTitle(tr("&Edit"));
  _sheet.addEditActions(m, nullptr);

#ifndef Q_OS_MAC
  _menuBar->addMenu(m);
#endif
  _sheet.addMenu(m);

  // Insert menu
  m=new QMenu(this);
  m->setTitle(tr("&Insert"));
  _sheet.addInsertActions(m, nullptr);

  m->addSeparator();

  a=new QAction(tr("&Parameter plot"), this);
  a->setStatusTip(tr("Add a new plot"));
  connect(a, SIGNAL(triggered()), this, SLOT(addPlot()));
  m->addAction(a);

#ifndef Q_OS_MAC
  _menuBar->addMenu(m);
#endif
  _sheet.addMenu(m);

  // Format menu
  m=new QMenu(this);
  m->setTitle(tr("&Format"));
  _sheet.addFormatActions(m);

  m->addSeparator();

  a=new QAction(tr("Set &limits"), this);
  a->setStatusTip(tr("Adjust limits of plots automatically"));
  connect(a, SIGNAL(triggered()), this, SLOT(setLimits()));
  m->addAction(a);

#ifndef Q_OS_MAC
  _menuBar->addMenu(m);
#endif
  _sheet.addMenu(m);
}

void PSViewer::addThreadData(InversionThread * models)
{
  TRACE;
  // Look if this set of models is already in
  if(_threads.contains(models)) {
    return;
  }
  // Test if parameter space are compatible with current models
  if(!_threads.isEmpty() && *parameterList()!=*models) {
    return;
  }
  if(models->hasModelsToImport()) {
    models->importModels(models->reportFileName(), false);
  }

  lockPlots();
  _threads.insert(models, new ViewerThreadInfo);
  unlockPlots();

  if(_threads.count()==1) {
    int nd=models->variableParameterCount();
    double y=0.5;
    for(int i=0;i < nd;i++ ) {
      AxisWindow * w=addPlot( -1, i);
      w->setPrintAnchor(Point2D( 0.5, y) );
      w->updateGeometry();
      y += 6.0;
    }
  }
}

void PSViewer::synchronize()
{
  TRACE;
  //App::log("Synchronize now" << endl;
  lockPlots();
  ViewerThreadMap::iterator it;
  for(it=_threads.begin();it!=_threads.end();++it) {
    const InversionThread * t=static_cast<const InversionThread *>(it.key());
    t->lock();
    int nModels=t->visitedModelCount();
    int& iModel=it.value()->alreadyLoaded;
    //App::log(tr("Already loaded %1, max %2\n").arg(iModel).arg(nModels) );
    if(iModel > nModels) { // Less models than in view (probably a clear)
      _models.clear();
      iModel=0;
    }
    if(iModel < nModels) {
      for( ;iModel < nModels;iModel++ ) {
        if(t->misfit(iModel)!=std::numeric_limits<double>::infinity()) {
          _models.append(ModelThreadInfo( t, iModel) );
        }
      }
    }
    t->unlock();
  }
  lockDataModels();
  std::sort(_models.begin(), _models.end());
  unlockDataModels();
  unlockPlots();
  _sheet.deepUpdate();
}

void PSViewer::setLimits()
{
  TRACE;
  QList<NAModelsPlot *> plotList=_sheet.findChildren<NAModelsPlot>();
  for(QList<NAModelsPlot *>::iterator it=plotList.begin(); it!=plotList.end(); ++it) {
    NAModelsPlot * plot=*it;
    Rect r=plot->boundingRect();
    AxisWindow * w=plot->graph();
    w->xAxis()->setRange(r.x1(), r.x2());
    w->yAxis()->setRange(r.y1(), r.y2());
    w->deepUpdate();
  }
  if(modelList().size()>0) {
    lockDataModels();
    ModelThreadList::const_iterator it=modelList().begin();
    if(it!=modelList().end()) {
      double minM=std::numeric_limits<double>::infinity(), maxM=-std::numeric_limits<double>::infinity();
      double misfitValue;
      SAFE_UNINITIALIZED(misfitValue, 0);
      for(; it!=modelList().end(); ++it) {
        const ModelThreadInfo& modelInfo=*it;
        misfitValue=it->thread()->misfit(modelInfo.index());
        if(misfitValue>maxM) maxM=misfitValue;
        if(misfitValue<minM) minM=misfitValue;
      }
      if(misfitValue>0) {
        _colorMap->setVLog(minM, maxM);
      } else {
        _colorMap->setVLinear(minM, maxM);
      }
      _sheet.deepUpdate();
    }
    unlockDataModels();
  }
}

void PSViewer::lockPlots()
{
  TRACE;
  QList<NAModelsPlot *> plotList=_sheet.findChildren<NAModelsPlot>();
  for(QList<NAModelsPlot *>::iterator it=plotList.begin(); it!=plotList.end(); ++it) {
    (*it)->lockDelayPainting();
  }
}

void PSViewer::unlockPlots()
{
  TRACE;
  QList<NAModelsPlot *> plotList=_sheet.findChildren<NAModelsPlot>();
  for(QList<NAModelsPlot *>::iterator it=plotList.begin(); it!=plotList.end(); ++it) {
    (*it)->unlock();
  }
}
void PSViewer::lockDataModels()
{
  TRACE;
  ViewerThreadMap::iterator it;
  for(it=_threads.begin();it!=_threads.end();++it) {
    it.key()->lock();
  }
}

void PSViewer::unlockDataModels()
{
  TRACE;
  ViewerThreadMap::iterator it;
  for(it=_threads.begin();it!=_threads.end();++it) {
    it.key()->unlock();
  }
}

void PSViewer::addPlot()
{
  TRACE;
  AddPlotParam * d=new AddPlotParam (this);
  d->param->setParameterList(parameterList());
  Settings::getWidget(d);
  if(d->exec()==QDialog::Accepted) {
    Settings::setWidget(d);
    addPlot(d->param->paramX(), d->param->paramY());
  }
}

AxisWindow * PSViewer::addPlot(int paramX, int paramY)
{
  TRACE;
  AxisWindow * w=new AxisWindow;
  w->setObjectName("graph");
  _sheet.addObject(w);
  NAModelsPlot * plot=new NAModelsPlot(w);
  connect(_colorMap, SIGNAL(changed(ColorMap)), w->graphContent(), SLOT(deepUpdate()));
  plot->setParamX(paramX);
  plot->setParamY(paramY);
  w->setRemovable(true);
  w->setPrintAnchor(_sheet.currentOrigin()+Point2D(1.0, 1.0));
  w->setPrintWidth(6.0);
  w->setPrintHeight(6.0);
  _sheet.showObject(w);
  return w;
}

void PSViewer::fileOpenPS()
{
  TRACE;
  _saveParameters=true;
  _sheet.fileOpen();
  _saveParameters=false;
}

void PSViewer::fileSavePS()
{
  TRACE;
  _saveParameters=true;
  _sheet.fileSave();
  _saveParameters=false;
}

void PSViewer::fileSaveAsPS()
{
  TRACE;
  _saveParameters=true;
  _sheet.fileSaveAs();
  _saveParameters=false;
}
