/***************************************************************************
**
**  This file is part of spac2disp.
**
**  spac2disp 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.
**
**  spac2disp 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-06-07
**  Copyright: 2004-2019
**    Marc Wathelet
**    Marc Wathelet (ULg, Liège, Belgium)
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include <DinverCore.h>
#include <DinverDCCore.h>
#include <QGpGuiWave.h>

#include "SpacSelector.h"
#include "Spac2DispOptions.h"

SpacSelector::SpacSelector(QWidget * parent) :
    GraphicSheetMenu(parent, Qt::Window)
{
  TRACE;
  _lastCurRing=0;
  _selAutocorr=nullptr;
  _autocorr=nullptr;
  _maxSolutionCount=5;

  _childLayout->removeWidget(&_sheet);

  QSplitter * hSplitter=new QSplitter(this);
  hSplitter->setOrientation(Qt::Horizontal);
  QSplitter * vSplitter=new QSplitter(hSplitter);
  vSplitter->setOrientation(Qt::Vertical);

  curves=new CurveBrowser(vSplitter);
  curves->setProxy(new DispersionProxy);
  curves->setPlotProxy(new ModalPlotProxy);
  AxisWindow * graph=new AxisWindow(vSplitter);
  graph->setObjectName("dispersion");
  _gridLayer=new IrregularGrid2DPlot(graph);
  LineLayer * dispLayer=new LineLayer(graph);
  ModalLine * line=new ModalLine;
  line->setPen(Pen(QColor(145,145,145), 0.6, Qt::SolidLine) );
  line->setSymbol(Symbol(Symbol::Circle,1.2, Pen(QColor(222,222,222)), Brush(QColor(101,101,101), Qt::SolidPattern) ));
  dispLayer->setReferenceLine(line);
  curves->initLayer(dispLayer);
  curves->setCurrentLayer(dispLayer);
  _limitsSolutionsLayer=new LineLayer(graph);
  _limitsSolutionsLayer->setReferenceLine(new PlotLine2D);
  _limitsSolutionsLayer->setObjectName("SPAC Limits & Solutions");
  _limitsSolutionsLayer->addLine(Pen(Qt::NoPen),Symbol(Symbol::Circle,1.2, Pen(Qt::black), Brush(Qt::black, Qt::SolidPattern)));
  _limitsSolutionsLayer->addLine(Pen(Qt::black,0.6,Qt::SolidLine),Symbol());
  _limitsSolutionsLayer->addLine(Pen(Qt::black,0.6,Qt::DotLine),Symbol());

  QAction * a=curves->addCurveAction(tr("&Adjust"), tr("Adjust current curve to the closest maximum"), true);
  connect(a, SIGNAL(triggered()), this, SLOT(adjustCurve()) );

  hSplitter->addWidget(&_sheet);
  hSplitter->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
  _childLayout->addWidget(hSplitter);

  options=new Spac2DispOptions(this);
  _childLayout->addWidget(options);
  Settings::getWidget(this, "SpacSelector");
  connect(options->kminEdit, SIGNAL(valueChanged(double)), this, SLOT(setK()));
  connect(options->kmaxEdit, SIGNAL(valueChanged(double)), this, SLOT(setK()));
  connect(options->vminEdit, SIGNAL(valueChanged(double)), this, SLOT(inverse()));
  connect(options->vmaxEdit, SIGNAL(valueChanged(double)), this, SLOT(inverse()));
  connect(options->nCells, SIGNAL(valueChanged(int)), this,SLOT(inverse()));
  connect(options->radiusScroll, SIGNAL(valueChanged(int)), this, SLOT(radiusScroll_valueChanged()));
  connect(options->frequencyScroll, SIGNAL(valueChanged(int)), this, SLOT(inverseAt()));
  connect(options->selectBut, SIGNAL(clicked()), this, SLOT(selectSamples()));
  connect(options->saveBut, SIGNAL(clicked()), this, SLOT(saveSelection()));
  connect(options->autoPick, SIGNAL(clicked()), this, SLOT(autoPick()));
}

SpacSelector::~SpacSelector()
{
  TRACE;
  delete _selAutocorr;
}

void SpacSelector::closeEvent (QCloseEvent * e)
{
  TRACE;
  Settings::setWidget(this, "SpacSelector");
  e->accept();
}

Curve<Point2D>& SpacSelector::limitsSolutionsCurve(int index)
{
  return static_cast<PlotLine2D *>(_limitsSolutionsLayer->line(index))->curve();
}

bool SpacSelector::createObjects(AutocorrCurves * autocorr)
{
  TRACE;
  _autocorr=autocorr;
  // Check that all rings have only one curve and is not empty
  int ringCount=_autocorr->rings()->count();
  for(int iRing=0; iRing<ringCount; iRing++) {
    if(_autocorr->ringCurves(iRing).count()!=1) {
      const AutocorrRing& ring=_autocorr->ring(iRing);
      Message::critical(MSG_ID, tr("Loading Dinver target/project ..."),
                        tr("Ring %1 (from %2 to %3 m) contains %4 curves. Check your "
                           "target files with dinver so that each ring has only one curve.\n\n"
                           "Tip:\nStart from an empty environment, load all your target files, edit the curves "
                           "and choose 'Export targets' from 'File' menu.")
                              .arg(iRing).arg(ring.minRadius())
                              .arg(ring.maxRadius())
                              .arg(_autocorr->ringCurves(iRing).count()), Message::cancel());
      return false;
    }
  }

  AutocorrFactory autocorrFactory(_autocorr->rings());
  autocorrFactory.setX(_autocorr->curves());
  int omegaCount=autocorrFactory.x()->count();
  if(omegaCount<2) {
    Message::critical(MSG_ID, tr( "Loading Dinver target/project ..." ),
                     tr("No frequency samples found in target."));
    return false;
  }

  options->radiusScroll->setMaximum(ringCount-1);
  options->frequencyScroll->setMaximum(omegaCount-1);
  Curve<Point2D>& resol=limitsSolutionsCurve(1);
  Curve<Point2D>& aliasing=limitsSolutionsCurve(2);
  Point2D fcurve;
  for(int i=0;i<omegaCount;i++) {
    fcurve.setX(autocorrFactory.x()->at(i));
    resol.append(fcurve);
    aliasing.append(fcurve);
  }
  double orX=0.5;
  double orY=0.5;
  AxisWindow * w;
  Rect r;
  static const QString caption(tr("Ring %1 from %2 m to %3 m"));
  _spacLayers.resize(ringCount);
  for(int ig=0;ig<ringCount;ig++) {
    w=new AxisWindow;
    w->setObjectName(QString("ring_%1").arg(ig, 3, 10, QChar('0')));
    _spacLayers[ig]=new LineLayer(w);
    ModalLine * line=new ModalLine;
    line->setPen(Pen(Qt::NoPen));
    line->setSymbol(Symbol( Symbol::Circle,1.2, Pen(Qt::gray),
                                Brush(Qt::gray,Qt::SolidPattern)) );
    _spacLayers[ig]->setReferenceLine(line);
    line=static_cast<ModalLine *>(_spacLayers[ig]->addLine());
    line->curve()=_autocorr->ringCurves(ig).first();
    _spacLayers[ig]->setReferenceSymbol(Symbol( Symbol::Circle, 1.2,
                                         Pen(Qt::black),
                                         Brush(Qt::black,Qt::SolidPattern) ));
    line=static_cast<ModalLine *>(_spacLayers[ig]->addLine());
    line->curve()=_autocorr->ringCurves(ig).first();
    line->curve().setValid(false);
    _spacLayers[ig]->setReferenceSymbol(Symbol( Symbol::Circle, 2.75,
                                         Pen(Qt::black,0.6)) );
    _spacLayers[ig]->addLine();

    w->xAxis()->setFrequency();
    w->yAxis()->setTitle(tr("Autocorr ratio"));
    w->setGeometry(orX, orY+0.5, 9, 6);
    r=_spacLayers[ig]->boundingRect();
    w->xAxis()->setRange(r.x1()*0.95, r.x2()*1.05);
    w->yAxis()->setRange(-1.05, 1.05);
    sheet()->addObject(w, true);

    TextEdit * c=sheet()->addText();
    c->setGeometry(orX, orY, 9, 1);
    const AutocorrRing& r=_autocorr->ring(ig);
    c->setText(caption.arg(ig+1).arg(r.minRadius()).arg(r.maxRadius()));

    if(orX==0.5) orX=9.5; else {orX=0.5;orY+=6.5;}
  }
  _gridLayer->graph()->xAxis()->setRange(autocorrFactory.x()->first()*0.95, autocorrFactory.x()->last()*1.05);
  // TODO: fix bug if vmin is left to 0, infinite loop after
  radiusScroll_valueChanged();
  inverse();
  setK();
  return true;
}

void SpacSelector::radiusScroll_valueChanged()
{
  TRACE;
  int iRadius=options->radiusScroll->value();
  if(iRadius>(int)_autocorr->rings()->count()-1) return;
  // Remove marker from last ring plot
  _spacLayers[_lastCurRing]->line(2)->clear();
  _spacLayers[_lastCurRing]->graphContent()->deepUpdate();
  options->radiusMin->setText(QString::number(_autocorr->ring(iRadius).minRadius()));
  options->radiusMax->setText(QString::number(_autocorr->ring(iRadius).maxRadius()));
  _lastCurRing=iRadius;
  inverseAt();
}

void SpacSelector::inverseAt()
{
  TRACE;
  AutocorrFactory autocorrFactory(_autocorr->rings());
  autocorrFactory.setX(_autocorr->curves());
  autocorrFactory.linkX(_autocorr->curves());
  int ringCount=_autocorr->rings()->count();
  int omegaCount=autocorrFactory.x()->count();

  double smin=1.0/options->vmaxEdit->value();
  double smax=1.0/options->vminEdit->value();
  if(smin>smax) return;
  int iRing=options->radiusScroll->value();
  if(iRing>ringCount-1) return;
  int iFreq=options->frequencyScroll->value();
  if(iFreq>omegaCount-1) return;
  LineLayer * plot=_spacLayers[iRing];
  AxisWindow * w=plot->graph();
  LayerLocker ll(plot);
  QList<ModalCurve> ringCurves=_autocorr->ringCurves(iRing);
  ModalCurve& curve=ringCurves.first();
  int index=curve.indexOf(iFreq); // iFreq is considered as a factory index, hence it returns -1
                                  // if iFreq does not exist for that curve
  AutocorrDispersion eng;
  if(index>-1) {
    FactoryPoint p=curve.at(index);
    p.setStddev(0.0);
    plot->line(2)->clear();
    static_cast<ModalLine *>(plot->line(2))->curve().append(p);
    // Inversion itself
    eng.setRing(_autocorr->ring(iRing));
    double omega=2*M_PI*autocorrFactory.x()->at(iFreq);
    QVector<double> solutions=eng.dispersion(p, omega*smin, omega*smax);
    // Plot solutions
    LayerLocker ll(_limitsSolutionsLayer);
    Curve<Point2D>& invRes=limitsSolutionsCurve(0);
    invRes.clear();
    for(int i=0; i<solutions.count() && i<_maxSolutionCount; i++) {
      //printf("sol @ ring %i, %lf Hz %lf\n", iRing, autocorrFactory.x()->at(iFreq), 1/solutions.at(i));
      invRes.append(Point2D( p.x(), solutions.at(i)) );
    }
    _gridLayer->graph()->graphContent()->deepUpdate();
    w->graphContent()->deepUpdate();
  }
}

void SpacSelector::setK()
{
  TRACE;
  if(!options->isValid()) {
    return;
  }
  double kmin=options->kminEdit->value();
  double kmax=options->kmaxEdit->value();
  LayerLocker ll(_limitsSolutionsLayer);
  double invWLmin=kmin/(2*M_PI);
  double invWLmax=kmax/(2*M_PI);
  Curve<Point2D>& resol=limitsSolutionsCurve(1);
  Curve<Point2D>& aliasing=limitsSolutionsCurve(2);
  for(int i=0;i<resol.count();i++) {
    resol[i].setY(invWLmin/resol[i].x());
    aliasing[i].setY(invWLmax/aliasing[i].x());
  }
  _gridLayer->graph()->graphContent()->deepUpdate();
}

void SpacSelector::inverse()
{
  TRACE;
  if(!options->isValid()) {
    return;
  }
  AutocorrFactory autocorrFactory(_autocorr->rings());
  autocorrFactory.setX(_autocorr->curves());
  autocorrFactory.linkX(_autocorr->curves());
  int ringCount=_autocorr->rings()->count();
  int omegaCount=autocorrFactory.x()->count();

  double smin=1.0/options->vmaxEdit->value();
  double smax=1.0/options->vminEdit->value();
  int nCells=options->nCells->value();
  // Get grids ready
  IrregularGrid2D grid(omegaCount,nCells);
  for(int i=0;i<omegaCount;i++)
    grid.setX(i, autocorrFactory.x()->at(i));
  grid.setLinear(YAxis,smin,smax);
  grid.init(0.0);
  IrregularGrid2D ringGrid;
  AutocorrDispersion eng;
  // Uncoupled inversion of all points
  for(int ir=0;ir<ringCount;ir++) {
    eng.setRing(_autocorr->ring(ir));
    ringGrid.init(omegaCount,nCells,0.0);
    QList<ModalCurve> ringCurves=_autocorr->ringCurves(ir);
    ModalCurve& curve=ringCurves.first();
    curve.sort(); // to remove in 3.4
    for(int iw=0; iw<omegaCount; iw++) {
      double x=autocorrFactory.x()->at(iw);
      FactoryPoint p=curve.at(x);
      if(p.isValid() && fabs(p.y())>0.025) {
        double f=autocorrFactory.x()->at(iw);
        double omega=2*M_PI*f;
        QVector<double> solutions=eng.dispersion(p, omega*smin, omega*smax);
        for(int i=0; i<solutions.count() && i<_maxSolutionCount; i++) {
          double s=solutions.at(i);
          //printf("%lf %lf\n", f, s);
          int is=grid.indexOfY(s);
          //printf("sol @ ring %i, %lf Hz %lf --> cell %i\n", ir, autocorrFactory.x()->at(iw), 1/solutions.at(i), is);
          if(s>smin && s<smax && is>-1 && is<grid.ny()) {
            if(ringGrid.value(iw,is)==0.0) {
              *ringGrid.valuePointer(iw,is)=1;
            }
          }
        }
      }
    }
    grid+=ringGrid;
  }
  // Set grid values
  _gridLayer->setGrid(grid);
  setLimits();
}

void SpacSelector::setLimits()
{
  TRACE;
  double smin=1.0/options->vmaxEdit->value();
  double smax=1.0/options->vminEdit->value();
  // Ajust palette
  int maxCount=(int)_gridLayer->grid().maximumValue();
  ColorMap pal=_gridLayer->colorMap();
  pal.generateColorScale(maxCount+2, ColorPalette::Hsv);
  pal.setColor(0, Qt::white);
  pal.setVLinear(0.5,maxCount+0.5);
  pal.setWhiteTransparent(true);
  _gridLayer->setColorMap(pal);
  _gridLayer->graph()->yAxis()->setRange(smin*0.95,smax*1.05);
  _gridLayer->graph()->deepUpdate();
}

void SpacSelector::setWavenumberRange(double kmin, double kmax)
{
  TRACE;
  if(kmin>0.0 && kmax>0.0 && kmin<kmax) {
    options->kmaxEdit->setValue(kmax);
    options->kminEdit->setValue(kmin);
  }
}

void SpacSelector::setVelocityRange(double vmin, double vmax)
{
  TRACE;
  if(vmin>0.0 && vmax>0.0 && vmin<vmax) {
    options->vmaxEdit->setValue(vmax);
    options->vminEdit->setValue(vmin);
  }
}

void SpacSelector::setCellCount(int c)
{
  TRACE;
  if(c>0) {
    options->nCells->setValue(c);
  }
}

void SpacSelector::selectSamples()
{
  TRACE;
  AutocorrFactory autocorrFactory(_autocorr->rings());
  autocorrFactory.setX(_autocorr->curves());
  autocorrFactory.linkX(_autocorr->curves());
  int ringCount=_autocorr->rings()->count();
  int omegaCount=autocorrFactory.x()->count();

  if(!options->isValid()) {
    return;
  }

  double kmin=options->kminEdit->value();
  double kmax=options->kmaxEdit->value();
  double smin=1.0/options->vmaxEdit->value();
  double smax=1.0/options->vminEdit->value();

  int nCells=options->nCells->value();
  int nCurves=curves->count();
  // Check that only two curve are visible
  const QVector<double>& freq=*autocorrFactory.x();
  ModalCurve limits[2];
  int nLimits=0;
  for(int i=0;i<nCurves;i++) {
    if(curves->currentLayer()->line(i)->isVisible()) {
      if(nLimits<2) {
        ModalCurve& f=static_cast<ModalLine *>(curves->currentLayer()->line(i))->curve();
        // Extrapole to cover the whole frequency range
        if(f.at(0).x()>freq[0]) {
          f.append(FactoryPoint(freq[0], f.at(0).y()));
          f.sort();
        }
        if(f.last().x()<freq.last()) {
          f.append(FactoryPoint( freq.last(), f.last().y()) );
          f.sort();
        }
        limits[nLimits]=f;
        nLimits++;
      } else {
        Message::warning(MSG_ID, tr("Selecting samples"),
                         tr("More than 2 curves are visible, only the first two are taken to define the slowness "
                            "range. Other are ignored."), Message::ok());
        break;
      }
    }
  }
  if(nLimits<2) {
        Message::warning(MSG_ID, tr("Selecting samples"),
                         tr("Less than 2 curves are visible, cannot define the slowness range. Pick at least two curves to define "
                            "the minimum and maximum range for dispersion curves."),
                         Message::cancel());
    return;
  }
  limits[0].sort();
  limits[1].sort();
  limits[0].resample(freq);
  limits[1].resample(freq);
  Curve<FactoryPoint>& minS=limits[0];
  Curve<FactoryPoint>& maxS=limits[1];
  // Make sure that minS is always less than maxS
  int iw;
  for(iw=0;iw<omegaCount;iw++) {
    if(minS[iw].y()>maxS[iw].y()) {
      double tmp=maxS[iw].y();
      maxS[iw].setY(minS[iw].y());
      minS[iw].setY(tmp);
    }
  }
  if(_selAutocorr) delete _selAutocorr;
  _selAutocorr=new AutocorrCurves;
  AutocorrDispersion eng;
  for(int ir=0; ir<ringCount; ir++ ) {
    const AutocorrRing& ring=_autocorr->ring(ir);
    eng.setRing(ring);
    _selAutocorr->addRing(ring);
    LineLayer * plot=_spacLayers[ir];
    AxisWindow * w=plot->graph();
    LayerLocker ll(plot);
    ModalCurve& selCurve=static_cast<ModalLine *>(plot->line(1))->curve();
    selCurve.setValid(false);
    QList<ModalCurve> curves=_autocorr->ringCurves(ir);
    ModalCurve& curve=curves.first();
    for(int iw=0;iw<omegaCount;iw++) {
      int index=curve.indexOf(iw);
      if(index>-1) {
        const FactoryPoint& p=curve.at(index);
        double omega=2*M_PI*autocorrFactory.x()->at(iw);
        QVector<double> solutions=eng.dispersion(p, omega*smin, omega*smax);
        for(int i=0; i<solutions.count() && i<_maxSolutionCount; i++) {
          double s=solutions.at(i);
          //printf("sol @ ring %i, %lf Hz %lf\n", ir, autocorrFactory.x()->at(iw), 1/s);
          if(s>minS[iw].y() && s<maxS[iw].y() &&
              s>smin && s<smax &&
              omega*s>kmin && omega*s<kmax) {
            selCurve[index].setValid(true);
            break;
          }
        }
      }
    }
    QString log=tr("---------------------------------------------\n"
                     "Samples selected using spac2disp\n\n"
                     "Wave number: [ %1 , %2 ] (rad/m)\n"
                     "Velocity: [ %3 , %4 ] (m/s)\n"
                     "Number of slowness cells: %5\n"
                     "Dispersion curve searched between:\n");
    log=log.arg(kmin).arg(kmax).arg(1.0/smax).arg(1.0/smin).arg(nCells);
    QString str("%1 Hz\t%2 m/s\t%3 m/s\n");
    for(int iw=0;iw<omegaCount;iw++)
      log+=str.arg(freq[iw]).arg(minS[iw].y()).arg(maxS[iw].y());
    selCurve.addLog(log);
    _selAutocorr->addCurve(selCurve);
    w->graphContent()->deepUpdate();
  }
}

void SpacSelector::saveSelection()
{
  TRACE;
  if(!_selAutocorr) selectSamples();
  if(!_selAutocorr) {
    Message::warning(MSG_ID,tr("Searching dispersion limits"),
                         tr("You must pick 2 curves to define the dispersion\n"
                         "limits and to select samples."), Message::cancel());
    return;
  }
  QString fileName=Message::getSaveFileName(tr("Save SPAC selection"),
                                            tr("Dinver targets (*.target)"));
  if(fileName.length()==0) return;
  TargetList tl;
  tl.autocorrTarget().setCurves( *_selAutocorr);
  XMLVirtualPlugin plugin(&tl, "DispersionCurve");
  XMLDinverHeader hdr(&plugin);
  hdr.xml_saveFile(fileName);
}

void SpacSelector::autoPick()
{
  TRACE;
  LayerLocker ll(_limitsSolutionsLayer);
  ModalLine * line=static_cast<ModalLine *>(curves->currentLayer()->addLine());
  ModalCurve& curve=line->curve();

  if(!options->isValid()) {
    return;
  }

  IrregularGrid2D grid=_gridLayer->grid();
  grid.smooth(YAxis);
  grid.smooth(YAxis);
  Curve<Point2D> cs=grid.crossSection(YAxis, 0);
  double iyMax=cs.maximumY(0);
  curve=grid.followMaximumX<ModalCurve, FactoryPoint>(0, grid.nx()-1, iyMax);
  invalidateRangeK(curve);
  curves->addLine(line);
}

void SpacSelector::adjustCurve()
{
  TRACE;
  int index=curves->currentLine();
  if(index>=0) {
    CurveProperties * properties=curves->curves().at(index);
    CurveProxy * proxy=properties->proxy();
    proxy->sort();
    if(proxy->sampleCount()==0) return;
    // Activate dialog
    CurveBrowserCut * d=new CurveBrowserCut(this);
    d->setCurveLimits(proxy->minimumX(), proxy->maximumX() );
    d->setAxisNames(proxy->xName(), proxy->xInversedName());
    d->setWindowTitle(tr("Adjust"));
    Settings::getWidget(d, "SpacSelector::adjustCurve");
    if(d->exec()==QDialog::Accepted) {
      Settings::setWidget(d, "SpacSelector::adjustCurve");
      double minimum=d->minimumEdit->text().toDouble();
      double maximum=d->maximumEdit->text().toDouble();
      SamplingOptions options;
      if(d->inversedBut->isChecked()) {
        options=InversedScale;
      } else {
        options=LinearScale;
      }
      adjustCurve(properties, minimum, maximum, options);
    }
    delete d;
  }
}

void SpacSelector::adjustCurve(CurveProperties * properties, double min, double max, SamplingOptions options)
{
  TRACE;
  ModalProxy * proxy=static_cast<ModalProxy *>(properties->proxy());
  ModalCurve& c=proxy->curve();
  if(min<=0.0) {
    min=c.at(c.minimumX()).x();
  }
  if(max<=0.0) {
    max=c.at(c.maximumX()).x();
  }
  properties->beginCurveChange();
  adjustCurve(c, min, max, options);
  invalidateRangeK(c);
  QString unit;
  if(options & InversedScale) {
    unit=proxy->xInversedUnit();
  } else {
    unit=proxy->xUnit();
  }
  proxy->addLog(tr("Adjust from %1 %2 to %3 %4\n").arg(min).arg(unit).arg(max).arg(unit) );
  properties->endCurveChange();
}

void SpacSelector::adjustCurve(ModalCurve& curve, double min, double max, SamplingOptions options)
{
  TRACE;
  // Transforms X scale according to options (log and inv)
  if(options & InversedScale) {
    curve.xInverse();
  }
  IrregularGrid2D grid=_gridLayer->grid();
  grid.smooth(YAxis);
  grid.smooth(YAxis);
  grid.followMaximumX<ModalCurve, FactoryPoint>(curve, min, max);
  // Re-Transforms axis according options (log and inv)
  if(options & InversedScale) {
    curve.xInverse();
  }
}

void SpacSelector::invalidateRangeK(ModalCurve& curve)
{
  TRACE;
  double kmin=options->kminEdit->value();
  double kmax=options->kmaxEdit->value();
  for(ModalCurve::iterator it=curve.begin(); it!=curve.end(); it++) {
    FactoryPoint& p=*it;
    double k=2.0*M_PI*p.y()*p.x();
    if(k<kmin || k>kmax) {
      p.setValid(false);
    }
  }
}

void SpacSelector::exportPlot(const ExportOptions& o)
{
  TRACE;
  AxisWindow * w=curves->currentLayer()->graph();
  if(!o.legendFile().isEmpty()) {
    Legend l;
    XMLErrorReport xmler(XMLErrorReport::Read | XMLErrorReport::NoMessageBox);
    xmler.setTitle(tr("Load legend"));
    xmler.setFileName(o.legendFile());
    XMLSciFigs s;
    if(xmler.exec(s.restoreFile(o.legendFile(), &l, XMLSciFigs::Data))) {
      curves->setLegend(l);
    }
  }
  if(!o.colorMapFile().isEmpty()) {
    ColorMap pal;
    XMLErrorReport xmler(XMLErrorReport::Read | XMLErrorReport::NoMessageBox);
    xmler.setTitle(tr("Load palette"));
    xmler.setFileName(o.colorMapFile());
    XMLSciFigs s;
    if(xmler.exec(s.restoreFile(o.colorMapFile(), &pal, XMLSciFigs::Data))) {
      _gridLayer->setColorMap(pal);
    }
  }
  if(!o.appendLayerFiles().isEmpty()) {
    for(QStringList::const_iterator it=o.appendLayerFiles().begin(); it!=o.appendLayerFiles().end(); it++) {
      w->graphContent()->appendLayers(*it);
    }
  }
  if(!o.prependLayerFiles().isEmpty()) {
    for(QStringList::const_iterator it=o.prependLayerFiles().begin(); it!=o.prependLayerFiles().end(); it++) {
      w->graphContent()->prependLayers(*it);
    }
  }
  setLimits();
  foreach(QString m, o.makeupObjectFiles()) {
    w->restoreMakeUp(m);
  }
  if(!o.exportFile().isEmpty()) {
    w->exportFile(o.exportFile(), o.exportFormat(), o.dpi());
  }
}

/*!
  Loads curves from file \a fileName.
*/
void SpacSelector::loadCurves(const QString& fileName, const QString& parserFileName)
{
  TRACE;
  ColumnTextParser parser;
  parser.setStandardTypes(curves->proxy()->columnFileTypes());
  if(parserFileName.isEmpty()) {
    parser.setTypes(curves->proxy()->defaultColumnFileTypes());
  } else {
    XMLHeader hdr(&parser);
    if(hdr.xml_restoreFile(parserFileName)!=XMLClass::NoError) {
      return;
    }
  }
  QFile f(fileName);
  if(f.open(QIODevice::ReadOnly)) {
    parser.setText(new QTextStream(&f));
    parser.start();
    parser.wait();
    curves->loadMultiColumns(&parser, fileName);
  } else {
    App::log(tr("Cannot read file %1\n").arg(fileName) );
  }
}
