/***************************************************************************
**
**  This file is part of dinverdc.
**
**  dinverdc 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.
**
**  dinverdc 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-11-05
**  Copyright: 2005-2019
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include "DCPlugin.h"
#include "dinverdcVersion.h"
#include "DispersionTargetWidget.h"
#include "ParamGroundModelWidget.h"
#include "ParamProfileWidget.h"
#include "TargetListFrameWidget.h"
#include "TargetListWidget.h"
#include "dinverdcVersion.h"
#include "ParamProfileWidget.h"
#include "ParamLayerWidget.h"

/*
   To test if there are missing symbols, uncomment the following lines and switch project template to 'app'
*/

/*int main(int argc, char ** argv)
{
  QApplication a(argc, argv);
  DCPlugin p;
  return 0;
}*/

DCPlugin::DCPlugin()
{
  TRACE;
  _paramWidget=nullptr;
  _targetWidget=nullptr;
  _param=nullptr;
  _target=nullptr;
}

DCPlugin::~DCPlugin()
{
  TRACE;
  delete _param;
  delete _target;
}

DinverInterface * DCPlugin::clone()
{
  TRACE;
  return new DCPlugin;
}

QString DCPlugin::tag() const
{
  TRACE;
  return "DispersionCurve";
}

QString DCPlugin::title() const
{
  TRACE;
  return tr("Surface Wave Inversion");
}

QString DCPlugin::version() const
{
  TRACE;
  return DINVERDC_VERSION;
}

QString DCPlugin::description() const
{
  TRACE;
  return tr("<p>Inversion of theoretical dispersion curves, autocorrelation curves, "
            "ellipticity curves and refraction curves.</p>");
}

void DCPlugin::addViewMenu(QMenu * m)
{
  TRACE;
  QAction * a;

  a=new QAction(tr("Ground profiles"), m);
  a->setStatusTip(tr("Create a new sheet with plots of ground profiles"));
  connect(a, SIGNAL(triggered()), this, SLOT(groundProfilesViewer()));
  m->addAction(a);

  a=new QAction(tr("Dispersion"), m);
  a->setStatusTip(tr("Create a new sheet with plots of dispersion curves"));
  connect(a, SIGNAL(triggered()), this, SLOT(dispersionViewer()));
  m->addAction(a);

  a=new QAction(tr("Autocorrelation"), m);
  a->setStatusTip(tr("Create a new sheet with plots of autocorr curves"));
  connect(a, SIGNAL(triggered()), this, SLOT(autocorrViewer()));
  m->addAction(a);

  a=new QAction(tr("Ellipticity"), m);
  a->setStatusTip(tr("Create a new sheet with plots of ellipticity curves"));
  connect(a, SIGNAL(triggered()), this, SLOT(ellipticityViewer()));
  m->addAction(a);

  a=new QAction(tr("Refraction Vp"), m);
  a->setStatusTip(tr("Create a new sheet with plots of refraction Vp curves"));
  connect(a, SIGNAL(triggered()), this, SLOT(refractionVpViewer()));
  m->addAction(a);

  a=new QAction(tr("Refraction Vs"), m);
  a->setStatusTip(tr("Create a new sheet with plots of refraction Vs curves"));
  connect(a, SIGNAL(triggered()), this, SLOT(refractionVsViewer()));
  m->addAction(a);

  a=new QAction(tr("Magneto-telluric"), m);
  a->setStatusTip(tr("Create a new sheet with plots of magneto-telluric curves"));
  connect(a, SIGNAL(triggered()), this, SLOT(magnetoTelluricViewer()));
  m->addAction(a);
}

void DCPlugin::setCurrentForward(AbstractForward * forward, const QString& caption)
{
  TRACE;
  if(forward) {
    if(!_param) {
      // Means that last list of forwards was null or contained more than 1 forward
      // Hence the main global parameterization and target are currently active
      // Save them before showing a particular case
      ASSERT(!_target);
      _param=_paramWidget->paramGroundModel();
      _target=_targetWidget->targetList();
    }
    Forward * f=static_cast<Forward *>(forward);
    _paramWidget->setFrom(f->paramGroundModel());
    _paramWidget->setEditable(false);
    _targetWidget->setFrom(f->targetList());
    _targetWidget->setEditable(false);
    _targetWidget->setTitles(caption);
  } else if(_param) {
    ASSERT(_target);
    _paramWidget->setFrom(_param);
    _paramWidget->setEditable(true);
    _targetWidget->setFrom(_target);
    _targetWidget->setEditable(true);
    _targetWidget->setTitles(QString());
    delete _param;
    delete _target;
    _param=nullptr;
    _target=nullptr;
  }
}

void DCPlugin::setCurrentGroundModel(ParamGroundModel * gm)
{
  TRACE;
  _paramWidget->setFrom(gm);
}

void DCPlugin::clear ()
{
  TRACE;
  _targetWidget->clear();
  _paramWidget->clear();
}

QWidget * DCPlugin::createTargetWidget()
{
  TRACE;
  TargetListFrameWidget * targetFrame=new TargetListFrameWidget;
  _targetWidget=targetFrame->targetListWidget;
  connect(_targetWidget, SIGNAL(targetChanged()), this, SLOT(setParamFromTargets()));
  return targetFrame;
}

QWidget * DCPlugin::createParamWidget()
{
  TRACE;
  _paramWidget=new ParamGroundModelWidget;
  return _paramWidget;
}

void DCPlugin::setParamFromTargets()
{
  TRACE;
  _targetWidget->setParamProfiles(_paramWidget);
}

void DCPlugin::xml_writeChildren(XML_WRITECHILDREN_ARGS) const
{
  TRACE;
  XMLDinverContext * dinverContext=static_cast<XMLDinverContext *>(context);
  if(dinverContext->targets()) {
    if(_targetWidget) {
      TargetList * t=_targetWidget->targetList();
      t->xml_save(s, context);
      delete t;
    } else {
      _target->xml_save(s, context);
    }
  }
  if(dinverContext->parameters()) {
    if(_paramWidget) {
      ParamGroundModel * p=_paramWidget->paramGroundModel();
      p->xml_save(s, context);
      delete p;
    } else {
      _param->xml_save(s, context);
    }
  }
}

XMLMember DCPlugin::xml_member(XML_MEMBER_ARGS)
{
  TRACE;
  Q_UNUSED(attributes)
  XMLDinverContext * dinverContext=static_cast<XMLDinverContext *>(context);
  if(tag=="TargetList") {
    if(dinverContext->targets()) {
      if(_targetWidget) {
        return XMLMember(new TargetList, true);
      } else {
        delete _target;
        _target=new TargetList;
        return XMLMember(_target);
      }
    } else {
      return XMLMember(XMLMember::Skip);
    }
  } else if(tag=="ParamGroundModel") {
    if(dinverContext->parameters()) {
      if(_paramWidget) {
        return XMLMember(new ParamGroundModel, true);
      } else {
        delete _param;
        _param=new ParamGroundModel;
        return XMLMember(_param);
      }
    } else {
      return XMLMember(XMLMember::Skip);
    }
  } else return XMLMember(XMLMember::Unknown);
}

bool DCPlugin::xml_polishChild(XML_POLISHCHILD_ARGS)
{
  TRACE;
  XMLDinverContext * dinverContext=static_cast<XMLDinverContext *>(context);
  if(dinverContext->targets() && child->xml_tagName()=="TargetList") {
    if(_targetWidget) {
      _targetWidget->setFrom(static_cast<TargetList *>(child));
    }
    return true;
  } else if(dinverContext->parameters() && child->xml_tagName()=="ParamGroundModel") {
    if(_paramWidget) { // gui run
      _paramWidget->setFrom(static_cast<ParamGroundModel *>(child));
    }
    return true;
  } else
    return false;
}

AbstractForward * DCPlugin::createForward()
{
  return new Forward;
}

bool DCPlugin::initForward(AbstractForward * forward)
{
  ParamGroundModel * gm;
  if(_paramWidget) {
    gm=_paramWidget->paramGroundModel();
  } else {
    if(!_param) {
      App::log(tr("Parameters are missing.\n") );
      return 0;
    }
    gm=new ParamGroundModel(*_param);
  }
  if(gm->isEmpty()) {
    App::log(tr("Parametrized model is empty or at least one of its profiles is missing.\n") );
    delete gm;
    return false;
  }
  TargetList * tl;
  if(_targetWidget) {
    tl=_targetWidget->targetList();
  } else {
   if(_target) {
     tl=new TargetList(*_target);
   } else {
     tl=new TargetList();
   }
  }
  if(tl->dispersionSampleCount()>100) {
    if(Message::warning(MSG_ID, tr("Creating a new inversion run"),
                                     tr("The effective number of samples for dispersion curve is greater than 100 (%1). This is "
                                        "likely to slow down the inversion process. A usual number of samples is 50. A high number "
                                        "of samples may be observed when various curves are inverted together (dispersion, spac,...). "
                                        "In this case make sure that all these curves use the same frequency samples. Resample "
                                        "all curves if necessary.").arg(tl->dispersionSampleCount()),
                                     tr("Continue"), Message::cancel(), true) ==Message::Answer1) {
      delete gm;
      delete tl;
      return false;
    }
  }
  Forward * f=static_cast<Forward *>(forward);
  f->setTargets(tl);
  return f->setParamSpace(gm);
}

QWidget * DCPlugin::parentWidget(QObject * o)
{
  TRACE;
  QAction * a=qobject_cast<QAction *>(o);
  ASSERT(a);
  return a->parentWidget();
}

void DCPlugin::groundProfilesViewer()
{
  TRACE;
  QStringList reportList=DinverCoreEngine::instance()->selectedReports(parentWidget(sender()), tr("View ground profiles"));
  if(reportList.isEmpty()) return;
  ViewerParam * d=new ViewerParam(QApplication::activeWindow());
  d->groundProfile(reportList);
  delete d;
}

void DCPlugin::dispersionViewer()
{
  TRACE;
  QWidget * mw=parentWidget(sender());
  QStringList reportList=DinverCoreEngine::instance()->selectedReports(mw, tr("View dispersion curves"));
  if(reportList.isEmpty()) return;
  ViewerParam * d=new ViewerParam(QApplication::activeWindow());
  DCModelViewer * w=d->dispersion(reportList);
  delete d;
  setTargets(w, mw, reportList);
}

void DCPlugin::autocorrViewer()
{
  TRACE;
  QWidget * mw=parentWidget(sender());
  QStringList reportList=DinverCoreEngine::instance()->selectedReports(mw, tr("View autocorr curves"));
  if(reportList.isEmpty()) return;
  ViewerParam * d=new ViewerParam(QApplication::activeWindow());
  DCModelViewer * w=d->autocorr(reportList);
  delete d;
  setTargets(w, mw, reportList);
}

void DCPlugin::ellipticityViewer()
{
  TRACE;
  QWidget * mw=parentWidget(sender());
  QStringList reportList=DinverCoreEngine::instance()->selectedReports(mw, tr("View ellipticity curves"));
  if(reportList.isEmpty()) return;
  ViewerParam * d=new ViewerParam(QApplication::activeWindow());
  DCModelViewer * w=d->ellipticity(reportList);
  delete d;
  setTargets(w, mw, reportList);
}

void DCPlugin::refractionVpViewer()
{
  TRACE;
  QWidget * mw=parentWidget(sender());
  QStringList reportList=DinverCoreEngine::instance()->selectedReports(mw, tr("View refraction Vp curves"));
  if(reportList.isEmpty()) return;
  ViewerParam * d=new ViewerParam(QApplication::activeWindow());
  DCModelViewer * w=d->refractionVp(reportList);
  delete d;
  setTargets(w, mw, reportList);
}

void DCPlugin::refractionVsViewer()
{
  TRACE;
  QWidget * mw=parentWidget(sender());
  QStringList reportList=DinverCoreEngine::instance()->selectedReports(mw, tr("View refraction Vs curves"));
  if(reportList.isEmpty()) return;
  ViewerParam * d=new ViewerParam(QApplication::activeWindow());
  DCModelViewer * w=d->refractionVs(reportList);
  delete d;
  setTargets(w, mw, reportList);
}

void DCPlugin::magnetoTelluricViewer()
{
  TRACE;
  QWidget * mw=parentWidget(sender());
  QStringList reportList=DinverCoreEngine::instance()->selectedReports(mw, tr("View magneto-telluric curves"));
  if(reportList.isEmpty()) return;
  ViewerParam * d=new ViewerParam(QApplication::activeWindow());
  DCModelViewer * w=d->magnetoTelluric(reportList);
  delete d;
  setTargets(w, mw, reportList);
}

QList<const TargetList *> DCPlugin::targets(QList<const AbstractForward *> fl)
{
  TRACE;
  QList<const TargetList *> tl;
  int n=fl.count();
  for(int i=0; i<n; i++) {
    const Forward * f=static_cast<const Forward *>(fl.at(i));
    tl.append(f->targetList());
  }
  return tl;
}

void DCPlugin::setTargets(DCModelViewer * w, QWidget * mw, const QStringList& reports)
{
  if(w) {
    QList<const TargetList *> tll=targets(DinverCoreEngine::instance()->forwardList(mw, reports));
    int n=tll.count();
    for(int i=0; i<n; i++) {
      w->setTarget(tll.at(i));
    }
  }
}

PACKAGE_INFO("dinverdc", DINVERDC)
#if(QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
Q_EXPORT_PLUGIN2(dinverdc, DCPlugin)
#endif
